diff --git a/board/stm32f407-st-discovery/third_party_driver/Kconfig b/board/stm32f407-st-discovery/third_party_driver/Kconfig
index 43448e65..d83a254e 100755
--- a/board/stm32f407-st-discovery/third_party_driver/Kconfig
+++ b/board/stm32f407-st-discovery/third_party_driver/Kconfig
@@ -98,6 +98,11 @@ if BSP_USING_USB
source "$BSP_DIR/third_party_driver/usb/Kconfig"
endif
+menuconfig BSP_USING_LWIP
+bool "Using LwIP device"
+default n
+select RESOURCES_LWIP
+
menuconfig BSP_USING_WDT
bool "Using WATCHDOG device"
default y
diff --git a/board/stm32f407-st-discovery/third_party_driver/Makefile b/board/stm32f407-st-discovery/third_party_driver/Makefile
index 3057ed77..02b461b7 100644
--- a/board/stm32f407-st-discovery/third_party_driver/Makefile
+++ b/board/stm32f407-st-discovery/third_party_driver/Makefile
@@ -50,4 +50,8 @@ ifeq ($(CONFIG_BSP_USING_WDT),y)
SRC_DIR += watchdog
endif
+ifeq ($(CONFIG_BSP_USING_LWIP),y)
+ SRC_DIR += ethernet
+endif
+
include $(KERNEL_ROOT)/compiler.mk
diff --git a/board/stm32f407-st-discovery/third_party_driver/ethernet/Kconfig b/board/stm32f407-st-discovery/third_party_driver/ethernet/Kconfig
new file mode 100644
index 00000000..a6889b41
--- /dev/null
+++ b/board/stm32f407-st-discovery/third_party_driver/ethernet/Kconfig
@@ -0,0 +1,12 @@
+
+config CAN_BUS_NAME_1
+ string "can bus name"
+ default "can1"
+
+config CAN_DRIVER_NAME
+ string "can driver name"
+ default "can1_drv"
+
+config CAN_1_DEVICE_NAME_1
+ string "can bus 1 device 1 name"
+ default "can1_dev1"
\ No newline at end of file
diff --git a/board/stm32f407-st-discovery/third_party_driver/ethernet/Makefile b/board/stm32f407-st-discovery/third_party_driver/ethernet/Makefile
new file mode 100644
index 00000000..cf27edf9
--- /dev/null
+++ b/board/stm32f407-st-discovery/third_party_driver/ethernet/Makefile
@@ -0,0 +1,4 @@
+SRC_FILES := hardware_ethernet.c connect_ethernet.c ethernetif.c
+
+
+include $(KERNEL_ROOT)/compiler.mk
\ No newline at end of file
diff --git a/board/stm32f407-st-discovery/third_party_driver/ethernet/connect_ethernet.c b/board/stm32f407-st-discovery/third_party_driver/ethernet/connect_ethernet.c
new file mode 100644
index 00000000..eb7a6266
--- /dev/null
+++ b/board/stm32f407-st-discovery/third_party_driver/ethernet/connect_ethernet.c
@@ -0,0 +1,363 @@
+/**
+ ******************************************************************************
+ * @file stm32f4x7_eth_bsp.c
+ * @author MCD Application Team
+ * @version V1.0.0
+ * @date 31-October-2011
+ * @brief STM32F4x7 Ethernet hardware configuration.
+ ******************************************************************************
+ * @attention
+ *
+ * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
+ * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
+ * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
+ * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
+ * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
+ * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
+ *
+ *
© COPYRIGHT 2011 STMicroelectronics
+ ******************************************************************************
+ */
+
+ /*
+* Copyright (c) 2020 AIIT XUOS Lab
+* XiUOS is licensed under Mulan PSL v2.
+* You can use this software according to the terms and conditions of the Mulan PSL v2.
+* You may obtain a copy of Mulan PSL v2 at:
+* http://license.coscl.org.cn/MulanPSL2
+* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
+* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
+* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
+* See the Mulan PSL v2 for more details.
+*/
+
+/**
+* @file connect_ethernet.c
+* @brief Adapted network software protocol stack and hardware operation functions
+* @version 1.0
+* @author AIIT XUOS Lab
+* @date 2021-05-29
+*/
+
+/* Includes ------------------------------------------------------------------*/
+#include "hardware_syscfg.h"
+#include "hardware_gpio.h"
+#include "hardware_rcc.h"
+#include "misc.h"
+#include "hardware_exti.h"
+#include "hardware_eth.h"
+#include "connect_ethernet.h"
+#include
+#include
+#include
+
+__IO uint32_t EthInitStatus = 0;
+__IO uint8_t EthLinkStatus = 0;
+
+/* Private function prototypes -----------------------------------------------*/
+static void ETH_GPIO_Config(void);
+static void ETH_MACDMA_Config(void);
+
+extern int32 s_xSemaphore;
+
+
+/* Private functions ---------------------------------------------------------*/
+
+/**
+ * @brief ETH_BSP_Config
+ * @param None
+ * @retval None
+ */
+void ETH_BSP_Config(void)
+{
+ RCC_ClocksTypeDef RCC_Clocks;
+
+ /* Configure the GPIO ports for ethernet pins */
+ ETH_GPIO_Config();
+
+ /* Configure the Ethernet MAC/DMA */
+ ETH_MACDMA_Config();
+
+ if (EthInitStatus == 0)
+ {
+ while(1);
+ }
+
+ /* Configure Systick clock source as HCLK */
+ SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);
+
+ /* SystTick configuration: an interrupt every 10ms */
+ RCC_GetClocksFreq(&RCC_Clocks);
+
+ SysTick_Config(RCC_Clocks.HCLK_Frequency / 100);
+}
+
+/**
+ * @brief Configures the Ethernet Interface
+ * @param None
+ * @retval None
+ */
+static void ETH_MACDMA_Config(void)
+{
+ ETH_InitTypeDef ETH_InitStructure;
+
+ /* Enable ETHERNET clock */
+ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_ETH_MAC | RCC_AHB1Periph_ETH_MAC_Tx |
+ RCC_AHB1Periph_ETH_MAC_Rx, ENABLE);
+
+ /* Reset ETHERNET on AHB Bus */
+ ETH_DeInit();
+
+ /* Software reset */
+ ETH_SoftwareReset();
+
+ /* Wait for software reset */
+ while (ETH_GetSoftwareResetStatus() == SET);
+
+ /* ETHERNET Configuration --------------------------------------------------*/
+ /* Call ETH_StructInit if you don't like to configure all ETH_InitStructure parameter */
+ ETH_StructInit(Ð_InitStructure);
+
+ /* Fill ETH_InitStructure parametrs */
+ /*------------------------ MAC -----------------------------------*/
+ ETH_InitStructure.ETH_AutoNegotiation = ETH_AutoNegotiation_Enable;
+
+
+ ETH_InitStructure.ETH_LoopbackMode = ETH_LoopbackMode_Disable;
+ ETH_InitStructure.ETH_RetryTransmission = ETH_RetryTransmission_Disable;
+ ETH_InitStructure.ETH_AutomaticPadCRCStrip = ETH_AutomaticPadCRCStrip_Disable;
+ ETH_InitStructure.ETH_ReceiveAll = ETH_ReceiveAll_Disable;
+ ETH_InitStructure.ETH_BroadcastFramesReception = ETH_BroadcastFramesReception_Enable;
+ ETH_InitStructure.ETH_PromiscuousMode = ETH_PromiscuousMode_Disable;
+ ETH_InitStructure.ETH_MulticastFramesFilter = ETH_MulticastFramesFilter_Perfect;
+ ETH_InitStructure.ETH_UnicastFramesFilter = ETH_UnicastFramesFilter_Perfect;
+#ifdef CHECKSUM_BY_HARDWARE
+ ETH_InitStructure.ETH_ChecksumOffload = ETH_ChecksumOffload_Enable;
+#endif
+
+ /*------------------------ DMA -----------------------------------*/
+
+ /* When we use the Checksum offload feature, we need to enable the Store and Forward mode:
+ the store and forward guarantee that a whole frame is stored in the FIFO, so the MAC can insert/verify the checksum,
+ if the checksum is OK the DMA can handle the frame otherwise the frame is dropped */
+ ETH_InitStructure.ETH_DropTCPIPChecksumErrorFrame = ETH_DropTCPIPChecksumErrorFrame_Enable;
+ ETH_InitStructure.ETH_ReceiveStoreForward = ETH_ReceiveStoreForward_Enable;
+ ETH_InitStructure.ETH_TransmitStoreForward = ETH_TransmitStoreForward_Enable;
+
+ ETH_InitStructure.ETH_ForwardErrorFrames = ETH_ForwardErrorFrames_Disable;
+ ETH_InitStructure.ETH_ForwardUndersizedGoodFrames = ETH_ForwardUndersizedGoodFrames_Disable;
+ ETH_InitStructure.ETH_SecondFrameOperate = ETH_SecondFrameOperate_Enable;
+ ETH_InitStructure.ETH_AddressAlignedBeats = ETH_AddressAlignedBeats_Enable;
+ ETH_InitStructure.ETH_FixedBurst = ETH_FixedBurst_Enable;
+ ETH_InitStructure.ETH_RxDMABurstLength = ETH_RxDMABurstLength_32Beat;
+ ETH_InitStructure.ETH_TxDMABurstLength = ETH_TxDMABurstLength_32Beat;
+ ETH_InitStructure.ETH_DMAArbitration = ETH_DMAArbitration_RoundRobin_RxTx_2_1;
+
+ /* Configure Ethernet */
+ EthInitStatus = ETH_Init(Ð_InitStructure, DP83848_PHY_ADDRESS);
+}
+
+/**
+ * @brief Configures the different GPIO ports.
+ * @param None
+ * @retval None
+ */
+void ETH_GPIO_Config(void)
+{
+ GPIO_InitTypeDef GPIO_InitStructure;
+
+ /* Enable GPIOs clocks */
+ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA |
+ RCC_AHB1Periph_GPIOB |
+ RCC_AHB1Periph_GPIOC |
+ RCC_AHB1Periph_GPIOI |
+ RCC_AHB1Periph_GPIOG |
+ RCC_AHB1Periph_GPIOH |
+ RCC_AHB1Periph_GPIOF, ENABLE);
+
+ /* Enable SYSCFG clock */
+ RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
+
+ /* Configure MCO (PA8) */
+ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
+ GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
+ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
+ GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
+ GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
+ GPIO_Init(GPIOA, &GPIO_InitStructure);
+
+ /* MII/RMII Media interface selection --------------------------------------*/
+#ifdef MII_MODE /* Mode MII with STM324xG-EVAL */
+ #ifdef PHY_CLOCK_MCO
+
+
+ /* Output HSE clock (25MHz) on MCO pin (PA8) to clock the PHY */
+ RCC_MCO1Config(RCC_MCO1Source_HSE, RCC_MCO1Div_1);
+ #endif /* PHY_CLOCK_MCO */
+
+ SYSCFG_ETH_MediaInterfaceConfig(SYSCFG_ETH_MediaInterface_MII);
+#elif defined RMII_MODE /* Mode RMII with STM324xG-EVAL */
+
+ SYSCFG_ETH_MediaInterfaceConfig(SYSCFG_ETH_MediaInterface_RMII);
+#endif
+
+/* Ethernet pins configuration ************************************************/
+ /*
+ ETH_MDIO -------------------------> PA2
+ ETH_MDC --------------------------> PC1
+ ETH_PPS_OUT ----------------------> PB5
+ ETH_MII_CRS ----------------------> PH2
+ ETH_MII_COL ----------------------> PH3
+ ETH_MII_RX_ER --------------------> PI10
+ ETH_MII_RXD2 ---------------------> PH6
+ ETH_MII_RXD3 ---------------------> PH7
+ ETH_MII_TX_CLK -------------------> PC3
+ ETH_MII_TXD2 ---------------------> PC2
+ ETH_MII_TXD3 ---------------------> PB8
+ ETH_MII_RX_CLK/ETH_RMII_REF_CLK---> PA1
+ ETH_MII_RX_DV/ETH_RMII_CRS_DV ----> PA7
+ ETH_MII_RXD0/ETH_RMII_RXD0 -------> PC4
+ ETH_MII_RXD1/ETH_RMII_RXD1 -------> PC5
+ ETH_MII_TX_EN/ETH_RMII_TX_EN -----> PG11
+ ETH_MII_TXD0/ETH_RMII_TXD0 -------> PG13
+ ETH_MII_TXD1/ETH_RMII_TXD1 -------> PG14
+ */
+
+ /* Configure PA1, PA2 and PA7 */
+ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_7;
+ GPIO_Init(GPIOA, &GPIO_InitStructure);
+ GPIO_PinAFConfig(GPIOA, GPIO_PinSource1, GPIO_AF_ETH);
+ GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_ETH);
+ GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_ETH);
+
+ /* Configure PB11 and PB12 */
+ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13;
+ GPIO_Init(GPIOB, &GPIO_InitStructure);
+ GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_ETH);
+ GPIO_PinAFConfig(GPIOB, GPIO_PinSource12, GPIO_AF_ETH);
+ GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_ETH);
+
+ /* Configure PC1, PC2, PC3, PC4 and PC5 */
+ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_4 | GPIO_Pin_5;
+ GPIO_Init(GPIOC, &GPIO_InitStructure);
+ GPIO_PinAFConfig(GPIOC, GPIO_PinSource1, GPIO_AF_ETH);
+ GPIO_PinAFConfig(GPIOC, GPIO_PinSource4, GPIO_AF_ETH);
+ GPIO_PinAFConfig(GPIOC, GPIO_PinSource5, GPIO_AF_ETH);
+
+}
+
+/**
+ * @brief Configure the PHY to generate an interrupt on change of link status.
+ * @param PHYAddress: external PHY address
+ * @retval None
+ */
+uint32_t Eth_Link_PHYITConfig(uint16_t PHYAddress)
+{
+ uint32_t tmpreg = 0;
+
+ /* Read MICR register */
+ tmpreg = ETH_ReadPHYRegister(PHYAddress, PHY_MICR);
+
+ /* Enable output interrupt events to signal via the INT pin */
+ tmpreg |= (uint32_t)PHY_MICR_INT_EN | PHY_MICR_INT_OE;
+
+ if(!(ETH_WritePHYRegister(PHYAddress, PHY_MICR, tmpreg)))
+ {
+ /* Return ERROR in case of write timeout */
+ return ETH_ERROR;
+ }
+ /* Read MISR register */
+ tmpreg = ETH_ReadPHYRegister(PHYAddress, PHY_MISR);
+
+ /* Enable Interrupt on change of link status */
+ tmpreg |= (uint32_t)PHY_MISR_LINK_INT_EN;
+
+ if(!(ETH_WritePHYRegister(PHYAddress, PHY_MISR, tmpreg)))
+ {
+
+ /* Return ERROR in case of write timeout */
+ return ETH_ERROR;
+ }
+
+ /* Return SUCCESS */
+ return ETH_SUCCESS;
+}
+
+/**
+ * @brief EXTI configuration for Ethernet link status.
+ * @param PHYAddress: external PHY address
+ * @retval None
+ */
+void Eth_Link_EXTIConfig(void)
+{
+ GPIO_InitTypeDef GPIO_InitStructure;
+ EXTI_InitTypeDef EXTI_InitStructure;
+ NVIC_InitTypeDef NVIC_InitStructure;
+
+ /* Enable the INT (PB14) Clock */
+ RCC_AHB1PeriphClockCmd(ETH_LINK_GPIO_CLK, ENABLE);
+ RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
+
+ /* Configure INT pin as input */
+ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
+ GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
+ GPIO_InitStructure.GPIO_Pin = ETH_LINK_PIN;
+ GPIO_Init(ETH_LINK_GPIO_PORT, &GPIO_InitStructure);
+
+ /* Connect EXTI Line to INT Pin */
+ SYSCFG_EXTILineConfig(ETH_LINK_EXTI_PORT_SOURCE, ETH_LINK_EXTI_PIN_SOURCE);
+
+ /* Configure EXTI line */
+ EXTI_InitStructure.EXTI_Line = ETH_LINK_EXTI_LINE;
+ EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
+ EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
+ EXTI_InitStructure.EXTI_LineCmd = ENABLE;
+ EXTI_Init(&EXTI_InitStructure);
+
+ /* Enable and set the EXTI interrupt to the highest priority */
+ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
+ NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
+ NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
+ NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
+ NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
+ NVIC_Init(&NVIC_InitStructure);
+}
+
+/**
+ * @brief This function handles Ethernet link status.
+ * @param None
+ * @retval None
+ */
+void Eth_Link_ITHandler(uint16_t PHYAddress)
+{
+ /* Check whether the link interrupt has occurred or not */
+ if(((ETH_ReadPHYRegister(PHYAddress, PHY_MISR)) & PHY_LINK_STATUS) != 0)
+ {
+ EthLinkStatus = ~EthLinkStatus;
+
+ }
+}
+
+void assert_failed(uint8_t* file, uint32_t line)
+{
+ /* User can add his own implementation to report the file name and line number,
+ ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
+
+ /* Infinite loop */
+ while (1)
+ {}
+}
+
+#ifdef BSP_USING_LWIP
+void ETHERNET_IRQHandler(int irq_num, void *arg)
+{
+ KPrintf("ethernet irq comes one ...\n");
+ ETH_DMAClearITPendingBit(ETH_DMA_IT_R);
+ ETH_DMAClearITPendingBit(ETH_DMA_IT_NIS);
+ KSemaphoreAbandon(s_xSemaphore);
+}
+DECLARE_HW_IRQ(ETH_IRQn, ETHERNET_IRQHandler, NONE);
+#endif
+
+
+
diff --git a/board/stm32f407-st-discovery/third_party_driver/ethernet/ethernetif.c b/board/stm32f407-st-discovery/third_party_driver/ethernet/ethernetif.c
new file mode 100644
index 00000000..616cc983
--- /dev/null
+++ b/board/stm32f407-st-discovery/third_party_driver/ethernet/ethernetif.c
@@ -0,0 +1,445 @@
+/**
+ * @file
+ * Ethernet Interface Skeleton
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ */
+
+/*
+ * This file is a skeleton for developing Ethernet network interface
+ * drivers for lwIP. Add code to the low_level functions and do a
+ * search-and-replace for the word "ethernetif" to replace it with
+ * something that better describes your network interface.
+ */
+
+#include "lwip/opt.h"
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/pbuf.h"
+#include "lwip/sys.h"
+#include "netif/etharp.h"
+#include "err.h"
+#include "ethernetif.h"
+
+#include "main.h"
+#include
+#include
+#include
+#include
+#include
+
+#define netifMTU (1500)
+#define netifINTERFACE_TASK_STACK_SIZE ( 2048 )
+#define netifINTERFACE_TASK_PRIORITY ( configMAX_PRIORITIES - 1 )
+#define netifGUARD_BLOCK_TIME ( 250 )
+/* The time to block waiting for input. */
+#define emacBLOCK_TIME_WAITING_FOR_INPUT ( ( portTickType ) 100 )
+
+/* Define those to better describe your network interface. */
+#define IFNAME0 's'
+#define IFNAME1 't'
+
+/* MAC ADDRESS: MAC_ADDR0:MAC_ADDR1:MAC_ADDR2:MAC_ADDR3:MAC_ADDR4:MAC_ADDR5 */
+#define MAC_ADDR0 2
+#define MAC_ADDR1 0
+#define MAC_ADDR2 0
+#define MAC_ADDR3 0
+#define MAC_ADDR4 0
+#define MAC_ADDR5 0
+
+static struct netif *s_pxNetIf = NULL;
+int32 s_xSemaphore = -1;
+
+
+/* Ethernet Rx & Tx DMA Descriptors */
+extern ETH_DMADESCTypeDef DMARxDscrTab[ETH_RXBUFNB], DMATxDscrTab[ETH_TXBUFNB];
+
+/* Ethernet Receive buffers */
+extern uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE];
+
+/* Ethernet Transmit buffers */
+extern uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE];
+
+/* Global pointers to track current transmit and receive descriptors */
+extern ETH_DMADESCTypeDef *DMATxDescToSet;
+extern ETH_DMADESCTypeDef *DMARxDescToGet;
+
+/* Global pointer for last received frame infos */
+extern ETH_DMA_Rx_Frame_infos *DMA_RX_FRAME_infos;
+
+
+
+
+void ethernetif_input( void * pvParameters );
+void LoopGetMacPkg(void * pvParameters);
+
+static void arp_timer(void *arg);
+
+
+
+uint32_t TCPTimer = 0;
+uint32_t ARPTimer = 0;
+__IO uint32_t LocalTime = 0; /* this variable is used to create a time reference incremented by 10ms */
+
+void LwIP_Periodic_Handle(__IO uint32_t localtime)
+{
+#if LWIP_TCP
+ /* TCP periodic process every 250 ms */
+ if (localtime - TCPTimer >= TCP_TMR_INTERVAL)
+ {
+ TCPTimer = localtime;
+ tcp_tmr();
+ }
+#endif
+
+ /* ARP periodic process every 5s */
+ if ((localtime - ARPTimer) >= ARP_TMR_INTERVAL)
+ {
+ ARPTimer = localtime;
+ etharp_tmr();
+ }
+
+}
+
+void LwIP_Pkt_Handle(struct netif *netif)
+{
+ /* Read a received packet from the Ethernet buffers and send it to the lwIP for handling */
+ ethernetif_input(netif);
+}
+
+void Time_Update_LwIP(void)
+{
+ LocalTime += MS_PER_SYSTICK_F407;
+}
+
+
+
+/**
+ * In this function, the hardware should be initialized.
+ * Called from ethernetif_init().
+ *
+ * @param netif the already initialized lwip network interface structure
+ * for this ethernetif
+ */
+static void low_level_init(struct netif *netif)
+{
+ uint32_t i;
+
+ /* set netif MAC hardware address length */
+ netif->hwaddr_len = ETHARP_HWADDR_LEN;
+
+ /* set netif MAC hardware address */
+ netif->hwaddr[0] = MAC_ADDR0;
+ netif->hwaddr[1] = MAC_ADDR1;
+ netif->hwaddr[2] = MAC_ADDR2;
+ netif->hwaddr[3] = MAC_ADDR3;
+ netif->hwaddr[4] = MAC_ADDR4;
+ netif->hwaddr[5] = MAC_ADDR5;
+
+ /* set netif maximum transfer unit */
+ netif->mtu = 1500;
+
+ /* Accept broadcast address and ARP traffic */
+ netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
+
+ s_pxNetIf =netif;
+
+ /* create binary semaphore used for informing ethernetif of frame reception */
+ if (s_xSemaphore < 0)
+ {
+ s_xSemaphore = KSemaphoreCreate(0);
+ }
+
+ /* initialize MAC address in ethernet MAC */
+ ETH_MACAddressConfig(ETH_MAC_Address0, netif->hwaddr);
+
+ /* Initialize Tx Descriptors list: Chain Mode */
+ ETH_DMATxDescChainInit(DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB);
+ /* Initialize Rx Descriptors list: Chain Mode */
+ ETH_DMARxDescChainInit(DMARxDscrTab, &Rx_Buff[0][0], ETH_RXBUFNB);
+
+ /* Enable MAC and DMA transmission and reception */
+ ETH_Start();
+
+ /* Enable Ethernet Rx interrrupt */
+ {
+ for(i=0; i= 0)
+ {
+ StartupKTask(thr_id);
+ }
+ else
+ {
+ KPrintf("Eth create failed !");
+ }
+}
+
+
+/**
+ * This function should do the actual transmission of the packet. The packet is
+ * contained in the pbuf that is passed to the function. This pbuf
+ * might be chained.
+ *
+ * @param netif the lwip network interface structure for this ethernetif
+ * @param p the MAC packet to send (e.g. IP packet including MAC addresses and type)
+ * @return ERR_OK if the packet could be sent
+ * an err_t value if the packet couldn't be sent
+ *
+ * @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to
+ * strange results. You might consider waiting for space in the DMA queue
+ * to become availale since the stack doesn't retry to send a packet
+ * dropped because of memory failure (except for the TCP timers).
+ */
+
+static err_t low_level_output(struct netif *netif, struct pbuf *p)
+{
+ static int32 sem = -1;
+ struct pbuf *q;
+ uint32_t l = 0;
+ u8 *buffer ;
+
+ if(sem < 0)
+ {
+ sem = KSemaphoreCreate(1);
+ }
+
+ KSemaphoreObtain(sem, WAITING_FOREVER);
+
+
+ buffer = (u8 *)(DMATxDescToSet->Buffer1Addr);
+ for(q = p; q != NULL; q = q->next)
+ {
+ memcpy((u8_t*)&buffer[l], q->payload, q->len);
+ l = l + q->len;
+ }
+ ETH_Prepare_Transmit_Descriptors(l);
+
+ KSemaphoreAbandon(sem);
+
+ return ERR_OK;
+}
+
+
+
+
+
+/**
+ * Should allocate a pbuf and transfer the bytes of the incoming
+ * packet from the interface into the pbuf.
+ *
+ * @param netif the lwip network interface structure for this ethernetif
+ * @return a pbuf filled with the received packet (including MAC header)
+ * NULL on memory error
+ */
+static struct pbuf * low_level_input(struct netif *netif)
+{
+ struct pbuf *p, *q;
+ u16_t len;
+ uint32_t l=0,i =0;
+ FrameTypeDef frame;
+ u8 *buffer;
+ __IO ETH_DMADESCTypeDef *DMARxNextDesc;
+
+ p = NULL;
+
+ /* Get received frame */
+ frame = ETH_Get_Received_Frame_interrupt();
+
+ /* check that frame has no error */
+ if ((frame.descriptor->Status & ETH_DMARxDesc_ES) == (uint32_t)RESET)
+ {
+
+ /* Obtain the size of the packet and put it into the "len" variable. */
+ len = frame.length;
+ buffer = (u8 *)frame.buffer;
+
+ /* We allocate a pbuf chain of pbufs from the pool. */
+ p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
+
+ /* Copy received frame from ethernet driver buffer to stack buffer */
+ if (p != NULL)
+ {
+ for (q = p; q != NULL; q = q->next)
+ {
+ memcpy((u8_t*)q->payload, (u8_t*)&buffer[l], q->len);
+ l = l + q->len;
+ }
+ }
+ }
+
+ /* Release descriptors to DMA */
+ /* Check if received frame with multiple DMA buffer segments */
+ if (DMA_RX_FRAME_infos->Seg_Count > 1)
+ {
+ DMARxNextDesc = DMA_RX_FRAME_infos->FS_Rx_Desc;
+ }
+ else
+ {
+ DMARxNextDesc = frame.descriptor;
+ }
+
+ /* Set Own bit in Rx descriptors: gives the buffers back to DMA */
+ for (i=0; iSeg_Count; i++)
+ {
+ DMARxNextDesc->Status = ETH_DMARxDesc_OWN;
+ DMARxNextDesc = (ETH_DMADESCTypeDef *)(DMARxNextDesc->Buffer2NextDescAddr);
+ }
+
+ /* Clear Segment_Count */
+ DMA_RX_FRAME_infos->Seg_Count =0;
+
+
+ /* When Rx Buffer unavailable flag is set: clear it and resume reception */
+ if ((ETH->DMASR & ETH_DMASR_RBUS) != (u32)RESET)
+ {
+ /* Clear RBUS ETHERNET DMA flag */
+ ETH->DMASR = ETH_DMASR_RBUS;
+
+ /* Resume DMA reception */
+ ETH->DMARPDR = 0;
+ }
+ return p;
+}
+
+
+/**
+ * This function is the ethernetif_input task, it is processed when a packet
+ * is ready to be read from the interface. It uses the function low_level_input()
+ * that should handle the actual reception of bytes from the network
+ * interface. Then the type of the received packet is determined and
+ * the appropriate input function is called.
+ *
+ * @param netif the lwip network interface structure for this ethernetif
+ */
+void ethernetif_input( void * pvParameters )
+{
+ struct pbuf *p;
+
+ for( ;; )
+ {
+ if (KSemaphoreObtain( s_xSemaphore, WAITING_FOREVER)==EOK)
+ {
+ p = low_level_input( s_pxNetIf );
+
+ if (ERR_OK != s_pxNetIf->input( p, s_pxNetIf))
+ {
+ KPrintf("netif input return not OK ! \n");
+ pbuf_free(p);
+ p=NULL;
+ }
+ }
+ }
+}
+
+void LoopGetMacPkg(void * pvParameters)
+{
+ // Loop Get MAX Pkg
+ struct netif *netif = (struct netif *)pvParameters;
+ while (1)
+ {
+ /* check if any packet received */
+ if (ETH_CheckFrameReceived())
+ {
+ KPrintf("ETH_CheckFrameReceived invoke !\n");
+ /* process received ethernet packet */
+ LwIP_Pkt_Handle(netif);
+ }
+ /* handle periodic timers for LwIP */
+ LwIP_Periodic_Handle(LocalTime);
+ }
+}
+
+/**
+ * Should be called at the beginning of the program to set up the
+ * network interface. It calls the function low_level_init() to do the
+ * actual setup of the hardware.
+ *
+ * This function should be passed as a parameter to netif_add().
+ *
+ * @param netif the lwip network interface structure for this ethernetif
+ * @return ERR_OK if the loopif is initialized
+ * ERR_MEM if private data couldn't be allocated
+ * any other err_t on error
+ */
+err_t ethernetif_init(struct netif *netif)
+{
+ LWIP_ASSERT("netif != NULL", (netif != NULL));
+
+#if LWIP_NETIF_HOSTNAME
+ /* Initialize interface hostname */
+ netif->hostname = "lwip";
+#endif /* LWIP_NETIF_HOSTNAME */
+
+ netif->name[0] = IFNAME0;
+ netif->name[1] = IFNAME1;
+
+ netif->output = etharp_output;
+ netif->linkoutput = low_level_output;
+
+ /* initialize the hardware */
+ low_level_init(netif);
+
+ etharp_init();
+ sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL);
+
+ return ERR_OK;
+}
+
+
+static void arp_timer(void *arg)
+{
+ etharp_tmr();
+ sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL);
+}
diff --git a/board/stm32f407-st-discovery/third_party_driver/ethernet/hardware_ethernet.c b/board/stm32f407-st-discovery/third_party_driver/ethernet/hardware_ethernet.c
new file mode 100644
index 00000000..20146a1b
--- /dev/null
+++ b/board/stm32f407-st-discovery/third_party_driver/ethernet/hardware_ethernet.c
@@ -0,0 +1,2718 @@
+/******************************************************************************
+/**
+ * @file hardware_ethernet.c
+ * @author MCD Application Team
+ * @version V1.0.0
+ * @date 14-October-2011
+ * @brief This file is the low level driver for STM32F407xx/417xx Ethernet Controller.
+ * This driver does not include low level functions for PTP time-stamp.
+ ******************************************************************************
+ * @attention
+ *
+ * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
+ * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
+ * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
+ * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
+ * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
+ * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
+ *
+ * © COPYRIGHT 2011 STMicroelectronics
+ ******************************************************************************
+ */
+
+/* Includes ------------------------------------------------------------------*/
+#include "hardware_eth.h"
+#include "hardware_rcc.h"
+#include "hardware_conf.h"
+#include
+#include
+
+/** @addtogroup STM32F4x7_ETH_Driver
+ * @brief ETH driver modules
+ * @{
+ */
+
+/** @defgroup ETH_Private_TypesDefinitions
+ * @{
+ */
+/**
+ * @}
+ */
+
+
+/** @defgroup ETH_Private_Defines
+ * @{
+ */
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Private_Macros
+ * @{
+ */
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Private_Variables
+ * @{
+ */
+
+#if defined (__CC_ARM) /*!< ARM Compiler */
+ __align(4)
+ ETH_DMADESCTypeDef DMARxDscrTab[ETH_RXBUFNB];/* Ethernet Rx MA Descriptor */
+ __align(4)
+ ETH_DMADESCTypeDef DMATxDscrTab[ETH_TXBUFNB];/* Ethernet Tx DMA Descriptor */
+ __align(4)
+ uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE]; /* Ethernet Receive Buffer */
+ __align(4)
+ uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE]; /* Ethernet Transmit Buffer */
+
+#elif defined ( __ICCARM__ ) /*!< IAR Compiler */
+ #pragma data_alignment=4
+ ETH_DMADESCTypeDef DMARxDscrTab[ETH_RXBUFNB];/* Ethernet Rx MA Descriptor */
+ #pragma data_alignment=4
+ ETH_DMADESCTypeDef DMATxDscrTab[ETH_TXBUFNB];/* Ethernet Tx DMA Descriptor */
+ #pragma data_alignment=4
+ uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE]; /* Ethernet Receive Buffer */
+ #pragma data_alignment=4
+ uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE]; /* Ethernet Transmit Buffer */
+
+#elif defined (__GNUC__) /*!< GNU Compiler */
+ ETH_DMADESCTypeDef DMARxDscrTab[ETH_RXBUFNB] __attribute__ ((aligned (4))); /* Ethernet Rx DMA Descriptor */
+ ETH_DMADESCTypeDef DMATxDscrTab[ETH_TXBUFNB] __attribute__ ((aligned (4))); /* Ethernet Tx DMA Descriptor */
+ uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE] __attribute__ ((aligned (4))); /* Ethernet Receive Buffer */
+ uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE] __attribute__ ((aligned (4))); /* Ethernet Transmit Buffer */
+
+#elif defined (__TASKING__) /*!< TASKING Compiler */
+ __align(4)
+ ETH_DMADESCTypeDef DMARxDscrTab[ETH_RXBUFNB];/* Ethernet Rx MA Descriptor */
+ __align(4)
+ ETH_DMADESCTypeDef DMATxDscrTab[ETH_TXBUFNB];/* Ethernet Tx DMA Descriptor */
+ __align(4)
+ uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE]; /* Ethernet Receive Buffer */
+ __align(4)
+ uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE]; /* Ethernet Transmit Buffer */
+
+#endif /* __CC_ARM */
+
+
+/* Global pointers on Tx and Rx descriptor used to track transmit and receive descriptors */
+__IO ETH_DMADESCTypeDef *DMATxDescToSet;
+__IO ETH_DMADESCTypeDef *DMARxDescToGet;
+
+
+/* Structure used to hold the last received packet descriptors info */
+
+ETH_DMA_Rx_Frame_infos RX_Frame_Descriptor;
+__IO ETH_DMA_Rx_Frame_infos *DMA_RX_FRAME_infos;
+__IO uint32_t Frame_Rx_index;
+
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Private_FunctionPrototypes
+ * @{
+ */
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Private_Functions
+ * @{
+ */
+
+#ifndef USE_Delay
+/**
+ * @brief Inserts a delay time.
+ * @param nCount: specifies the delay time length.
+ * @retval None
+ */
+static void ETH_Delay(__IO uint32_t nCount)
+{
+ __IO uint32_t index = 0;
+ for(index = nCount; index != 0; index--)
+ {
+ }
+}
+#endif /* USE_Delay*/
+
+
+
+/******************************************************************************/
+/* Global ETH MAC/DMA functions */
+/******************************************************************************/
+
+/**
+ * @brief Deinitializes the ETHERNET peripheral registers to their default reset values.
+ * @param None
+ * @retval None
+ */
+void ETH_DeInit(void)
+{
+ RCC_AHB1PeriphResetCmd(RCC_AHB1Periph_ETH_MAC, ENABLE);
+ RCC_AHB1PeriphResetCmd(RCC_AHB1Periph_ETH_MAC, DISABLE);
+}
+
+
+/**
+ * @brief Fills each ETH_InitStruct member with its default value.
+ * @param ETH_InitStruct: pointer to a ETH_InitTypeDef structure which will be initialized.
+ * @retval None
+ */
+void ETH_StructInit(ETH_InitTypeDef* ETH_InitStruct)
+{
+ /* ETH_InitStruct members default value */
+ /*------------------------ MAC Configuration ---------------------------*/
+
+ /* PHY Auto-negotiation enabled */
+ ETH_InitStruct->ETH_AutoNegotiation = ETH_AutoNegotiation_Enable;
+ /* MAC watchdog enabled: cuts-off long frame */
+ ETH_InitStruct->ETH_Watchdog = ETH_Watchdog_Enable;
+ /* MAC Jabber enabled in Half-duplex mode */
+ ETH_InitStruct->ETH_Jabber = ETH_Jabber_Enable;
+ /* Ethernet interframe gap set to 96 bits */
+ ETH_InitStruct->ETH_InterFrameGap = ETH_InterFrameGap_96Bit;
+ /* Carrier Sense Enabled in Half-Duplex mode */
+ ETH_InitStruct->ETH_CarrierSense = ETH_CarrierSense_Enable;
+ /* PHY speed configured to 100Mbit/s */
+ ETH_InitStruct->ETH_Speed = ETH_Speed_100M;
+ /* Receive own Frames in Half-Duplex mode enabled */
+ ETH_InitStruct->ETH_ReceiveOwn = ETH_ReceiveOwn_Enable;
+ /* MAC MII loopback disabled */
+ ETH_InitStruct->ETH_LoopbackMode = ETH_LoopbackMode_Disable;
+ /* Full-Duplex mode selected */
+ ETH_InitStruct->ETH_Mode = ETH_Mode_FullDuplex;
+ /* IPv4 and TCP/UDP/ICMP frame Checksum Offload disabled */
+ ETH_InitStruct->ETH_ChecksumOffload = ETH_ChecksumOffload_Disable;
+ /* Retry Transmission enabled for half-duplex mode */
+ ETH_InitStruct->ETH_RetryTransmission = ETH_RetryTransmission_Enable;
+ /* Automatic PAD/CRC strip disabled*/
+ ETH_InitStruct->ETH_AutomaticPadCRCStrip = ETH_AutomaticPadCRCStrip_Disable;
+ /* half-duplex mode retransmission Backoff time_limit = 10 slot times*/
+ ETH_InitStruct->ETH_BackOffLimit = ETH_BackOffLimit_10;
+ /* half-duplex mode Deferral check disabled */
+ ETH_InitStruct->ETH_DeferralCheck = ETH_DeferralCheck_Disable;
+ /* Receive all frames disabled */
+ ETH_InitStruct->ETH_ReceiveAll = ETH_ReceiveAll_Disable;
+ /* Source address filtering (on the optional MAC addresses) disabled */
+ ETH_InitStruct->ETH_SourceAddrFilter = ETH_SourceAddrFilter_Disable;
+ /* Do not forward control frames that do not pass the address filtering */
+ ETH_InitStruct->ETH_PassControlFrames = ETH_PassControlFrames_BlockAll;
+ /* Disable reception of Broadcast frames */
+ ETH_InitStruct->ETH_BroadcastFramesReception = ETH_BroadcastFramesReception_Disable;
+ /* Normal Destination address filtering (not reverse addressing) */
+ ETH_InitStruct->ETH_DestinationAddrFilter = ETH_DestinationAddrFilter_Normal;
+ /* Promiscuous address filtering mode disabled */
+ ETH_InitStruct->ETH_PromiscuousMode = ETH_PromiscuousMode_Disable;
+ /* Perfect address filtering for multicast addresses */
+ ETH_InitStruct->ETH_MulticastFramesFilter = ETH_MulticastFramesFilter_Perfect;
+ /* Perfect address filtering for unicast addresses */
+ ETH_InitStruct->ETH_UnicastFramesFilter = ETH_UnicastFramesFilter_Perfect;
+ /* Initialize hash table high and low regs */
+ ETH_InitStruct->ETH_HashTableHigh = 0x0;
+ ETH_InitStruct->ETH_HashTableLow = 0x0;
+ /* Flow control config (flow control disabled)*/
+ ETH_InitStruct->ETH_PauseTime = 0x0;
+ ETH_InitStruct->ETH_ZeroQuantaPause = ETH_ZeroQuantaPause_Disable;
+ ETH_InitStruct->ETH_PauseLowThreshold = ETH_PauseLowThreshold_Minus4;
+ ETH_InitStruct->ETH_UnicastPauseFrameDetect = ETH_UnicastPauseFrameDetect_Disable;
+ ETH_InitStruct->ETH_ReceiveFlowControl = ETH_ReceiveFlowControl_Disable;
+ ETH_InitStruct->ETH_TransmitFlowControl = ETH_TransmitFlowControl_Disable;
+ /* VLANtag config (VLAN field not checked) */
+ ETH_InitStruct->ETH_VLANTagComparison = ETH_VLANTagComparison_16Bit;
+ ETH_InitStruct->ETH_VLANTagIdentifier = 0x0;
+
+ /*---------------------- DMA Configuration -------------------------------*/
+
+ /* Drops frames with with TCP/IP checksum errors */
+ ETH_InitStruct->ETH_DropTCPIPChecksumErrorFrame = ETH_DropTCPIPChecksumErrorFrame_Disable;
+ /* Store and forward mode enabled for receive */
+ ETH_InitStruct->ETH_ReceiveStoreForward = ETH_ReceiveStoreForward_Enable;
+ /* Flush received frame that created FIFO overflow */
+ ETH_InitStruct->ETH_FlushReceivedFrame = ETH_FlushReceivedFrame_Enable;
+ /* Store and forward mode enabled for transmit */
+ ETH_InitStruct->ETH_TransmitStoreForward = ETH_TransmitStoreForward_Enable;
+ /* Threshold TXFIFO level set to 64 bytes (used when threshold mode is enabled) */
+ ETH_InitStruct->ETH_TransmitThresholdControl = ETH_TransmitThresholdControl_64Bytes;
+ /* Disable forwarding frames with errors (short frames, CRC,...)*/
+ ETH_InitStruct->ETH_ForwardErrorFrames = ETH_ForwardErrorFrames_Disable;
+ /* Disable undersized good frames */
+ ETH_InitStruct->ETH_ForwardUndersizedGoodFrames = ETH_ForwardUndersizedGoodFrames_Disable;
+ /* Threshold RXFIFO level set to 64 bytes (used when Cut-through mode is enabled) */
+ ETH_InitStruct->ETH_ReceiveThresholdControl = ETH_ReceiveThresholdControl_64Bytes;
+ /* Disable Operate on second frame (transmit a second frame to FIFO without
+ waiting status of previous frame*/
+ ETH_InitStruct->ETH_SecondFrameOperate = ETH_SecondFrameOperate_Disable;
+ /* DMA works on 32-bit aligned start source and destinations addresses */
+ ETH_InitStruct->ETH_AddressAlignedBeats = ETH_AddressAlignedBeats_Enable;
+ /* Enabled Fixed Burst Mode (mix of INC4, INC8, INC16 and SINGLE DMA transactions */
+ ETH_InitStruct->ETH_FixedBurst = ETH_FixedBurst_Enable;
+ /* DMA transfer max burst length = 32 beats = 32 x 32bits */
+ ETH_InitStruct->ETH_RxDMABurstLength = ETH_RxDMABurstLength_32Beat;
+ ETH_InitStruct->ETH_TxDMABurstLength = ETH_TxDMABurstLength_32Beat;
+ /* DMA Ring mode skip length = 0 */
+ ETH_InitStruct->ETH_DescriptorSkipLength = 0x0;
+ /* Equal priority (round-robin) between transmit and receive DMA engines */
+ ETH_InitStruct->ETH_DMAArbitration = ETH_DMAArbitration_RoundRobin_RxTx_1_1;
+}
+
+
+/**
+ * @brief Initializes the ETHERNET peripheral according to the specified
+ * parameters in the ETH_InitStruct .
+ * @param ETH_InitStruct: pointer to a ETH_InitTypeDef structure that contains
+ * the configuration information for the specified ETHERNET peripheral.
+ * @param PHYAddress: external PHY address
+ * @retval ETH_ERROR: Ethernet initialization failed
+ * ETH_SUCCESS: Ethernet successfully initialized
+ */
+uint32_t ETH_Init(ETH_InitTypeDef* ETH_InitStruct, uint16_t PHYAddress)
+{
+ uint32_t RegValue = 0, tmpreg = 0;
+ __IO uint32_t i = 0;
+ RCC_ClocksTypeDef rcc_clocks;
+ uint32_t hclk = 60000000;
+ __IO uint32_t timeout = 0;
+ /* Check the parameters */
+ /* MAC --------------------------*/
+ assert_param(IS_ETH_AUTONEGOTIATION(ETH_InitStruct->ETH_AutoNegotiation));
+ assert_param(IS_ETH_WATCHDOG(ETH_InitStruct->ETH_Watchdog));
+ assert_param(IS_ETH_JABBER(ETH_InitStruct->ETH_Jabber));
+ assert_param(IS_ETH_INTER_FRAME_GAP(ETH_InitStruct->ETH_InterFrameGap));
+ assert_param(IS_ETH_CARRIER_SENSE(ETH_InitStruct->ETH_CarrierSense));
+ assert_param(IS_ETH_SPEED(ETH_InitStruct->ETH_Speed));
+ assert_param(IS_ETH_RECEIVE_OWN(ETH_InitStruct->ETH_ReceiveOwn));
+ assert_param(IS_ETH_LOOPBACK_MODE(ETH_InitStruct->ETH_LoopbackMode));
+ assert_param(IS_ETH_DUPLEX_MODE(ETH_InitStruct->ETH_Mode));
+ assert_param(IS_ETH_CHECKSUM_OFFLOAD(ETH_InitStruct->ETH_ChecksumOffload));
+ assert_param(IS_ETH_RETRY_TRANSMISSION(ETH_InitStruct->ETH_RetryTransmission));
+ assert_param(IS_ETH_AUTOMATIC_PADCRC_STRIP(ETH_InitStruct->ETH_AutomaticPadCRCStrip));
+ assert_param(IS_ETH_BACKOFF_LIMIT(ETH_InitStruct->ETH_BackOffLimit));
+ assert_param(IS_ETH_DEFERRAL_CHECK(ETH_InitStruct->ETH_DeferralCheck));
+ assert_param(IS_ETH_RECEIVE_ALL(ETH_InitStruct->ETH_ReceiveAll));
+ assert_param(IS_ETH_SOURCE_ADDR_FILTER(ETH_InitStruct->ETH_SourceAddrFilter));
+ assert_param(IS_ETH_CONTROL_FRAMES(ETH_InitStruct->ETH_PassControlFrames));
+ assert_param(IS_ETH_BROADCAST_FRAMES_RECEPTION(ETH_InitStruct->ETH_BroadcastFramesReception));
+ assert_param(IS_ETH_DESTINATION_ADDR_FILTER(ETH_InitStruct->ETH_DestinationAddrFilter));
+ // assert_param(IS_ETH_PROMISCIOUS_MODE(ETH_InitStruct->ETH_PromiscuousMode));
+ assert_param(IS_ETH_MULTICAST_FRAMES_FILTER(ETH_InitStruct->ETH_MulticastFramesFilter));
+ assert_param(IS_ETH_UNICAST_FRAMES_FILTER(ETH_InitStruct->ETH_UnicastFramesFilter));
+ assert_param(IS_ETH_PAUSE_TIME(ETH_InitStruct->ETH_PauseTime));
+ assert_param(IS_ETH_ZEROQUANTA_PAUSE(ETH_InitStruct->ETH_ZeroQuantaPause));
+ assert_param(IS_ETH_PAUSE_LOW_THRESHOLD(ETH_InitStruct->ETH_PauseLowThreshold));
+ assert_param(IS_ETH_UNICAST_PAUSE_FRAME_DETECT(ETH_InitStruct->ETH_UnicastPauseFrameDetect));
+ assert_param(IS_ETH_RECEIVE_FLOWCONTROL(ETH_InitStruct->ETH_ReceiveFlowControl));
+ assert_param(IS_ETH_TRANSMIT_FLOWCONTROL(ETH_InitStruct->ETH_TransmitFlowControl));
+ assert_param(IS_ETH_VLAN_TAG_COMPARISON(ETH_InitStruct->ETH_VLANTagComparison));
+ assert_param(IS_ETH_VLAN_TAG_IDENTIFIER(ETH_InitStruct->ETH_VLANTagIdentifier));
+ /* DMA --------------------------*/
+ assert_param(IS_ETH_DROP_TCPIP_CHECKSUM_FRAME(ETH_InitStruct->ETH_DropTCPIPChecksumErrorFrame));
+ assert_param(IS_ETH_RECEIVE_STORE_FORWARD(ETH_InitStruct->ETH_ReceiveStoreForward));
+ assert_param(IS_ETH_FLUSH_RECEIVE_FRAME(ETH_InitStruct->ETH_FlushReceivedFrame));
+ assert_param(IS_ETH_TRANSMIT_STORE_FORWARD(ETH_InitStruct->ETH_TransmitStoreForward));
+ assert_param(IS_ETH_TRANSMIT_THRESHOLD_CONTROL(ETH_InitStruct->ETH_TransmitThresholdControl));
+ assert_param(IS_ETH_FORWARD_ERROR_FRAMES(ETH_InitStruct->ETH_ForwardErrorFrames));
+ assert_param(IS_ETH_FORWARD_UNDERSIZED_GOOD_FRAMES(ETH_InitStruct->ETH_ForwardUndersizedGoodFrames));
+ assert_param(IS_ETH_RECEIVE_THRESHOLD_CONTROL(ETH_InitStruct->ETH_ReceiveThresholdControl));
+ assert_param(IS_ETH_SECOND_FRAME_OPERATE(ETH_InitStruct->ETH_SecondFrameOperate));
+ assert_param(IS_ETH_ADDRESS_ALIGNED_BEATS(ETH_InitStruct->ETH_AddressAlignedBeats));
+ assert_param(IS_ETH_FIXED_BURST(ETH_InitStruct->ETH_FixedBurst));
+ assert_param(IS_ETH_RXDMA_BURST_LENGTH(ETH_InitStruct->ETH_RxDMABurstLength));
+ assert_param(IS_ETH_TXDMA_BURST_LENGTH(ETH_InitStruct->ETH_TxDMABurstLength));
+ assert_param(IS_ETH_DMA_DESC_SKIP_LENGTH(ETH_InitStruct->ETH_DescriptorSkipLength));
+ assert_param(IS_ETH_DMA_ARBITRATION_ROUNDROBIN_RXTX(ETH_InitStruct->ETH_DMAArbitration));
+ /*-------------------------------- MAC Config ------------------------------*/
+ /*---------------------- ETHERNET MACMIIAR Configuration -------------------*/
+ /* Get the ETHERNET MACMIIAR value */
+ tmpreg = ETH->MACMIIAR;
+ /* Clear CSR Clock Range CR[2:0] bits */
+ tmpreg &= MACMIIAR_CR_MASK;
+ /* Get hclk frequency value */
+ RCC_GetClocksFreq(&rcc_clocks);
+ hclk = rcc_clocks.HCLK_Frequency;
+
+ /* Set CR bits depending on hclk value */
+ if((hclk >= 20000000)&&(hclk < 35000000))
+ {
+ /* CSR Clock Range between 20-35 MHz */
+ tmpreg |= (uint32_t)ETH_MACMIIAR_CR_Div16;
+ }
+ else if((hclk >= 35000000)&&(hclk < 60000000))
+ {
+ /* CSR Clock Range between 35-60 MHz */
+ tmpreg |= (uint32_t)ETH_MACMIIAR_CR_Div26;
+ }
+ else if((hclk >= 60000000)&&(hclk < 100000000))
+ {
+ /* CSR Clock Range between 60-100 MHz */
+ tmpreg |= (uint32_t)ETH_MACMIIAR_CR_Div42;
+ }
+ else if((hclk >= 100000000)&&(hclk < 150000000))
+ {
+ /* CSR Clock Range between 100-150 MHz */
+ tmpreg |= (uint32_t)ETH_MACMIIAR_CR_Div62;
+ }
+ else /* ((hclk >= 150000000)&&(hclk <= 168000000)) */
+ {
+ /* CSR Clock Range between 150-168 MHz */
+ tmpreg |= (uint32_t)ETH_MACMIIAR_CR_Div102;
+ }
+
+ /* Write to ETHERNET MAC MIIAR: Configure the ETHERNET CSR Clock Range */
+ ETH->MACMIIAR = (uint32_t)tmpreg;
+ /*-------------------- PHY initialization and configuration ----------------*/
+ /* Put the PHY in reset mode */
+ if(!(ETH_WritePHYRegister(PHYAddress, PHY_BCR, PHY_Reset)))
+ {
+ /* Return ERROR in case of write timeout */
+ return ETH_ERROR;
+ }
+
+ /* Delay to assure PHY reset */
+ _eth_delay_(PHY_RESET_DELAY);
+
+ if(ETH_InitStruct->ETH_AutoNegotiation != ETH_AutoNegotiation_Disable)
+ {
+ /* We wait for linked status... */
+ do
+ {
+ timeout++;
+ } while (!(ETH_ReadPHYRegister(PHYAddress, PHY_BSR) & PHY_Linked_Status) && (timeout < PHY_READ_TO));
+
+ /* Return ERROR in case of timeout */
+ if(timeout == PHY_READ_TO)
+ {
+ return ETH_ERROR;
+ }
+
+ /* Reset Timeout counter */
+ timeout = 0;
+ /* Enable Auto-Negotiation */
+ if(!(ETH_WritePHYRegister(PHYAddress, PHY_BCR, PHY_AutoNegotiation)))
+ {
+ /* Return ERROR in case of write timeout */
+ return ETH_ERROR;
+ }
+
+ /* Wait until the auto-negotiation will be completed */
+ do
+ {
+ timeout++;
+ } while (!(ETH_ReadPHYRegister(PHYAddress, PHY_BSR) & PHY_AutoNego_Complete) && (timeout < (uint32_t)PHY_READ_TO));
+
+ /* Return ERROR in case of timeout */
+ if(timeout == PHY_READ_TO)
+ {
+ return ETH_ERROR;
+ }
+
+ /* Reset Timeout counter */
+ timeout = 0;
+
+ /* Read the result of the auto-negotiation */
+ RegValue = ETH_ReadPHYRegister(PHYAddress, PHY_SR);
+
+ /* Configure the MAC with the Duplex Mode fixed by the auto-negotiation process */
+ if((RegValue & PHY_DUPLEX_STATUS) != (uint32_t)RESET)
+ {
+ /* Set Ethernet duplex mode to Full-duplex following the auto-negotiation */
+ ETH_InitStruct->ETH_Mode = ETH_Mode_FullDuplex;
+ }
+ else
+ {
+ /* Set Ethernet duplex mode to Half-duplex following the auto-negotiation */
+ ETH_InitStruct->ETH_Mode = ETH_Mode_HalfDuplex;
+ }
+
+ /* Configure the MAC with the speed fixed by the auto-negotiation process */
+ if(RegValue & PHY_SPEED_STATUS)
+ {
+ /* Set Ethernet speed to 10M following the auto-negotiation */
+ ETH_InitStruct->ETH_Speed = ETH_Speed_10M;
+ }
+ else
+ {
+ /* Set Ethernet speed to 100M following the auto-negotiation */
+ ETH_InitStruct->ETH_Speed = ETH_Speed_100M;
+ }
+ }
+ else
+ {
+ if(!ETH_WritePHYRegister(PHYAddress, PHY_BCR, ((uint16_t)(ETH_InitStruct->ETH_Mode >> 3) |
+ (uint16_t)(ETH_InitStruct->ETH_Speed >> 1))))
+ {
+ /* Return ERROR in case of write timeout */
+ return ETH_ERROR;
+ }
+ /* Delay to assure PHY configuration */
+ _eth_delay_(PHY_CONFIG_DELAY);
+
+ }
+ /*------------------------ ETHERNET MACCR Configuration --------------------*/
+ /* Get the ETHERNET MACCR value */
+ tmpreg = ETH->MACCR;
+ /* Clear WD, PCE, PS, TE and RE bits */
+ tmpreg &= MACCR_CLEAR_MASK;
+ /* Set the WD bit according to ETH_Watchdog value */
+ /* Set the JD: bit according to ETH_Jabber value */
+ /* Set the IFG bit according to ETH_InterFrameGap value */
+ /* Set the DCRS bit according to ETH_CarrierSense value */
+ /* Set the FES bit according to ETH_Speed value */
+ /* Set the DO bit according to ETH_ReceiveOwn value */
+ /* Set the LM bit according to ETH_LoopbackMode value */
+ /* Set the DM bit according to ETH_Mode value */
+ /* Set the IPCO bit according to ETH_ChecksumOffload value */
+ /* Set the DR bit according to ETH_RetryTransmission value */
+ /* Set the ACS bit according to ETH_AutomaticPadCRCStrip value */
+ /* Set the BL bit according to ETH_BackOffLimit value */
+ /* Set the DC bit according to ETH_DeferralCheck value */
+ tmpreg |= (uint32_t)(ETH_InitStruct->ETH_Watchdog |
+ ETH_InitStruct->ETH_Jabber |
+ ETH_InitStruct->ETH_InterFrameGap |
+ ETH_InitStruct->ETH_CarrierSense |
+ ETH_InitStruct->ETH_Speed |
+ ETH_InitStruct->ETH_ReceiveOwn |
+ ETH_InitStruct->ETH_LoopbackMode |
+ ETH_InitStruct->ETH_Mode |
+ ETH_InitStruct->ETH_ChecksumOffload |
+ ETH_InitStruct->ETH_RetryTransmission |
+ ETH_InitStruct->ETH_AutomaticPadCRCStrip |
+ ETH_InitStruct->ETH_BackOffLimit |
+ ETH_InitStruct->ETH_DeferralCheck);
+ /* Write to ETHERNET MACCR */
+ ETH->MACCR = (uint32_t)tmpreg;
+
+ /*----------------------- ETHERNET MACFFR Configuration --------------------*/
+ /* Set the RA bit according to ETH_ReceiveAll value */
+ /* Set the SAF and SAIF bits according to ETH_SourceAddrFilter value */
+ /* Set the PCF bit according to ETH_PassControlFrames value */
+ /* Set the DBF bit according to ETH_BroadcastFramesReception value */
+ /* Set the DAIF bit according to ETH_DestinationAddrFilter value */
+ /* Set the PR bit according to ETH_PromiscuousMode value */
+ /* Set the PM, HMC and HPF bits according to ETH_MulticastFramesFilter value */
+ /* Set the HUC and HPF bits according to ETH_UnicastFramesFilter value */
+ /* Write to ETHERNET MACFFR */
+ ETH->MACFFR = (uint32_t)(ETH_InitStruct->ETH_ReceiveAll |
+ ETH_InitStruct->ETH_SourceAddrFilter |
+ ETH_InitStruct->ETH_PassControlFrames |
+ ETH_InitStruct->ETH_BroadcastFramesReception |
+ ETH_InitStruct->ETH_DestinationAddrFilter |
+ ETH_InitStruct->ETH_PromiscuousMode |
+ ETH_InitStruct->ETH_MulticastFramesFilter |
+ ETH_InitStruct->ETH_UnicastFramesFilter);
+ /*--------------- ETHERNET MACHTHR and MACHTLR Configuration ---------------*/
+ /* Write to ETHERNET MACHTHR */
+ ETH->MACHTHR = (uint32_t)ETH_InitStruct->ETH_HashTableHigh;
+ /* Write to ETHERNET MACHTLR */
+ ETH->MACHTLR = (uint32_t)ETH_InitStruct->ETH_HashTableLow;
+ /*----------------------- ETHERNET MACFCR Configuration --------------------*/
+ /* Get the ETHERNET MACFCR value */
+ tmpreg = ETH->MACFCR;
+ /* Clear xx bits */
+ tmpreg &= MACFCR_CLEAR_MASK;
+
+ /* Set the PT bit according to ETH_PauseTime value */
+ /* Set the DZPQ bit according to ETH_ZeroQuantaPause value */
+ /* Set the PLT bit according to ETH_PauseLowThreshold value */
+ /* Set the UP bit according to ETH_UnicastPauseFrameDetect value */
+ /* Set the RFE bit according to ETH_ReceiveFlowControl value */
+ /* Set the TFE bit according to ETH_TransmitFlowControl value */
+ tmpreg |= (uint32_t)((ETH_InitStruct->ETH_PauseTime << 16) |
+ ETH_InitStruct->ETH_ZeroQuantaPause |
+ ETH_InitStruct->ETH_PauseLowThreshold |
+ ETH_InitStruct->ETH_UnicastPauseFrameDetect |
+ ETH_InitStruct->ETH_ReceiveFlowControl |
+ ETH_InitStruct->ETH_TransmitFlowControl);
+ /* Write to ETHERNET MACFCR */
+ ETH->MACFCR = (uint32_t)tmpreg;
+ /*----------------------- ETHERNET MACVLANTR Configuration -----------------*/
+ /* Set the ETV bit according to ETH_VLANTagComparison value */
+ /* Set the VL bit according to ETH_VLANTagIdentifier value */
+ ETH->MACVLANTR = (uint32_t)(ETH_InitStruct->ETH_VLANTagComparison |
+ ETH_InitStruct->ETH_VLANTagIdentifier);
+
+ /*-------------------------------- DMA Config ------------------------------*/
+ /*----------------------- ETHERNET DMAOMR Configuration --------------------*/
+ /* Get the ETHERNET DMAOMR value */
+ tmpreg = ETH->DMAOMR;
+ /* Clear xx bits */
+ tmpreg &= DMAOMR_CLEAR_MASK;
+
+ /* Set the DT bit according to ETH_DropTCPIPChecksumErrorFrame value */
+ /* Set the RSF bit according to ETH_ReceiveStoreForward value */
+ /* Set the DFF bit according to ETH_FlushReceivedFrame value */
+ /* Set the TSF bit according to ETH_TransmitStoreForward value */
+ /* Set the TTC bit according to ETH_TransmitThresholdControl value */
+ /* Set the FEF bit according to ETH_ForwardErrorFrames value */
+ /* Set the FUF bit according to ETH_ForwardUndersizedGoodFrames value */
+ /* Set the RTC bit according to ETH_ReceiveThresholdControl value */
+ /* Set the OSF bit according to ETH_SecondFrameOperate value */
+ tmpreg |= (uint32_t)(ETH_InitStruct->ETH_DropTCPIPChecksumErrorFrame |
+ ETH_InitStruct->ETH_ReceiveStoreForward |
+ ETH_InitStruct->ETH_FlushReceivedFrame |
+ ETH_InitStruct->ETH_TransmitStoreForward |
+ ETH_InitStruct->ETH_TransmitThresholdControl |
+ ETH_InitStruct->ETH_ForwardErrorFrames |
+ ETH_InitStruct->ETH_ForwardUndersizedGoodFrames |
+ ETH_InitStruct->ETH_ReceiveThresholdControl |
+ ETH_InitStruct->ETH_SecondFrameOperate);
+ /* Write to ETHERNET DMAOMR */
+ ETH->DMAOMR = (uint32_t)tmpreg;
+
+ /*----------------------- ETHERNET DMABMR Configuration --------------------*/
+ /* Set the AAL bit according to ETH_AddressAlignedBeats value */
+ /* Set the FB bit according to ETH_FixedBurst value */
+ /* Set the RPBL and 4*PBL bits according to ETH_RxDMABurstLength value */
+ /* Set the PBL and 4*PBL bits according to ETH_TxDMABurstLength value */
+ /* Set the DSL bit according to ETH_DesciptorSkipLength value */
+ /* Set the PR and DA bits according to ETH_DMAArbitration value */
+ ETH->DMABMR = (uint32_t)(ETH_InitStruct->ETH_AddressAlignedBeats |
+ ETH_InitStruct->ETH_FixedBurst |
+ ETH_InitStruct->ETH_RxDMABurstLength | /* !! if 4xPBL is selected for Tx or Rx it is applied for the other */
+ ETH_InitStruct->ETH_TxDMABurstLength |
+ (ETH_InitStruct->ETH_DescriptorSkipLength << 2) |
+ ETH_InitStruct->ETH_DMAArbitration |
+ ETH_DMABMR_USP); /* Enable use of separate PBL for Rx and Tx */
+
+ #ifdef USE_ENHANCED_DMA_DESCRIPTORS
+ /* Enable the Enhanced DMA descriptors */
+ ETH->DMABMR |= ETH_DMABMR_EDE;
+ #endif /* USE_ENHANCED_DMA_DESCRIPTORS */
+
+ /* Return Ethernet configuration success */
+ return ETH_SUCCESS;
+}
+
+/**
+ * @brief Enables ENET MAC and DMA reception/transmission
+ * @param None
+ * @retval None
+ */
+void ETH_Start(void)
+{
+ /*tang****************************************/
+
+ NVIC_InitTypeDef NVIC_initstruct;
+ /*tang****************************************/
+ /* Enable transmit state machine of the MAC for transmission on the MII */
+ ETH_MACTransmissionCmd(ENABLE);
+ /* Flush Transmit FIFO */
+ ETH_FlushTransmitFIFO();
+ /* Enable receive state machine of the MAC for reception from the MII */
+ ETH_MACReceptionCmd(ENABLE);
+
+
+
+
+ /*tang****************************************/
+ ETH_DMAITConfig(ETH_DMA_IT_NIS | ETH_DMA_IT_R,ENABLE);
+ NVIC_initstruct.NVIC_IRQChannel = ETH_IRQn;
+ NVIC_initstruct.NVIC_IRQChannelPreemptionPriority =0;
+ NVIC_initstruct.NVIC_IRQChannelSubPriority = 0;
+ NVIC_initstruct.NVIC_IRQChannelCmd = ENABLE;
+ NVIC_Init(&NVIC_initstruct);
+
+ /*tang****************************************/
+
+ /* Start DMA transmission */
+ ETH_DMATransmissionCmd(ENABLE);
+ /* Start DMA reception */
+ ETH_DMAReceptionCmd(ENABLE);
+}
+
+
+/**
+ * @brief Enables or disables the MAC transmission.
+ * @param NewState: new state of the MAC transmission.
+ * This parameter can be: ENABLE or DISABLE.
+ * @retval None
+ */
+void ETH_MACTransmissionCmd(FunctionalState NewState)
+{
+ /* Check the parameters */
+ assert_param(IS_FUNCTIONAL_STATE(NewState));
+
+ if (NewState != DISABLE)
+ {
+ /* Enable the MAC transmission */
+ ETH->MACCR |= ETH_MACCR_TE;
+ }
+ else
+ {
+ /* Disable the MAC transmission */
+ ETH->MACCR &= ~ETH_MACCR_TE;
+ }
+}
+
+
+/**
+ * @brief Enables or disables the MAC reception.
+ * @param NewState: new state of the MAC reception.
+ * This parameter can be: ENABLE or DISABLE.
+ * @retval None
+ */
+void ETH_MACReceptionCmd(FunctionalState NewState)
+{
+ /* Check the parameters */
+ assert_param(IS_FUNCTIONAL_STATE(NewState));
+
+ if (NewState != DISABLE)
+ {
+ /* Enable the MAC reception */
+ ETH->MACCR |= ETH_MACCR_RE;
+ }
+ else
+ {
+ /* Disable the MAC reception */
+ ETH->MACCR &= ~ETH_MACCR_RE;
+ }
+}
+
+
+/**
+ * @brief Checks whether the ETHERNET flow control busy bit is set or not.
+ * @param None
+ * @retval The new state of flow control busy status bit (SET or RESET).
+ */
+FlagStatus ETH_GetFlowControlBusyStatus(void)
+{
+ FlagStatus bitstatus = RESET;
+ /* The Flow Control register should not be written to until this bit is cleared */
+ if ((ETH->MACFCR & ETH_MACFCR_FCBBPA) != (uint32_t)RESET)
+ {
+ bitstatus = SET;
+ }
+ else
+ {
+ bitstatus = RESET;
+ }
+ return bitstatus;
+}
+
+
+/**
+ * @brief Initiate a Pause Control Frame (Full-duplex only).
+ * @param None
+ * @retval None
+ */
+void ETH_InitiatePauseControlFrame(void)
+{
+ /* When Set In full duplex MAC initiates pause control frame */
+ ETH->MACFCR |= ETH_MACFCR_FCBBPA;
+}
+
+
+/**
+ * @brief Enables or disables the MAC BackPressure operation activation (Half-duplex only).
+ * @param NewState: new state of the MAC BackPressure operation activation.
+ * This parameter can be: ENABLE or DISABLE.
+ * @retval None
+ */
+void ETH_BackPressureActivationCmd(FunctionalState NewState)
+{
+ /* Check the parameters */
+ assert_param(IS_FUNCTIONAL_STATE(NewState));
+
+ if (NewState != DISABLE)
+ {
+ /* Activate the MAC BackPressure operation */
+ /* In Half duplex: during backpressure, when the MAC receives a new frame,
+ the transmitter starts sending a JAM pattern resulting in a collision */
+ ETH->MACFCR |= ETH_MACFCR_FCBBPA;
+ }
+ else
+ {
+ /* Desactivate the MAC BackPressure operation */
+ ETH->MACFCR &= ~ETH_MACFCR_FCBBPA;
+ }
+}
+
+
+/**
+ * @brief Checks whether the specified ETHERNET MAC flag is set or not.
+ * @param ETH_MAC_FLAG: specifies the flag to check.
+ * This parameter can be one of the following values:
+ * @arg ETH_MAC_FLAG_TST : Time stamp trigger flag
+ * @arg ETH_MAC_FLAG_MMCT : MMC transmit flag
+ * @arg ETH_MAC_FLAG_MMCR : MMC receive flag
+ * @arg ETH_MAC_FLAG_MMC : MMC flag
+ * @arg ETH_MAC_FLAG_PMT : PMT flag
+ * @retval The new state of ETHERNET MAC flag (SET or RESET).
+ */
+FlagStatus ETH_GetMACFlagStatus(uint32_t ETH_MAC_FLAG)
+{
+ FlagStatus bitstatus = RESET;
+ /* Check the parameters */
+ assert_param(IS_ETH_MAC_GET_FLAG(ETH_MAC_FLAG));
+ if ((ETH->MACSR & ETH_MAC_FLAG) != (uint32_t)RESET)
+ {
+ bitstatus = SET;
+ }
+ else
+ {
+ bitstatus = RESET;
+ }
+ return bitstatus;
+}
+
+
+/**
+ * @brief Checks whether the specified ETHERNET MAC interrupt has occurred or not.
+ * @param ETH_MAC_IT: specifies the interrupt source to check.
+ * This parameter can be one of the following values:
+ * @arg ETH_MAC_IT_TST : Time stamp trigger interrupt
+ * @arg ETH_MAC_IT_MMCT : MMC transmit interrupt
+ * @arg ETH_MAC_IT_MMCR : MMC receive interrupt
+ * @arg ETH_MAC_IT_MMC : MMC interrupt
+ * @arg ETH_MAC_IT_PMT : PMT interrupt
+ * @retval The new state of ETHERNET MAC interrupt (SET or RESET).
+ */
+ITStatus ETH_GetMACITStatus(uint32_t ETH_MAC_IT)
+{
+ ITStatus bitstatus = RESET;
+ /* Check the parameters */
+ assert_param(IS_ETH_MAC_GET_IT(ETH_MAC_IT));
+ if ((ETH->MACSR & ETH_MAC_IT) != (uint32_t)RESET)
+ {
+ bitstatus = SET;
+ }
+ else
+ {
+ bitstatus = RESET;
+ }
+ return bitstatus;
+}
+
+
+/**
+ * @brief Enables or disables the specified ETHERNET MAC interrupts.
+ * @param ETH_MAC_IT: specifies the ETHERNET MAC interrupt sources to be
+ * enabled or disabled.
+ * This parameter can be any combination of the following values:
+ * @arg ETH_MAC_IT_TST : Time stamp trigger interrupt
+ * @arg ETH_MAC_IT_PMT : PMT interrupt
+ * @param NewState: new state of the specified ETHERNET MAC interrupts.
+ * This parameter can be: ENABLE or DISABLE.
+ * @retval None
+ */
+void ETH_MACITConfig(uint32_t ETH_MAC_IT, FunctionalState NewState)
+{
+ /* Check the parameters */
+ assert_param(IS_ETH_MAC_IT(ETH_MAC_IT));
+ assert_param(IS_FUNCTIONAL_STATE(NewState));
+
+ if (NewState != DISABLE)
+ {
+ /* Enable the selected ETHERNET MAC interrupts */
+ ETH->MACIMR &= (~(uint32_t)ETH_MAC_IT);
+ }
+ else
+ {
+ /* Disable the selected ETHERNET MAC interrupts */
+ ETH->MACIMR |= ETH_MAC_IT;
+ }
+}
+
+
+/**
+ * @brief Configures the selected MAC address.
+ * @param MacAddr: The MAC address to configure.
+ * This parameter can be one of the following values:
+ * @arg ETH_MAC_Address0 : MAC Address0
+ * @arg ETH_MAC_Address1 : MAC Address1
+ * @arg ETH_MAC_Address2 : MAC Address2
+ * @arg ETH_MAC_Address3 : MAC Address3
+ * @param Addr: Pointer on MAC address buffer data (6 bytes).
+ * @retval None
+ */
+void ETH_MACAddressConfig(uint32_t MacAddr, uint8_t *Addr)
+{
+ uint32_t tmpreg;
+ /* Check the parameters */
+ assert_param(IS_ETH_MAC_ADDRESS0123(MacAddr));
+
+ /* Calculate the selected MAC address high register */
+ tmpreg = ((uint32_t)Addr[5] << 8) | (uint32_t)Addr[4];
+ /* Load the selected MAC address high register */
+ (*(__IO uint32_t *) (ETH_MAC_ADDR_HBASE + MacAddr)) = tmpreg;
+ /* Calculate the selected MAC address low register */
+ tmpreg = ((uint32_t)Addr[3] << 24) | ((uint32_t)Addr[2] << 16) | ((uint32_t)Addr[1] << 8) | Addr[0];
+
+ /* Load the selected MAC address low register */
+ (*(__IO uint32_t *) (ETH_MAC_ADDR_LBASE + MacAddr)) = tmpreg;
+}
+
+
+/**
+ * @brief Get the selected MAC address.
+ * @param MacAddr: The MAC address to return.
+ * This parameter can be one of the following values:
+ * @arg ETH_MAC_Address0 : MAC Address0
+ * @arg ETH_MAC_Address1 : MAC Address1
+ * @arg ETH_MAC_Address2 : MAC Address2
+ * @arg ETH_MAC_Address3 : MAC Address3
+ * @param Addr: Pointer on MAC address buffer data (6 bytes).
+ * @retval None
+ */
+void ETH_GetMACAddress(uint32_t MacAddr, uint8_t *Addr)
+{
+ uint32_t tmpreg;
+ /* Check the parameters */
+ assert_param(IS_ETH_MAC_ADDRESS0123(MacAddr));
+
+ /* Get the selected MAC address high register */
+ tmpreg =(*(__IO uint32_t *) (ETH_MAC_ADDR_HBASE + MacAddr));
+
+ /* Calculate the selected MAC address buffer */
+ Addr[5] = ((tmpreg >> 8) & (uint8_t)0xFF);
+ Addr[4] = (tmpreg & (uint8_t)0xFF);
+ /* Load the selected MAC address low register */
+ tmpreg =(*(__IO uint32_t *) (ETH_MAC_ADDR_LBASE + MacAddr));
+ /* Calculate the selected MAC address buffer */
+ Addr[3] = ((tmpreg >> 24) & (uint8_t)0xFF);
+ Addr[2] = ((tmpreg >> 16) & (uint8_t)0xFF);
+ Addr[1] = ((tmpreg >> 8 ) & (uint8_t)0xFF);
+ Addr[0] = (tmpreg & (uint8_t)0xFF);
+}
+
+
+/**
+ * @brief Enables or disables the Address filter module uses the specified
+ * ETHERNET MAC address for perfect filtering
+ * @param MacAddr: specifies the ETHERNET MAC address to be used for perfect filtering.
+ * This parameter can be one of the following values:
+ * @arg ETH_MAC_Address1 : MAC Address1
+ * @arg ETH_MAC_Address2 : MAC Address2
+ * @arg ETH_MAC_Address3 : MAC Address3
+ * @param NewState: new state of the specified ETHERNET MAC address use.
+ * This parameter can be: ENABLE or DISABLE.
+ * @retval None
+ */
+void ETH_MACAddressPerfectFilterCmd(uint32_t MacAddr, FunctionalState NewState)
+{
+ /* Check the parameters */
+ assert_param(IS_ETH_MAC_ADDRESS123(MacAddr));
+ assert_param(IS_FUNCTIONAL_STATE(NewState));
+
+ if (NewState != DISABLE)
+ {
+ /* Enable the selected ETHERNET MAC address for perfect filtering */
+ (*(__IO uint32_t *) (ETH_MAC_ADDR_HBASE + MacAddr)) |= ETH_MACA1HR_AE;
+ }
+ else
+ {
+ /* Disable the selected ETHERNET MAC address for perfect filtering */
+ (*(__IO uint32_t *) (ETH_MAC_ADDR_HBASE + MacAddr)) &=(~(uint32_t)ETH_MACA1HR_AE);
+ }
+}
+
+
+/**
+ * @brief Set the filter type for the specified ETHERNET MAC address
+ * @param MacAddr: specifies the ETHERNET MAC address
+ * This parameter can be one of the following values:
+ * @arg ETH_MAC_Address1 : MAC Address1
+ * @arg ETH_MAC_Address2 : MAC Address2
+ * @arg ETH_MAC_Address3 : MAC Address3
+ * @param Filter: specifies the used frame received field for comparison
+ * This parameter can be one of the following values:
+ * @arg ETH_MAC_AddressFilter_SA : MAC Address is used to compare with the
+ * SA fields of the received frame.
+ * @arg ETH_MAC_AddressFilter_DA : MAC Address is used to compare with the
+ * DA fields of the received frame.
+ * @retval None
+ */
+void ETH_MACAddressFilterConfig(uint32_t MacAddr, uint32_t Filter)
+{
+ /* Check the parameters */
+ assert_param(IS_ETH_MAC_ADDRESS123(MacAddr));
+ assert_param(IS_ETH_MAC_ADDRESS_FILTER(Filter));
+
+ if (Filter != ETH_MAC_AddressFilter_DA)
+ {
+ /* The selected ETHERNET MAC address is used to compare with the SA fields of the
+ received frame. */
+ (*(__IO uint32_t *) (ETH_MAC_ADDR_HBASE + MacAddr)) |= ETH_MACA1HR_SA;
+ }
+ else
+ {
+ /* The selected ETHERNET MAC address is used to compare with the DA fields of the
+ received frame. */
+ (*(__IO uint32_t *) (ETH_MAC_ADDR_HBASE + MacAddr)) &=(~(uint32_t)ETH_MACA1HR_SA);
+ }
+}
+
+
+/**
+ * @brief Set the filter type for the specified ETHERNET MAC address
+ * @param MacAddr: specifies the ETHERNET MAC address
+ * This parameter can be one of the following values:
+ * @arg ETH_MAC_Address1 : MAC Address1
+ * @arg ETH_MAC_Address2 : MAC Address2
+ * @arg ETH_MAC_Address3 : MAC Address3
+ * @param MaskByte: specifies the used address bytes for comparison
+ * This parameter can be any combination of the following values:
+ * @arg ETH_MAC_AddressMask_Byte6 : Mask MAC Address high reg bits [15:8].
+ * @arg ETH_MAC_AddressMask_Byte5 : Mask MAC Address high reg bits [7:0].
+ * @arg ETH_MAC_AddressMask_Byte4 : Mask MAC Address low reg bits [31:24].
+ * @arg ETH_MAC_AddressMask_Byte3 : Mask MAC Address low reg bits [23:16].
+ * @arg ETH_MAC_AddressMask_Byte2 : Mask MAC Address low reg bits [15:8].
+ * @arg ETH_MAC_AddressMask_Byte1 : Mask MAC Address low reg bits [7:0].
+ * @retval None
+ */
+void ETH_MACAddressMaskBytesFilterConfig(uint32_t MacAddr, uint32_t MaskByte)
+{
+ /* Check the parameters */
+ assert_param(IS_ETH_MAC_ADDRESS123(MacAddr));
+ assert_param(IS_ETH_MAC_ADDRESS_MASK(MaskByte));
+
+ /* Clear MBC bits in the selected MAC address high register */
+ (*(__IO uint32_t *) (ETH_MAC_ADDR_HBASE + MacAddr)) &=(~(uint32_t)ETH_MACA1HR_MBC);
+ /* Set the selected Filter mask bytes */
+ (*(__IO uint32_t *) (ETH_MAC_ADDR_HBASE + MacAddr)) |= MaskByte;
+}
+
+
+/******************************************************************************/
+/* DMA Descriptors functions */
+/******************************************************************************/
+
+/**
+ * @brief This function should be called to get the received frame (to be used
+ * with polling method only).
+ * @param none
+ * @retval Structure of type FrameTypeDef
+ */
+FrameTypeDef ETH_Get_Received_Frame(void)
+{
+ uint32_t framelength = 0;
+ FrameTypeDef frame = {0,0,0};
+
+ /* Get the Frame Length of the received packet: substruct 4 bytes of the CRC */
+ framelength = ((DMARxDescToGet->Status & ETH_DMARxDesc_FL) >> ETH_DMARxDesc_FrameLengthShift) - 4;
+ frame.length = framelength;
+
+ /* Get the address of the buffer start address */
+ /* Check if more than one segment in the frame */
+ if (DMA_RX_FRAME_infos->Seg_Count >1)
+ {
+ frame.buffer =(DMA_RX_FRAME_infos->FS_Rx_Desc)->Buffer1Addr;
+ }
+ else
+ {
+ frame.buffer = DMARxDescToGet->Buffer1Addr;
+ }
+
+ frame.descriptor = DMARxDescToGet;
+
+ /* Update the ETHERNET DMA global Rx descriptor with next Rx descriptor */
+ /* Chained Mode */
+ /* Selects the next DMA Rx descriptor list for next buffer to read */
+ DMARxDescToGet = (ETH_DMADESCTypeDef*) (DMARxDescToGet->Buffer2NextDescAddr);
+
+ /* Return Frame */
+ return (frame);
+}
+
+
+/**
+ * @brief This function should be called when a frame is received using DMA
+ * Receive interrupt, it allows scanning of Rx descriptors to get the
+ * the receive frame (should be used with interrupt mode only)
+ * @param None
+ * @retval Structure of type FrameTypeDef
+ */
+FrameTypeDef ETH_Get_Received_Frame_interrupt(void)
+{
+ FrameTypeDef frame={0,0,0};
+ __IO uint32_t descriptor_scan_counter = 0;
+
+ /* scan descriptors owned by CPU */
+ while (((DMARxDescToGet->Status & ETH_DMARxDesc_OWN) == (uint32_t)RESET)&&
+ (descriptor_scan_counterStatus & ETH_DMARxDesc_FS) != (uint32_t)RESET)&&
+ ((DMARxDescToGet->Status & ETH_DMARxDesc_LS) == (uint32_t)RESET))
+ {
+ DMA_RX_FRAME_infos->FS_Rx_Desc = DMARxDescToGet;
+ DMA_RX_FRAME_infos->Seg_Count = 1;
+ DMARxDescToGet = (ETH_DMADESCTypeDef*) (DMARxDescToGet->Buffer2NextDescAddr);
+ }
+
+ /* check if intermediate segment */
+ else if (((DMARxDescToGet->Status & ETH_DMARxDesc_LS) == (uint32_t)RESET)&&
+ ((DMARxDescToGet->Status & ETH_DMARxDesc_FS) == (uint32_t)RESET))
+ {
+ (DMA_RX_FRAME_infos->Seg_Count) ++;
+ DMARxDescToGet = (ETH_DMADESCTypeDef*) (DMARxDescToGet->Buffer2NextDescAddr);
+ }
+
+ /* should be last segment */
+ else
+ {
+ /* last segment */
+ DMA_RX_FRAME_infos->LS_Rx_Desc = DMARxDescToGet;
+
+ (DMA_RX_FRAME_infos->Seg_Count)++;
+
+ /* first segment is last segment */
+ if ((DMA_RX_FRAME_infos->Seg_Count)==1)
+ DMA_RX_FRAME_infos->FS_Rx_Desc = DMARxDescToGet;
+
+ /* Get the Frame Length of the received packet: substruct 4 bytes of the CRC */
+ frame.length = ((DMARxDescToGet->Status & ETH_DMARxDesc_FL) >> ETH_DMARxDesc_FrameLengthShift) - 4;
+
+
+ /* Get the address of the buffer start address */
+ /* Check if more than one segment in the frame */
+ if (DMA_RX_FRAME_infos->Seg_Count >1)
+ {
+ frame.buffer =(DMA_RX_FRAME_infos->FS_Rx_Desc)->Buffer1Addr;
+ }
+ else
+ {
+ frame.buffer = DMARxDescToGet->Buffer1Addr;
+ }
+
+ frame.descriptor = DMARxDescToGet;
+
+ /* Update the ETHERNET DMA global Rx descriptor with next Rx descriptor */
+ DMARxDescToGet = (ETH_DMADESCTypeDef*) (DMARxDescToGet->Buffer2NextDescAddr);
+
+ /* Return Frame */
+ return (frame);
+ }
+ }
+ return (frame);
+}
+
+
+/**
+ * @brief Prepares DMA Tx descriptors to transmit an ethernet frame
+ * @param FrameLength : length of the frame to send
+ * @retval error status
+ */
+uint32_t ETH_Prepare_Transmit_Descriptors(u16 FrameLength)
+{
+ uint32_t buf_count =0, size=0,i=0;
+ __IO ETH_DMADESCTypeDef *DMATxNextDesc;
+
+ /* Check if the descriptor is owned by the ETHERNET DMA (when set) or CPU (when reset) */
+ if((DMATxDescToSet->Status & ETH_DMATxDesc_OWN) != (u32)RESET)
+ {
+ /* Return ERROR: OWN bit set */
+ return ETH_ERROR;
+ }
+
+ DMATxNextDesc = DMATxDescToSet;
+
+ if (FrameLength > ETH_TX_BUF_SIZE)
+ {
+ buf_count = FrameLength/ETH_TX_BUF_SIZE;
+ if (FrameLength%ETH_TX_BUF_SIZE) buf_count++;
+ }
+ else buf_count =1;
+
+ if (buf_count ==1)
+ {
+ /*set LAST and FIRST segment */
+ DMATxDescToSet->Status |=ETH_DMATxDesc_FS|ETH_DMATxDesc_LS;
+ /* Set frame size */
+ DMATxDescToSet->ControlBufferSize = (FrameLength& ETH_DMATxDesc_TBS1);
+ /* Set Own bit of the Tx descriptor Status: gives the buffer back to ETHERNET DMA */
+ DMATxDescToSet->Status |= ETH_DMATxDesc_OWN;
+ DMATxDescToSet= (ETH_DMADESCTypeDef *)(DMATxDescToSet->Buffer2NextDescAddr);
+ }
+ else
+ {
+ for (i=0; i< buf_count; i++)
+ {
+ if (i==0)
+ {
+ /* Setting the first segment bit */
+ DMATxDescToSet->Status |= ETH_DMATxDesc_FS;
+ }
+
+ /* Program size */
+ DMATxNextDesc->ControlBufferSize = (ETH_TX_BUF_SIZE & ETH_DMATxDesc_TBS1);
+
+ if (i== (buf_count-1))
+ {
+ /* Setting the last segment bit */
+ DMATxNextDesc->Status |= ETH_DMATxDesc_LS;
+ size = FrameLength - (buf_count-1)*ETH_TX_BUF_SIZE;
+ DMATxNextDesc->ControlBufferSize = (size & ETH_DMATxDesc_TBS1);
+ }
+
+ /*give back descriptor to DMA */
+ DMATxNextDesc->Status |= ETH_DMATxDesc_OWN;
+
+ DMATxNextDesc = (ETH_DMADESCTypeDef *)(DMATxNextDesc->Buffer2NextDescAddr);
+ /* Set Own bit of the Tx descriptor Status: gives the buffer back to ETHERNET DMA */
+ }
+ DMATxDescToSet = DMATxNextDesc ;
+ }
+
+ /* When Tx Buffer unavailable flag is set: clear it and resume transmission */
+ if ((ETH->DMASR & ETH_DMASR_TBUS) != (u32)RESET)
+ {
+ /* Clear TBUS ETHERNET DMA flag */
+ ETH->DMASR = ETH_DMASR_TBUS;
+ /* Resume DMA transmission*/
+ ETH->DMATPDR = 0;
+ }
+
+ /* Return SUCCESS */
+ return ETH_SUCCESS;
+}
+
+
+/**
+ * @brief Initializes the DMA Rx descriptors in chain mode.
+ * @param DMARxDescTab: Pointer on the first Rx desc list
+ * @param RxBuff: Pointer on the first RxBuffer list
+ * @param RxBuffCount: Number of the used Rx desc in the list
+ * @retval None
+ */
+void ETH_DMARxDescChainInit(ETH_DMADESCTypeDef *DMARxDescTab, uint8_t *RxBuff, uint32_t RxBuffCount)
+{
+ uint32_t i = 0;
+ ETH_DMADESCTypeDef *DMARxDesc;
+
+ /* Set the DMARxDescToGet pointer with the first one of the DMARxDescTab list */
+ DMARxDescToGet = DMARxDescTab;
+ /* Fill each DMARxDesc descriptor with the right values */
+ for(i=0; i < RxBuffCount; i++)
+ {
+ /* Get the pointer on the ith member of the Rx Desc list */
+ DMARxDesc = DMARxDescTab+i;
+ /* Set Own bit of the Rx descriptor Status */
+ DMARxDesc->Status = ETH_DMARxDesc_OWN;
+
+ /* Set Buffer1 size and Second Address Chained bit */
+ DMARxDesc->ControlBufferSize = ETH_DMARxDesc_RCH | (uint32_t)ETH_RX_BUF_SIZE;
+ /* Set Buffer1 address pointer */
+ DMARxDesc->Buffer1Addr = (uint32_t)(&RxBuff[i*ETH_RX_BUF_SIZE]);
+
+ /* Initialize the next descriptor with the Next Descriptor Polling Enable */
+ if(i < (RxBuffCount-1))
+ {
+ /* Set next descriptor address register with next descriptor base address */
+ DMARxDesc->Buffer2NextDescAddr = (uint32_t)(DMARxDescTab+i+1);
+ }
+ else
+ {
+ /* For last descriptor, set next descriptor address register equal to the first descriptor base address */
+ DMARxDesc->Buffer2NextDescAddr = (uint32_t)(DMARxDescTab);
+ }
+ }
+
+ /* Set Receive Descriptor List Address Register */
+ ETH->DMARDLAR = (uint32_t) DMARxDescTab;
+
+
+ DMA_RX_FRAME_infos = &RX_Frame_Descriptor;
+
+}
+
+/**
+ * @brief This function polls for a frame reception
+ * @param None
+ * @retval Returns 1 when a frame is received, 0 if none.
+ */
+uint32_t ETH_CheckFrameReceived(void)
+{
+ /* check if last segment */
+ if(((DMARxDescToGet->Status & ETH_DMARxDesc_OWN) == (uint32_t)RESET) &&
+ ((DMARxDescToGet->Status & ETH_DMARxDesc_LS) != (uint32_t)RESET))
+ {
+ DMA_RX_FRAME_infos->LS_Rx_Desc = DMARxDescToGet;
+ DMA_RX_FRAME_infos->Seg_Count++;
+ return 1;
+ }
+
+ /* check if first segment */
+ else if(((DMARxDescToGet->Status & ETH_DMARxDesc_OWN) == (uint32_t)RESET) &&
+ ((DMARxDescToGet->Status & ETH_DMARxDesc_FS) != (uint32_t)RESET)&&
+ ((DMARxDescToGet->Status & ETH_DMARxDesc_LS) == (uint32_t)RESET))
+ {
+ DMA_RX_FRAME_infos->FS_Rx_Desc = DMARxDescToGet;
+ DMA_RX_FRAME_infos->LS_Rx_Desc = NULL;
+ DMA_RX_FRAME_infos->Seg_Count = 1;
+ DMARxDescToGet = (ETH_DMADESCTypeDef*) (DMARxDescToGet->Buffer2NextDescAddr);
+ }
+
+ /* check if intermediate segment */
+ else if(((DMARxDescToGet->Status & ETH_DMARxDesc_OWN) == (uint32_t)RESET) &&
+ ((DMARxDescToGet->Status & ETH_DMARxDesc_FS) == (uint32_t)RESET)&&
+ ((DMARxDescToGet->Status & ETH_DMARxDesc_LS) == (uint32_t)RESET))
+ {
+ (DMA_RX_FRAME_infos->Seg_Count) ++;
+ DMARxDescToGet = (ETH_DMADESCTypeDef*) (DMARxDescToGet->Buffer2NextDescAddr);
+ }
+ return 0;
+}
+
+
+
+/**
+ * @brief Initializes the DMA Tx descriptors in chain mode.
+ * @param DMATxDescTab: Pointer on the first Tx desc list
+ * @param TxBuff: Pointer on the first TxBuffer list
+ * @param TxBuffCount: Number of the used Tx desc in the list
+ * @retval None
+ */
+void ETH_DMATxDescChainInit(ETH_DMADESCTypeDef *DMATxDescTab, uint8_t* TxBuff, uint32_t TxBuffCount)
+{
+ uint32_t i = 0;
+ ETH_DMADESCTypeDef *DMATxDesc;
+
+ /* Set the DMATxDescToSet pointer with the first one of the DMATxDescTab list */
+ DMATxDescToSet = DMATxDescTab;
+ /* Fill each DMATxDesc descriptor with the right values */
+ for(i=0; i < TxBuffCount; i++)
+ {
+ /* Get the pointer on the ith member of the Tx Desc list */
+ DMATxDesc = DMATxDescTab + i;
+ /* Set Second Address Chained bit */
+ DMATxDesc->Status = ETH_DMATxDesc_TCH;
+
+ /* Set Buffer1 address pointer */
+ DMATxDesc->Buffer1Addr = (uint32_t)(&TxBuff[i*ETH_TX_BUF_SIZE]);
+
+ /* Initialize the next descriptor with the Next Descriptor Polling Enable */
+ if(i < (TxBuffCount-1))
+ {
+ /* Set next descriptor address register with next descriptor base address */
+ DMATxDesc->Buffer2NextDescAddr = (uint32_t)(DMATxDescTab+i+1);
+ }
+ else
+ {
+ /* For last descriptor, set next descriptor address register equal to the first descriptor base address */
+ DMATxDesc->Buffer2NextDescAddr = (uint32_t) DMATxDescTab;
+ }
+ }
+
+ /* Set Transmit Desciptor List Address Register */
+ ETH->DMATDLAR = (uint32_t) DMATxDescTab;
+}
+
+
+
+
+/**
+ * @brief Checks whether the specified ETHERNET DMA Tx Desc flag is set or not.
+ * @param DMATxDesc: pointer on a DMA Tx descriptor
+ * @param ETH_DMATxDescFlag: specifies the flag to check.
+ * This parameter can be one of the following values:
+ * @arg ETH_DMATxDesc_OWN : OWN bit: descriptor is owned by DMA engine
+ * @arg ETH_DMATxDesc_IC : Interrupt on completion
+ * @arg ETH_DMATxDesc_LS : Last Segment
+ * @arg ETH_DMATxDesc_FS : First Segment
+ * @arg ETH_DMATxDesc_DC : Disable CRC
+ * @arg ETH_DMATxDesc_DP : Disable Pad
+ * @arg ETH_DMATxDesc_TTSE: Transmit Time Stamp Enable
+ * @arg ETH_DMATxDesc_CIC : Checksum insertion control
+ * @arg ETH_DMATxDesc_TER : Transmit End of Ring
+ * @arg ETH_DMATxDesc_TCH : Second Address Chained
+ * @arg ETH_DMATxDesc_TTSS: Tx Time Stamp Status
+ * @arg ETH_DMATxDesc_IHE : IP Header Error
+ * @arg ETH_DMATxDesc_ES : Error summary
+ * @arg ETH_DMATxDesc_JT : Jabber Timeout
+ * @arg ETH_DMATxDesc_FF : Frame Flushed: DMA/MTL flushed the frame due to SW flush
+ * @arg ETH_DMATxDesc_PCE : Payload Checksum Error
+ * @arg ETH_DMATxDesc_LCA : Loss of Carrier: carrier lost during transmission
+ * @arg ETH_DMATxDesc_NC : No Carrier: no carrier signal from the transceiver
+ * @arg ETH_DMATxDesc_LCO : Late Collision: transmission aborted due to collision
+ * @arg ETH_DMATxDesc_EC : Excessive Collision: transmission aborted after 16 collisions
+ * @arg ETH_DMATxDesc_VF : VLAN Frame
+ * @arg ETH_DMATxDesc_CC : Collision Count
+ * @arg ETH_DMATxDesc_ED : Excessive Deferral
+ * @arg ETH_DMATxDesc_UF : Underflow Error: late data arrival from the memory
+ * @arg ETH_DMATxDesc_DB : Deferred Bit
+ * @retval The new state of ETH_DMATxDescFlag (SET or RESET).
+ */
+FlagStatus ETH_GetDMATxDescFlagStatus(ETH_DMADESCTypeDef *DMATxDesc, uint32_t ETH_DMATxDescFlag)
+{
+ FlagStatus bitstatus = RESET;
+ /* Check the parameters */
+ assert_param(IS_ETH_DMATxDESC_GET_FLAG(ETH_DMATxDescFlag));
+
+ if ((DMATxDesc->Status & ETH_DMATxDescFlag) != (uint32_t)RESET)
+ {
+ bitstatus = SET;
+ }
+ else
+ {
+ bitstatus = RESET;
+ }
+ return bitstatus;
+}
+
+/**
+ * @brief Returns the specified ETHERNET DMA Tx Desc collision count.
+ * @param DMATxDesc: pointer on a DMA Tx descriptor
+ * @retval The Transmit descriptor collision counter value.
+ */
+uint32_t ETH_GetDMATxDescCollisionCount(ETH_DMADESCTypeDef *DMATxDesc)
+{
+ /* Return the Receive descriptor frame length */
+ return ((DMATxDesc->Status & ETH_DMATxDesc_CC) >> ETH_DMATXDESC_COLLISION_COUNTSHIFT);
+}
+
+/**
+ * @brief Set the specified DMA Tx Desc Own bit.
+ * @param DMATxDesc: Pointer on a Tx desc
+ * @retval None
+ */
+void ETH_SetDMATxDescOwnBit(ETH_DMADESCTypeDef *DMATxDesc)
+{
+ /* Set the DMA Tx Desc Own bit */
+ DMATxDesc->Status |= ETH_DMATxDesc_OWN;
+}
+
+/**
+ * @brief Enables or disables the specified DMA Tx Desc Transmit interrupt.
+ * @param DMATxDesc: Pointer on a Tx desc
+ * @param NewState: new state of the DMA Tx Desc transmit interrupt.
+ * This parameter can be: ENABLE or DISABLE.
+ * @retval None
+ */
+void ETH_DMATxDescTransmitITConfig(ETH_DMADESCTypeDef *DMATxDesc, FunctionalState NewState)
+{
+ /* Check the parameters */
+ assert_param(IS_FUNCTIONAL_STATE(NewState));
+
+ if (NewState != DISABLE)
+ {
+ /* Enable the DMA Tx Desc Transmit interrupt */
+ DMATxDesc->Status |= ETH_DMATxDesc_IC;
+ }
+ else
+ {
+ /* Disable the DMA Tx Desc Transmit interrupt */
+ DMATxDesc->Status &=(~(uint32_t)ETH_DMATxDesc_IC);
+ }
+}
+
+/**
+ * @brief configure Tx descriptor as last or first segment
+ * @param DMATxDesc: Pointer on a Tx desc
+ * @param DMATxDesc_FrameSegment: specifies is the actual Tx desc contain last or first segment.
+ * This parameter can be one of the following values:
+ * @arg ETH_DMATxDesc_LastSegment : actual Tx desc contain last segment
+ * @arg ETH_DMATxDesc_FirstSegment : actual Tx desc contain first segment
+ * @retval None
+ */
+void ETH_DMATxDescFrameSegmentConfig(ETH_DMADESCTypeDef *DMATxDesc, uint32_t DMATxDesc_FrameSegment)
+{
+ /* Check the parameters */
+ assert_param(IS_ETH_DMA_TXDESC_SEGMENT(DMATxDesc_FrameSegment));
+
+ /* Selects the DMA Tx Desc Frame segment */
+ DMATxDesc->Status |= DMATxDesc_FrameSegment;
+}
+
+/**
+ * @brief Selects the specified ETHERNET DMA Tx Desc Checksum Insertion.
+ * @param DMATxDesc: pointer on a DMA Tx descriptor
+ * @param DMATxDesc_Checksum: specifies is the DMA Tx desc checksum insertion.
+ * This parameter can be one of the following values:
+ * @arg ETH_DMATxDesc_ChecksumByPass : Checksum bypass
+ * @arg ETH_DMATxDesc_ChecksumIPV4Header : IPv4 header checksum
+ * @arg ETH_DMATxDesc_ChecksumTCPUDPICMPSegment : TCP/UDP/ICMP checksum. Pseudo header checksum is assumed to be present
+ * @arg ETH_DMATxDesc_ChecksumTCPUDPICMPFull : TCP/UDP/ICMP checksum fully in hardware including pseudo header
+ * @retval None
+ */
+void ETH_DMATxDescChecksumInsertionConfig(ETH_DMADESCTypeDef *DMATxDesc, uint32_t DMATxDesc_Checksum)
+{
+ /* Check the parameters */
+ assert_param(IS_ETH_DMA_TXDESC_CHECKSUM(DMATxDesc_Checksum));
+
+ /* Set the selected DMA Tx desc checksum insertion control */
+ DMATxDesc->Status |= DMATxDesc_Checksum;
+}
+
+/**
+ * @brief Enables or disables the DMA Tx Desc CRC.
+ * @param DMATxDesc: pointer on a DMA Tx descriptor
+ * @param NewState: new state of the specified DMA Tx Desc CRC.
+ * This parameter can be: ENABLE or DISABLE.
+ * @retval None
+ */
+void ETH_DMATxDescCRCCmd(ETH_DMADESCTypeDef *DMATxDesc, FunctionalState NewState)
+{
+ /* Check the parameters */
+ assert_param(IS_FUNCTIONAL_STATE(NewState));
+
+ if (NewState != DISABLE)
+ {
+ /* Enable the selected DMA Tx Desc CRC */
+ DMATxDesc->Status &= (~(uint32_t)ETH_DMATxDesc_DC);
+ }
+ else
+ {
+ /* Disable the selected DMA Tx Desc CRC */
+ DMATxDesc->Status |= ETH_DMATxDesc_DC;
+ }
+}
+
+
+/**
+ * @brief Enables or disables the DMA Tx Desc second address chained.
+ * @param DMATxDesc: pointer on a DMA Tx descriptor
+ * @param NewState: new state of the specified DMA Tx Desc second address chained.
+ * This parameter can be: ENABLE or DISABLE.
+ * @retval None
+ */
+void ETH_DMATxDescSecondAddressChainedCmd(ETH_DMADESCTypeDef *DMATxDesc, FunctionalState NewState)
+{
+ /* Check the parameters */
+ assert_param(IS_FUNCTIONAL_STATE(NewState));
+
+ if (NewState != DISABLE)
+ {
+ /* Enable the selected DMA Tx Desc second address chained */
+ DMATxDesc->Status |= ETH_DMATxDesc_TCH;
+ }
+ else
+ {
+ /* Disable the selected DMA Tx Desc second address chained */
+ DMATxDesc->Status &=(~(uint32_t)ETH_DMATxDesc_TCH);
+ }
+}
+
+/**
+ * @brief Enables or disables the DMA Tx Desc padding for frame shorter than 64 bytes.
+ * @param DMATxDesc: pointer on a DMA Tx descriptor
+ * @param NewState: new state of the specified DMA Tx Desc padding for frame shorter than 64 bytes.
+ * This parameter can be: ENABLE or DISABLE.
+ * @retval None
+ */
+void ETH_DMATxDescShortFramePaddingCmd(ETH_DMADESCTypeDef *DMATxDesc, FunctionalState NewState)
+{
+ /* Check the parameters */
+ assert_param(IS_FUNCTIONAL_STATE(NewState));
+
+ if (NewState != DISABLE)
+ {
+ /* Enable the selected DMA Tx Desc padding for frame shorter than 64 bytes */
+ DMATxDesc->Status &= (~(uint32_t)ETH_DMATxDesc_DP);
+ }
+ else
+ {
+ /* Disable the selected DMA Tx Desc padding for frame shorter than 64 bytes*/
+ DMATxDesc->Status |= ETH_DMATxDesc_DP;
+ }
+}
+
+
+/**
+ * @brief Configures the specified DMA Tx Desc buffer1 and buffer2 sizes.
+ * @param DMATxDesc: Pointer on a Tx desc
+ * @param BufferSize1: specifies the Tx desc buffer1 size.
+ * @param BufferSize2: specifies the Tx desc buffer2 size (put "0" if not used).
+ * @retval None
+ */
+void ETH_DMATxDescBufferSizeConfig(ETH_DMADESCTypeDef *DMATxDesc, uint32_t BufferSize1, uint32_t BufferSize2)
+{
+ /* Check the parameters */
+ assert_param(IS_ETH_DMATxDESC_BUFFER_SIZE(BufferSize1));
+ assert_param(IS_ETH_DMATxDESC_BUFFER_SIZE(BufferSize2));
+
+ /* Set the DMA Tx Desc buffer1 and buffer2 sizes values */
+ DMATxDesc->ControlBufferSize |= (BufferSize1 | (BufferSize2 << ETH_DMATXDESC_BUFFER2_SIZESHIFT));
+}
+
+
+/**
+ * @brief Checks whether the specified ETHERNET Rx Desc flag is set or not.
+ * @param DMARxDesc: pointer on a DMA Rx descriptor
+ * @param ETH_DMARxDescFlag: specifies the flag to check.
+ * This parameter can be one of the following values:
+ * @arg ETH_DMARxDesc_OWN: OWN bit: descriptor is owned by DMA engine
+ * @arg ETH_DMARxDesc_AFM: DA Filter Fail for the rx frame
+ * @arg ETH_DMARxDesc_ES: Error summary
+ * @arg ETH_DMARxDesc_DE: Descriptor error: no more descriptors for receive frame
+ * @arg ETH_DMARxDesc_SAF: SA Filter Fail for the received frame
+ * @arg ETH_DMARxDesc_LE: Frame size not matching with length field
+ * @arg ETH_DMARxDesc_OE: Overflow Error: Frame was damaged due to buffer overflow
+ * @arg ETH_DMARxDesc_VLAN: VLAN Tag: received frame is a VLAN frame
+ * @arg ETH_DMARxDesc_FS: First descriptor of the frame
+ * @arg ETH_DMARxDesc_LS: Last descriptor of the frame
+ * @arg ETH_DMARxDesc_IPV4HCE: IPC Checksum Error/Giant Frame: Rx Ipv4 header checksum error
+ * @arg ETH_DMARxDesc_LC: Late collision occurred during reception
+ * @arg ETH_DMARxDesc_FT: Frame type - Ethernet, otherwise 802.3
+ * @arg ETH_DMARxDesc_RWT: Receive Watchdog Timeout: watchdog timer expired during reception
+ * @arg ETH_DMARxDesc_RE: Receive error: error reported by MII interface
+ * @arg ETH_DMARxDesc_DE: Dribble bit error: frame contains non int multiple of 8 bits
+ * @arg ETH_DMARxDesc_CE: CRC error
+ * @arg ETH_DMARxDesc_MAMPCE: Rx MAC Address/Payload Checksum Error: Rx MAC address matched/ Rx Payload Checksum Error
+ * @retval The new state of ETH_DMARxDescFlag (SET or RESET).
+ */
+FlagStatus ETH_GetDMARxDescFlagStatus(ETH_DMADESCTypeDef *DMARxDesc, uint32_t ETH_DMARxDescFlag)
+{
+ FlagStatus bitstatus = RESET;
+ /* Check the parameters */
+ assert_param(IS_ETH_DMARxDESC_GET_FLAG(ETH_DMARxDescFlag));
+ if ((DMARxDesc->Status & ETH_DMARxDescFlag) != (uint32_t)RESET)
+ {
+ bitstatus = SET;
+ }
+ else
+ {
+ bitstatus = RESET;
+ }
+ return bitstatus;
+}
+
+#ifdef USE_ENHANCED_DMA_DESCRIPTORS
+/**
+ * @brief Checks whether the specified ETHERNET PTP Rx Desc extended flag is set or not.
+ * @param DMAPTPRxDesc: pointer on a DMA PTP Rx descriptor
+ * @param ETH_DMAPTPRxDescFlag: specifies the extended flag to check.
+ * This parameter can be one of the following values:
+ * @arg ETH_DMAPTPRxDesc_PTPV: PTP version
+ * @arg ETH_DMAPTPRxDesc_PTPFT: PTP frame type
+ * @arg ETH_DMAPTPRxDesc_PTPMT: PTP message type
+ * @arg ETH_DMAPTPRxDesc_IPV6PR: IPv6 packet received
+ * @arg ETH_DMAPTPRxDesc_IPV4PR: IPv4 packet received
+ * @arg ETH_DMAPTPRxDesc_IPCB: IP checksum bypassed
+ * @arg ETH_DMAPTPRxDesc_IPPE: IP payload error
+ * @arg ETH_DMAPTPRxDesc_IPHE: IP header error
+ * @arg ETH_DMAPTPRxDesc_IPPT: IP payload type
+ * @retval The new state of ETH_DMAPTPRxDescExtendedFlag (SET or RESET).
+ */
+FlagStatus ETH_GetDMAPTPRxDescExtendedFlagStatus(ETH_DMADESCTypeDef *DMAPTPRxDesc, uint32_t ETH_DMAPTPRxDescExtendedFlag)
+{
+ FlagStatus bitstatus = RESET;
+
+ /* Check the parameters */
+ assert_param(IS_ETH_DMAPTPRxDESC_GET_EXTENDED_FLAG(ETH_DMAPTPRxDescExtendedFlag));
+
+ if ((DMAPTPRxDesc->ExtendedStatus & ETH_DMAPTPRxDescExtendedFlag) != (uint32_t)RESET)
+ {
+ bitstatus = SET;
+ }
+ else
+ {
+ bitstatus = RESET;
+ }
+ return bitstatus;
+}
+#endif /* USE_ENHANCED_DMA_DESCRIPTORS */
+
+/**
+ * @brief Set the specified DMA Rx Desc Own bit.
+ * @param DMARxDesc: Pointer on a Rx desc
+ * @retval None
+ */
+void ETH_SetDMARxDescOwnBit(ETH_DMADESCTypeDef *DMARxDesc)
+{
+ /* Set the DMA Rx Desc Own bit */
+ DMARxDesc->Status |= ETH_DMARxDesc_OWN;
+}
+
+/**
+ * @brief Returns the specified DMA Rx Desc frame length.
+ * @param DMARxDesc: pointer on a DMA Rx descriptor
+ * @retval The Rx descriptor received frame length.
+ */
+uint32_t ETH_GetDMARxDescFrameLength(ETH_DMADESCTypeDef *DMARxDesc)
+{
+ /* Return the Receive descriptor frame length */
+ return ((DMARxDesc->Status & ETH_DMARxDesc_FL) >> ETH_DMARXDESC_FRAME_LENGTHSHIFT);
+}
+
+/**
+ * @brief Enables or disables the specified DMA Rx Desc receive interrupt.
+ * @param DMARxDesc: Pointer on a Rx desc
+ * @param NewState: new state of the specified DMA Rx Desc interrupt.
+ * This parameter can be: ENABLE or DISABLE.
+ * @retval None
+ */
+void ETH_DMARxDescReceiveITConfig(ETH_DMADESCTypeDef *DMARxDesc, FunctionalState NewState)
+{
+ /* Check the parameters */
+ assert_param(IS_FUNCTIONAL_STATE(NewState));
+
+ if (NewState != DISABLE)
+ {
+ /* Enable the DMA Rx Desc receive interrupt */
+ DMARxDesc->ControlBufferSize &=(~(uint32_t)ETH_DMARxDesc_DIC);
+ }
+ else
+ {
+ /* Disable the DMA Rx Desc receive interrupt */
+ DMARxDesc->ControlBufferSize |= ETH_DMARxDesc_DIC;
+ }
+}
+
+
+/**
+ * @brief Returns the specified ETHERNET DMA Rx Desc buffer size.
+ * @param DMARxDesc: pointer on a DMA Rx descriptor
+ * @param DMARxDesc_Buffer: specifies the DMA Rx Desc buffer.
+ * This parameter can be any one of the following values:
+ * @arg ETH_DMARxDesc_Buffer1 : DMA Rx Desc Buffer1
+ * @arg ETH_DMARxDesc_Buffer2 : DMA Rx Desc Buffer2
+ * @retval The Receive descriptor frame length.
+ */
+uint32_t ETH_GetDMARxDescBufferSize(ETH_DMADESCTypeDef *DMARxDesc, uint32_t DMARxDesc_Buffer)
+{
+ /* Check the parameters */
+ assert_param(IS_ETH_DMA_RXDESC_BUFFER(DMARxDesc_Buffer));
+
+ if(DMARxDesc_Buffer != ETH_DMARxDesc_Buffer1)
+ {
+ /* Return the DMA Rx Desc buffer2 size */
+ return ((DMARxDesc->ControlBufferSize & ETH_DMARxDesc_RBS2) >> ETH_DMARXDESC_BUFFER2_SIZESHIFT);
+ }
+ else
+ {
+ /* Return the DMA Rx Desc buffer1 size */
+ return (DMARxDesc->ControlBufferSize & ETH_DMARxDesc_RBS1);
+ }
+}
+
+
+/**
+ * @brief Get the size of the received packet.
+ * @param None
+ * @retval framelength: received packet size
+ */
+uint32_t ETH_GetRxPktSize(ETH_DMADESCTypeDef *DMARxDesc)
+{
+ uint32_t frameLength = 0;
+ if(((DMARxDesc->Status & ETH_DMARxDesc_OWN) == (uint32_t)RESET) &&
+ ((DMARxDesc->Status & ETH_DMARxDesc_ES) == (uint32_t)RESET) &&
+ ((DMARxDesc->Status & ETH_DMARxDesc_LS) != (uint32_t)RESET))
+ {
+ /* Get the size of the packet: including 4 bytes of the CRC */
+ frameLength = ETH_GetDMARxDescFrameLength(DMARxDesc);
+ }
+
+ /* Return Frame Length */
+ return frameLength;
+}
+
+#ifdef USE_ENHANCED_DMA_DESCRIPTORS
+/**
+ * @brief Enables or disables the Enhanced descriptor structure.
+ * @param NewState: new state of the Enhanced descriptor structure.
+ * This parameter can be: ENABLE or DISABLE.
+ * @retval None
+ */
+void ETH_EnhancedDescriptorCmd(FunctionalState NewState)
+{
+ /* Check the parameters */
+ assert_param(IS_FUNCTIONAL_STATE(NewState));
+
+ if (NewState != DISABLE)
+ {
+ /* Enable enhanced descriptor structure */
+ ETH->DMABMR |= ETH_DMABMR_EDE;
+ }
+ else
+ {
+ /* Disable enhanced descriptor structure */
+ ETH->DMABMR &= ~ETH_DMABMR_EDE;
+ }
+}
+#endif /* USE_ENHANCED_DMA_DESCRIPTORS */
+
+/******************************************************************************/
+/* DMA functions */
+/******************************************************************************/
+/**
+ * @brief Resets all MAC subsystem internal registers and logic.
+ * @param None
+ * @retval None
+ */
+void ETH_SoftwareReset(void)
+{
+ /* Set the SWR bit: resets all MAC subsystem internal registers and logic */
+ /* After reset all the registers holds their respective reset values */
+ ETH->DMABMR |= ETH_DMABMR_SR;
+}
+
+/**
+ * @brief Checks whether the ETHERNET software reset bit is set or not.
+ * @param None
+ * @retval The new state of DMA Bus Mode register SR bit (SET or RESET).
+ */
+FlagStatus ETH_GetSoftwareResetStatus(void)
+{
+ FlagStatus bitstatus = RESET;
+ if((ETH->DMABMR & ETH_DMABMR_SR) != (uint32_t)RESET)
+ {
+ bitstatus = SET;
+ }
+ else
+ {
+ bitstatus = RESET;
+ }
+ return bitstatus;
+}
+
+/**
+ * @brief Checks whether the specified ETHERNET DMA flag is set or not.
+ * @param ETH_DMA_FLAG: specifies the flag to check.
+ * This parameter can be one of the following values:
+ * @arg ETH_DMA_FLAG_TST : Time-stamp trigger flag
+ * @arg ETH_DMA_FLAG_PMT : PMT flag
+ * @arg ETH_DMA_FLAG_MMC : MMC flag
+ * @arg ETH_DMA_FLAG_DataTransferError : Error bits 0-data buffer, 1-desc. access
+ * @arg ETH_DMA_FLAG_ReadWriteError : Error bits 0-write trnsf, 1-read transfr
+ * @arg ETH_DMA_FLAG_AccessError : Error bits 0-Rx DMA, 1-Tx DMA
+ * @arg ETH_DMA_FLAG_NIS : Normal interrupt summary flag
+ * @arg ETH_DMA_FLAG_AIS : Abnormal interrupt summary flag
+ * @arg ETH_DMA_FLAG_ER : Early receive flag
+ * @arg ETH_DMA_FLAG_FBE : Fatal bus error flag
+ * @arg ETH_DMA_FLAG_ET : Early transmit flag
+ * @arg ETH_DMA_FLAG_RWT : Receive watchdog timeout flag
+ * @arg ETH_DMA_FLAG_RPS : Receive process stopped flag
+ * @arg ETH_DMA_FLAG_RBU : Receive buffer unavailable flag
+ * @arg ETH_DMA_FLAG_R : Receive flag
+ * @arg ETH_DMA_FLAG_TU : Underflow flag
+ * @arg ETH_DMA_FLAG_RO : Overflow flag
+ * @arg ETH_DMA_FLAG_TJT : Transmit jabber timeout flag
+ * @arg ETH_DMA_FLAG_TBU : Transmit buffer unavailable flag
+ * @arg ETH_DMA_FLAG_TPS : Transmit process stopped flag
+ * @arg ETH_DMA_FLAG_T : Transmit flag
+ * @retval The new state of ETH_DMA_FLAG (SET or RESET).
+ */
+FlagStatus ETH_GetDMAFlagStatus(uint32_t ETH_DMA_FLAG)
+{
+ FlagStatus bitstatus = RESET;
+ /* Check the parameters */
+ assert_param(IS_ETH_DMA_GET_IT(ETH_DMA_FLAG));
+ if ((ETH->DMASR & ETH_DMA_FLAG) != (uint32_t)RESET)
+ {
+ bitstatus = SET;
+ }
+ else
+ {
+ bitstatus = RESET;
+ }
+ return bitstatus;
+}
+
+/**
+ * @brief Clears the ETHERNET�s DMA pending flag.
+ * @param ETH_DMA_FLAG: specifies the flag to clear.
+ * This parameter can be any combination of the following values:
+ * @arg ETH_DMA_FLAG_NIS : Normal interrupt summary flag
+ * @arg ETH_DMA_FLAG_AIS : Abnormal interrupt summary flag
+ * @arg ETH_DMA_FLAG_ER : Early receive flag
+ * @arg ETH_DMA_FLAG_FBE : Fatal bus error flag
+ * @arg ETH_DMA_FLAG_ETI : Early transmit flag
+ * @arg ETH_DMA_FLAG_RWT : Receive watchdog timeout flag
+ * @arg ETH_DMA_FLAG_RPS : Receive process stopped flag
+ * @arg ETH_DMA_FLAG_RBU : Receive buffer unavailable flag
+ * @arg ETH_DMA_FLAG_R : Receive flag
+ * @arg ETH_DMA_FLAG_TU : Transmit Underflow flag
+ * @arg ETH_DMA_FLAG_RO : Receive Overflow flag
+ * @arg ETH_DMA_FLAG_TJT : Transmit jabber timeout flag
+ * @arg ETH_DMA_FLAG_TBU : Transmit buffer unavailable flag
+ * @arg ETH_DMA_FLAG_TPS : Transmit process stopped flag
+ * @arg ETH_DMA_FLAG_T : Transmit flag
+ * @retval None
+ */
+void ETH_DMAClearFlag(uint32_t ETH_DMA_FLAG)
+{
+ /* Check the parameters */
+ assert_param(IS_ETH_DMA_FLAG(ETH_DMA_FLAG));
+
+ /* Clear the selected ETHERNET DMA FLAG */
+ ETH->DMASR = (uint32_t) ETH_DMA_FLAG;
+}
+
+/**
+ * @brief Enables or disables the specified ETHERNET DMA interrupts.
+ * @param ETH_DMA_IT: specifies the ETHERNET DMA interrupt sources to be
+ * enabled or disabled.
+ * This parameter can be any combination of the following values:
+ * @arg ETH_DMA_IT_NIS : Normal interrupt summary
+ * @arg ETH_DMA_IT_AIS : Abnormal interrupt summary
+ * @arg ETH_DMA_IT_ER : Early receive interrupt
+ * @arg ETH_DMA_IT_FBE : Fatal bus error interrupt
+ * @arg ETH_DMA_IT_ET : Early transmit interrupt
+ * @arg ETH_DMA_IT_RWT : Receive watchdog timeout interrupt
+ * @arg ETH_DMA_IT_RPS : Receive process stopped interrupt
+ * @arg ETH_DMA_IT_RBU : Receive buffer unavailable interrupt
+ * @arg ETH_DMA_IT_R : Receive interrupt
+ * @arg ETH_DMA_IT_TU : Underflow interrupt
+ * @arg ETH_DMA_IT_RO : Overflow interrupt
+ * @arg ETH_DMA_IT_TJT : Transmit jabber timeout interrupt
+ * @arg ETH_DMA_IT_TBU : Transmit buffer unavailable interrupt
+ * @arg ETH_DMA_IT_TPS : Transmit process stopped interrupt
+ * @arg ETH_DMA_IT_T : Transmit interrupt
+ * @param NewState: new state of the specified ETHERNET DMA interrupts.
+ * This parameter can be: ENABLE or DISABLE.
+ * @retval None
+ */
+void ETH_DMAITConfig(uint32_t ETH_DMA_IT, FunctionalState NewState)
+{
+ /* Check the parameters */
+ assert_param(IS_ETH_DMA_IT(ETH_DMA_IT));
+ assert_param(IS_FUNCTIONAL_STATE(NewState));
+
+ if (NewState != DISABLE)
+ {
+ /* Enable the selected ETHERNET DMA interrupts */
+ ETH->DMAIER |= ETH_DMA_IT;
+ }
+ else
+ {
+ /* Disable the selected ETHERNET DMA interrupts */
+ ETH->DMAIER &=(~(uint32_t)ETH_DMA_IT);
+ }
+}
+
+/**
+ * @brief Checks whether the specified ETHERNET DMA interrupt has occurred or not.
+ * @param ETH_DMA_IT: specifies the interrupt source to check.
+ * This parameter can be one of the following values:
+ * @arg ETH_DMA_IT_TST : Time-stamp trigger interrupt
+ * @arg ETH_DMA_IT_PMT : PMT interrupt
+ * @arg ETH_DMA_IT_MMC : MMC interrupt
+ * @arg ETH_DMA_IT_NIS : Normal interrupt summary
+ * @arg ETH_DMA_IT_AIS : Abnormal interrupt summary
+ * @arg ETH_DMA_IT_ER : Early receive interrupt
+ * @arg ETH_DMA_IT_FBE : Fatal bus error interrupt
+ * @arg ETH_DMA_IT_ET : Early transmit interrupt
+ * @arg ETH_DMA_IT_RWT : Receive watchdog timeout interrupt
+ * @arg ETH_DMA_IT_RPS : Receive process stopped interrupt
+ * @arg ETH_DMA_IT_RBU : Receive buffer unavailable interrupt
+ * @arg ETH_DMA_IT_R : Receive interrupt
+ * @arg ETH_DMA_IT_TU : Underflow interrupt
+ * @arg ETH_DMA_IT_RO : Overflow interrupt
+ * @arg ETH_DMA_IT_TJT : Transmit jabber timeout interrupt
+ * @arg ETH_DMA_IT_TBU : Transmit buffer unavailable interrupt
+ * @arg ETH_DMA_IT_TPS : Transmit process stopped interrupt
+ * @arg ETH_DMA_IT_T : Transmit interrupt
+ * @retval The new state of ETH_DMA_IT (SET or RESET).
+ */
+ITStatus ETH_GetDMAITStatus(uint32_t ETH_DMA_IT)
+{
+ ITStatus bitstatus = RESET;
+ /* Check the parameters */
+ assert_param(IS_ETH_DMA_GET_IT(ETH_DMA_IT));
+ if ((ETH->DMASR & ETH_DMA_IT) != (uint32_t)RESET)
+ {
+ bitstatus = SET;
+ }
+ else
+ {
+ bitstatus = RESET;
+ }
+ return bitstatus;
+}
+
+/**
+ * @brief Clears the ETHERNET�s DMA IT pending bit.
+ * @param ETH_DMA_IT: specifies the interrupt pending bit to clear.
+ * This parameter can be any combination of the following values:
+ * @arg ETH_DMA_IT_NIS : Normal interrupt summary
+ * @arg ETH_DMA_IT_AIS : Abnormal interrupt summary
+ * @arg ETH_DMA_IT_ER : Early receive interrupt
+ * @arg ETH_DMA_IT_FBE : Fatal bus error interrupt
+ * @arg ETH_DMA_IT_ETI : Early transmit interrupt
+ * @arg ETH_DMA_IT_RWT : Receive watchdog timeout interrupt
+ * @arg ETH_DMA_IT_RPS : Receive process stopped interrupt
+ * @arg ETH_DMA_IT_RBU : Receive buffer unavailable interrupt
+ * @arg ETH_DMA_IT_R : Receive interrupt
+ * @arg ETH_DMA_IT_TU : Transmit Underflow interrupt
+ * @arg ETH_DMA_IT_RO : Receive Overflow interrupt
+ * @arg ETH_DMA_IT_TJT : Transmit jabber timeout interrupt
+ * @arg ETH_DMA_IT_TBU : Transmit buffer unavailable interrupt
+ * @arg ETH_DMA_IT_TPS : Transmit process stopped interrupt
+ * @arg ETH_DMA_IT_T : Transmit interrupt
+ * @retval None
+ */
+void ETH_DMAClearITPendingBit(uint32_t ETH_DMA_IT)
+{
+ /* Check the parameters */
+ assert_param(IS_ETH_DMA_IT(ETH_DMA_IT));
+
+ /* Clear the selected ETHERNET DMA IT */
+ ETH->DMASR = (uint32_t) ETH_DMA_IT;
+}
+
+/**
+ * @brief Returns the ETHERNET DMA Transmit Process State.
+ * @param None
+ * @retval The new ETHERNET DMA Transmit Process State:
+ * This can be one of the following values:
+ * - ETH_DMA_TransmitProcess_Stopped : Stopped - Reset or Stop Tx Command issued
+ * - ETH_DMA_TransmitProcess_Fetching : Running - fetching the Tx descriptor
+ * - ETH_DMA_TransmitProcess_Waiting : Running - waiting for status
+ * - ETH_DMA_TransmitProcess_Reading : Running - reading the data from host memory
+ * - ETH_DMA_TransmitProcess_Suspended : Suspended - Tx Descriptor unavailable
+ * - ETH_DMA_TransmitProcess_Closing : Running - closing Rx descriptor
+ */
+uint32_t ETH_GetTransmitProcessState(void)
+{
+ return ((uint32_t)(ETH->DMASR & ETH_DMASR_TS));
+}
+
+/**
+ * @brief Returns the ETHERNET DMA Receive Process State.
+ * @param None
+ * @retval The new ETHERNET DMA Receive Process State:
+ * This can be one of the following values:
+ * - ETH_DMA_ReceiveProcess_Stopped : Stopped - Reset or Stop Rx Command issued
+ * - ETH_DMA_ReceiveProcess_Fetching : Running - fetching the Rx descriptor
+ * - ETH_DMA_ReceiveProcess_Waiting : Running - waiting for packet
+ * - ETH_DMA_ReceiveProcess_Suspended : Suspended - Rx Descriptor unavailable
+ * - ETH_DMA_ReceiveProcess_Closing : Running - closing descriptor
+ * - ETH_DMA_ReceiveProcess_Queuing : Running - queuing the receive frame into host memory
+ */
+uint32_t ETH_GetReceiveProcessState(void)
+{
+ return ((uint32_t)(ETH->DMASR & ETH_DMASR_RS));
+}
+
+/**
+ * @brief Clears the ETHERNET transmit FIFO.
+ * @param None
+ * @retval None
+ */
+void ETH_FlushTransmitFIFO(void)
+{
+ /* Set the Flush Transmit FIFO bit */
+ ETH->DMAOMR |= ETH_DMAOMR_FTF;
+}
+
+/**
+ * @brief Checks whether the ETHERNET flush transmit FIFO bit is cleared or not.
+ * @param None
+ * @retval The new state of ETHERNET flush transmit FIFO bit (SET or RESET).
+ */
+FlagStatus ETH_GetFlushTransmitFIFOStatus(void)
+{
+ FlagStatus bitstatus = RESET;
+ if ((ETH->DMAOMR & ETH_DMAOMR_FTF) != (uint32_t)RESET)
+ {
+ bitstatus = SET;
+ }
+ else
+ {
+ bitstatus = RESET;
+ }
+ return bitstatus;
+}
+
+/**
+ * @brief Enables or disables the DMA transmission.
+ * @param NewState: new state of the DMA transmission.
+ * This parameter can be: ENABLE or DISABLE.
+ * @retval None
+ */
+void ETH_DMATransmissionCmd(FunctionalState NewState)
+{
+ /* Check the parameters */
+ assert_param(IS_FUNCTIONAL_STATE(NewState));
+
+ if (NewState != DISABLE)
+ {
+ /* Enable the DMA transmission */
+ ETH->DMAOMR |= ETH_DMAOMR_ST;
+ }
+ else
+ {
+ /* Disable the DMA transmission */
+ ETH->DMAOMR &= ~ETH_DMAOMR_ST;
+ }
+}
+
+/**
+ * @brief Enables or disables the DMA reception.
+ * @param NewState: new state of the DMA reception.
+ * This parameter can be: ENABLE or DISABLE.
+ * @retval None
+ */
+void ETH_DMAReceptionCmd(FunctionalState NewState)
+{
+ /* Check the parameters */
+ assert_param(IS_FUNCTIONAL_STATE(NewState));
+
+ if (NewState != DISABLE)
+ {
+ /* Enable the DMA reception */
+ ETH->DMAOMR |= ETH_DMAOMR_SR;
+ }
+ else
+ {
+ /* Disable the DMA reception */
+ ETH->DMAOMR &= ~ETH_DMAOMR_SR;
+ }
+}
+
+/**
+ * @brief Checks whether the specified ETHERNET DMA overflow flag is set or not.
+ * @param ETH_DMA_Overflow: specifies the DMA overflow flag to check.
+ * This parameter can be one of the following values:
+ * @arg ETH_DMA_Overflow_RxFIFOCounter : Overflow for FIFO Overflows Counter
+ * @arg ETH_DMA_Overflow_MissedFrameCounter : Overflow for Buffer Unavailable Missed Frame Counter
+ * @retval The new state of ETHERNET DMA overflow Flag (SET or RESET).
+ */
+FlagStatus ETH_GetDMAOverflowStatus(uint32_t ETH_DMA_Overflow)
+{
+ FlagStatus bitstatus = RESET;
+ /* Check the parameters */
+ assert_param(IS_ETH_DMA_GET_OVERFLOW(ETH_DMA_Overflow));
+
+ if ((ETH->DMAMFBOCR & ETH_DMA_Overflow) != (uint32_t)RESET)
+ {
+ bitstatus = SET;
+ }
+ else
+ {
+ bitstatus = RESET;
+ }
+ return bitstatus;
+}
+
+/**
+ * @brief Get the ETHERNET DMA Rx Overflow Missed Frame Counter value.
+ * @param None
+ * @retval The value of Rx overflow Missed Frame Counter.
+ */
+uint32_t ETH_GetRxOverflowMissedFrameCounter(void)
+{
+ return ((uint32_t)((ETH->DMAMFBOCR & ETH_DMAMFBOCR_MFA)>>ETH_DMA_RX_OVERFLOW_MISSEDFRAMES_COUNTERSHIFT));
+}
+
+/**
+ * @brief Get the ETHERNET DMA Buffer Unavailable Missed Frame Counter value.
+ * @param None
+ * @retval The value of Buffer unavailable Missed Frame Counter.
+ */
+uint32_t ETH_GetBufferUnavailableMissedFrameCounter(void)
+{
+ return ((uint32_t)(ETH->DMAMFBOCR) & ETH_DMAMFBOCR_MFC);
+}
+
+/**
+ * @brief Get the ETHERNET DMA DMACHTDR register value.
+ * @param None
+ * @retval The value of the current Tx desc start address.
+ */
+uint32_t ETH_GetCurrentTxDescStartAddress(void)
+{
+ return ((uint32_t)(ETH->DMACHTDR));
+}
+
+/**
+ * @brief Get the ETHERNET DMA DMACHRDR register value.
+ * @param None
+ * @retval The value of the current Rx desc start address.
+ */
+uint32_t ETH_GetCurrentRxDescStartAddress(void)
+{
+ return ((uint32_t)(ETH->DMACHRDR));
+}
+
+/**
+ * @brief Get the ETHERNET DMA DMACHTBAR register value.
+ * @param None
+ * @retval The value of the current transmit descriptor data buffer address.
+ */
+uint32_t ETH_GetCurrentTxBufferAddress(void)
+{
+ return ((uint32_t)(ETH->DMACHTBAR));
+}
+
+/**
+ * @brief Get the ETHERNET DMA DMACHRBAR register value.
+ * @param None
+ * @retval The value of the current receive descriptor data buffer address.
+ */
+uint32_t ETH_GetCurrentRxBufferAddress(void)
+{
+ return ((uint32_t)(ETH->DMACHRBAR));
+}
+
+/**
+ * @brief Resumes the DMA Transmission by writing to the DmaTxPollDemand register
+ * (the data written could be anything). This forces the DMA to resume transmission.
+ * @param None
+ * @retval None.
+ */
+void ETH_ResumeDMATransmission(void)
+{
+ ETH->DMATPDR = 0;
+}
+
+/**
+ * @brief Resumes the DMA Transmission by writing to the DmaRxPollDemand register
+ * (the data written could be anything). This forces the DMA to resume reception.
+ * @param None
+ * @retval None.
+ */
+void ETH_ResumeDMAReception(void)
+{
+ ETH->DMARPDR = 0;
+}
+
+/**
+ * @brief Set the DMA Receive status watchdog timer register value
+ * @param Value: DMA Receive status watchdog timer register value
+ * @retval None
+ */
+void ETH_SetReceiveWatchdogTimer(uint8_t Value)
+{
+ /* Set the DMA Receive status watchdog timer register */
+ ETH->DMARSWTR = Value;
+}
+
+/******************************************************************************/
+/* PHY functions */
+/******************************************************************************/
+
+/**
+ * @brief Read a PHY register
+ * @param PHYAddress: PHY device address, is the index of one of supported 32 PHY devices.
+ * This parameter can be one of the following values: 0,..,31
+ * @param PHYReg: PHY register address, is the index of one of the 32 PHY register.
+ * This parameter can be one of the following values:
+ * @arg PHY_BCR: Transceiver Basic Control Register
+ * @arg PHY_BSR: Transceiver Basic Status Register
+ * @arg PHY_SR : Transceiver Status Register
+ * @arg More PHY register could be read depending on the used PHY
+ * @retval ETH_ERROR: in case of timeout
+ * MAC MIIDR register value: Data read from the selected PHY register (correct read )
+ */
+uint16_t ETH_ReadPHYRegister(uint16_t PHYAddress, uint16_t PHYReg)
+{
+ uint32_t tmpreg = 0;
+__IO uint32_t timeout = 0;
+ /* Check the parameters */
+ assert_param(IS_ETH_PHY_ADDRESS(PHYAddress));
+ // assert_param(IS_ETH_PHY_REG(PHYReg));
+ /* Get the ETHERNET MACMIIAR value */
+ tmpreg = ETH->MACMIIAR;
+ /* Keep only the CSR Clock Range CR[2:0] bits value */
+ tmpreg &= ~MACMIIAR_CR_MASK;
+ /* Prepare the MII address register value */
+ tmpreg |=(((uint32_t)PHYAddress<<11) & ETH_MACMIIAR_PA); /* Set the PHY device address */
+ tmpreg |=(((uint32_t)PHYReg<<6) & ETH_MACMIIAR_MR); /* Set the PHY register address */
+ tmpreg &= ~ETH_MACMIIAR_MW; /* Set the read mode */
+ tmpreg |= ETH_MACMIIAR_MB; /* Set the MII Busy bit */
+ /* Write the result value into the MII Address register */
+
+ ETH->MACMIIAR = tmpreg;
+ /* Check for the Busy flag */
+ do
+ {
+ timeout++;
+ tmpreg = ETH->MACMIIAR;
+
+ } while ((tmpreg & ETH_MACMIIAR_MB) && (timeout < (uint32_t)PHY_READ_TO));
+ /* Return ERROR in case of timeout */
+ if(timeout == PHY_READ_TO)
+ {
+ return (uint16_t)ETH_ERROR;
+ }
+
+ /* Return data register value */
+ return (uint16_t)(ETH->MACMIIDR);
+}
+
+/**
+ * @brief Write to a PHY register
+ * @param PHYAddress: PHY device address, is the index of one of supported 32 PHY devices.
+ * This parameter can be one of the following values: 0,..,31
+ * @param PHYReg: PHY register address, is the index of one of the 32 PHY register.
+ * This parameter can be one of the following values:
+ * @arg PHY_BCR : Transceiver Control Register
+ * @arg More PHY register could be written depending on the used PHY
+ * @param PHYValue: the value to write
+ * @retval ETH_ERROR: in case of timeout
+ * ETH_SUCCESS: for correct write
+ */
+uint32_t ETH_WritePHYRegister(uint16_t PHYAddress, uint16_t PHYReg, uint16_t PHYValue)
+{
+ uint32_t tmpreg = 0;
+ __IO uint32_t timeout = 0;
+ /* Check the parameters */
+ assert_param(IS_ETH_PHY_ADDRESS(PHYAddress));
+ // assert_param(IS_ETH_PHY_REG(PHYReg));
+
+ /* Get the ETHERNET MACMIIAR value */
+ tmpreg = ETH->MACMIIAR;
+ /* Keep only the CSR Clock Range CR[2:0] bits value */
+ tmpreg &= ~MACMIIAR_CR_MASK;
+ /* Prepare the MII register address value */
+ tmpreg |=(((uint32_t)PHYAddress<<11) & ETH_MACMIIAR_PA); /* Set the PHY device address */
+ tmpreg |=(((uint32_t)PHYReg<<6) & ETH_MACMIIAR_MR); /* Set the PHY register address */
+ tmpreg |= ETH_MACMIIAR_MW; /* Set the write mode */
+ tmpreg |= ETH_MACMIIAR_MB; /* Set the MII Busy bit */
+ /* Give the value to the MII data register */
+ ETH->MACMIIDR = PHYValue;
+ /* Write the result value into the MII Address register */
+ ETH->MACMIIAR = tmpreg;
+ /* Check for the Busy flag */
+ do
+ {
+ timeout++;
+ tmpreg = ETH->MACMIIAR;
+ } while ((tmpreg & ETH_MACMIIAR_MB) && (timeout < (uint32_t)PHY_WRITE_TO));
+ /* Return ERROR in case of timeout */
+ if(timeout == PHY_WRITE_TO)
+ {
+ return ETH_ERROR;
+ }
+
+ /* Return SUCCESS */
+ return ETH_SUCCESS;
+}
+
+/**
+ * @brief Enables or disables the PHY loopBack mode.
+ * @Note: Don't be confused with ETH_MACLoopBackCmd function which enables internal
+ * loopback at MII level
+ * @param PHYAddress: PHY device address, is the index of one of supported 32 PHY devices.
+ * @param NewState: new state of the PHY loopBack mode.
+ * This parameter can be: ENABLE or DISABLE.
+ * @retval ETH_ERROR: in case of bad PHY configuration
+ * ETH_SUCCESS: for correct PHY configuration
+ */
+uint32_t ETH_PHYLoopBackCmd(uint16_t PHYAddress, FunctionalState NewState)
+{
+ uint16_t tmpreg = 0;
+ /* Check the parameters */
+ assert_param(IS_ETH_PHY_ADDRESS(PHYAddress));
+ assert_param(IS_FUNCTIONAL_STATE(NewState));
+
+ /* Get the PHY configuration to update it */
+ tmpreg = ETH_ReadPHYRegister(PHYAddress, PHY_BCR);
+
+ if (NewState != DISABLE)
+ {
+ /* Enable the PHY loopback mode */
+ tmpreg |= PHY_Loopback;
+ }
+ else
+ {
+ /* Disable the PHY loopback mode: normal mode */
+ tmpreg &= (uint16_t)(~(uint16_t)PHY_Loopback);
+ }
+ /* Update the PHY control register with the new configuration */
+ if(ETH_WritePHYRegister(PHYAddress, PHY_BCR, tmpreg) != (uint32_t)RESET)
+ {
+ return ETH_SUCCESS;
+ }
+ else
+ {
+ /* Return SUCCESS */
+ return ETH_ERROR;
+ }
+}
+
+/******************************************************************************/
+/* Power Management(PMT) functions */
+/******************************************************************************/
+/**
+ * @brief Reset Wakeup frame filter register pointer.
+ * @param None
+ * @retval None
+ */
+void ETH_ResetWakeUpFrameFilterRegisterPointer(void)
+{
+ /* Resets the Remote Wake-up Frame Filter register pointer to 0x0000 */
+ ETH->MACPMTCSR |= ETH_MACPMTCSR_WFFRPR;
+}
+
+/**
+ * @brief Populates the remote wakeup frame registers.
+ * @param Buffer: Pointer on remote WakeUp Frame Filter Register buffer data (8 words).
+ * @retval None
+ */
+void ETH_SetWakeUpFrameFilterRegister(uint32_t *Buffer)
+{
+ uint32_t i = 0;
+
+ /* Fill Remote Wake-up Frame Filter register with Buffer data */
+ for(i =0; iMACRWUFFR = Buffer[i];
+ }
+}
+
+/**
+ * @brief Enables or disables any unicast packet filtered by the MAC address
+ * recognition to be a wake-up frame.
+ * @param NewState: new state of the MAC Global Unicast Wake-Up.
+ * This parameter can be: ENABLE or DISABLE.
+ * @retval None
+ */
+void ETH_GlobalUnicastWakeUpCmd(FunctionalState NewState)
+{
+ /* Check the parameters */
+ assert_param(IS_FUNCTIONAL_STATE(NewState));
+
+ if (NewState != DISABLE)
+ {
+ /* Enable the MAC Global Unicast Wake-Up */
+ ETH->MACPMTCSR |= ETH_MACPMTCSR_GU;
+ }
+ else
+ {
+ /* Disable the MAC Global Unicast Wake-Up */
+ ETH->MACPMTCSR &= ~ETH_MACPMTCSR_GU;
+ }
+}
+
+/**
+ * @brief Checks whether the specified ETHERNET PMT flag is set or not.
+ * @param ETH_PMT_FLAG: specifies the flag to check.
+ * This parameter can be one of the following values:
+ * @arg ETH_PMT_FLAG_WUFFRPR : Wake-Up Frame Filter Register Pointer Reset
+ * @arg ETH_PMT_FLAG_WUFR : Wake-Up Frame Received
+ * @arg ETH_PMT_FLAG_MPR : Magic Packet Received
+ * @retval The new state of ETHERNET PMT Flag (SET or RESET).
+ */
+FlagStatus ETH_GetPMTFlagStatus(uint32_t ETH_PMT_FLAG)
+{
+ FlagStatus bitstatus = RESET;
+ /* Check the parameters */
+ assert_param(IS_ETH_PMT_GET_FLAG(ETH_PMT_FLAG));
+
+ if ((ETH->MACPMTCSR & ETH_PMT_FLAG) != (uint32_t)RESET)
+ {
+ bitstatus = SET;
+ }
+ else
+ {
+ bitstatus = RESET;
+ }
+ return bitstatus;
+}
+
+/**
+ * @brief Enables or disables the MAC Wake-Up Frame Detection.
+ * @param NewState: new state of the MAC Wake-Up Frame Detection.
+ * This parameter can be: ENABLE or DISABLE.
+ * @retval None
+ */
+void ETH_WakeUpFrameDetectionCmd(FunctionalState NewState)
+{
+ /* Check the parameters */
+ assert_param(IS_FUNCTIONAL_STATE(NewState));
+
+ if (NewState != DISABLE)
+ {
+ /* Enable the MAC Wake-Up Frame Detection */
+ ETH->MACPMTCSR |= ETH_MACPMTCSR_WFE;
+ }
+ else
+ {
+ /* Disable the MAC Wake-Up Frame Detection */
+ ETH->MACPMTCSR &= ~ETH_MACPMTCSR_WFE;
+ }
+}
+
+/**
+ * @brief Enables or disables the MAC Magic Packet Detection.
+ * @param NewState: new state of the MAC Magic Packet Detection.
+ * This parameter can be: ENABLE or DISABLE.
+ * @retval None
+ */
+void ETH_MagicPacketDetectionCmd(FunctionalState NewState)
+{
+ /* Check the parameters */
+ assert_param(IS_FUNCTIONAL_STATE(NewState));
+
+ if (NewState != DISABLE)
+ {
+ /* Enable the MAC Magic Packet Detection */
+ ETH->MACPMTCSR |= ETH_MACPMTCSR_MPE;
+ }
+ else
+ {
+ /* Disable the MAC Magic Packet Detection */
+ ETH->MACPMTCSR &= ~ETH_MACPMTCSR_MPE;
+ }
+}
+
+/**
+ * @brief Enables or disables the MAC Power Down.
+ * @param NewState: new state of the MAC Power Down.
+ * This parameter can be: ENABLE or DISABLE.
+ * @retval None
+ */
+void ETH_PowerDownCmd(FunctionalState NewState)
+{
+ /* Check the parameters */
+ assert_param(IS_FUNCTIONAL_STATE(NewState));
+
+ if (NewState != DISABLE)
+ {
+ /* Enable the MAC Power Down */
+ /* This puts the MAC in power down mode */
+ ETH->MACPMTCSR |= ETH_MACPMTCSR_PD;
+ }
+ else
+ {
+ /* Disable the MAC Power Down */
+ ETH->MACPMTCSR &= ~ETH_MACPMTCSR_PD;
+ }
+}
+
+/******************************************************************************/
+/* MMC functions */
+/******************************************************************************/
+/**
+ * @brief Preset and Initialize the MMC counters to almost-full value: 0xFFFF_FFF0 (full - 16)
+ * @param None
+ * @retval None
+ */
+void ETH_MMCCounterFullPreset(void)
+{
+ /* Preset and Initialize the MMC counters to almost-full value */
+ ETH->MMCCR |= ETH_MMCCR_MCFHP | ETH_MMCCR_MCP;
+}
+
+/**
+ * @brief Preset and Initialize the MMC counters to almost-hal value: 0x7FFF_FFF0 (half - 16).
+ * @param None
+ * @retval None
+ */
+void ETH_MMCCounterHalfPreset(void)
+{
+ /* Preset the MMC counters to almost-full value */
+ ETH->MMCCR &= ~ETH_MMCCR_MCFHP;
+ /* Initialize the MMC counters to almost-half value */
+ ETH->MMCCR |= ETH_MMCCR_MCP;
+}
+
+ /**
+ * @brief Enables or disables the MMC Counter Freeze.
+ * @param NewState: new state of the MMC Counter Freeze.
+ * This parameter can be: ENABLE or DISABLE.
+ * @retval None
+ */
+void ETH_MMCCounterFreezeCmd(FunctionalState NewState)
+{
+ /* Check the parameters */
+ assert_param(IS_FUNCTIONAL_STATE(NewState));
+
+ if (NewState != DISABLE)
+ {
+ /* Enable the MMC Counter Freeze */
+ ETH->MMCCR |= ETH_MMCCR_MCF;
+ }
+ else
+ {
+ /* Disable the MMC Counter Freeze */
+ ETH->MMCCR &= ~ETH_MMCCR_MCF;
+ }
+}
+
+/**
+ * @brief Enables or disables the MMC Reset On Read.
+ * @param NewState: new state of the MMC Reset On Read.
+ * This parameter can be: ENABLE or DISABLE.
+ * @retval None
+ */
+void ETH_MMCResetOnReadCmd(FunctionalState NewState)
+{
+ /* Check the parameters */
+ assert_param(IS_FUNCTIONAL_STATE(NewState));
+
+ if (NewState != DISABLE)
+ {
+ /* Enable the MMC Counter reset on read */
+ ETH->MMCCR |= ETH_MMCCR_ROR;
+ }
+ else
+ {
+ /* Disable the MMC Counter reset on read */
+ ETH->MMCCR &= ~ETH_MMCCR_ROR;
+ }
+}
+
+/**
+ * @brief Enables or disables the MMC Counter Stop Rollover.
+ * @param NewState: new state of the MMC Counter Stop Rollover.
+ * This parameter can be: ENABLE or DISABLE.
+ * @retval None
+ */
+void ETH_MMCCounterRolloverCmd(FunctionalState NewState)
+{
+ /* Check the parameters */
+ assert_param(IS_FUNCTIONAL_STATE(NewState));
+
+ if (NewState != DISABLE)
+ {
+ /* Disable the MMC Counter Stop Rollover */
+ ETH->MMCCR &= ~ETH_MMCCR_CSR;
+ }
+ else
+ {
+ /* Enable the MMC Counter Stop Rollover */
+ ETH->MMCCR |= ETH_MMCCR_CSR;
+ }
+}
+
+/**
+ * @brief Resets the MMC Counters.
+ * @param None
+ * @retval None
+ */
+void ETH_MMCCountersReset(void)
+{
+ /* Resets the MMC Counters */
+ ETH->MMCCR |= ETH_MMCCR_CR;
+}
+
+/**
+ * @brief Enables or disables the specified ETHERNET MMC interrupts.
+ * @param ETH_MMC_IT: specifies the ETHERNET MMC interrupt sources to be enabled or disabled.
+ * This parameter can be any combination of Tx interrupt or
+ * any combination of Rx interrupt (but not both)of the following values:
+ * @arg ETH_MMC_IT_TGF : When Tx good frame counter reaches half the maximum value
+ * @arg ETH_MMC_IT_TGFMSC: When Tx good multi col counter reaches half the maximum value
+ * @arg ETH_MMC_IT_TGFSC : When Tx good single col counter reaches half the maximum value
+ * @arg ETH_MMC_IT_RGUF : When Rx good unicast frames counter reaches half the maximum value
+ * @arg ETH_MMC_IT_RFAE : When Rx alignment error counter reaches half the maximum value
+ * @arg ETH_MMC_IT_RFCE : When Rx crc error counter reaches half the maximum value
+ * @param NewState: new state of the specified ETHERNET MMC interrupts.
+ * This parameter can be: ENABLE or DISABLE.
+ * @retval None
+ */
+void ETH_MMCITConfig(uint32_t ETH_MMC_IT, FunctionalState NewState)
+{
+ /* Check the parameters */
+ assert_param(IS_ETH_MMC_IT(ETH_MMC_IT));
+ assert_param(IS_FUNCTIONAL_STATE(NewState));
+
+ if ((ETH_MMC_IT & (uint32_t)0x10000000) != (uint32_t)RESET)
+ {
+ /* Remove Register mak from IT */
+ ETH_MMC_IT &= 0xEFFFFFFF;
+
+ /* ETHERNET MMC Rx interrupts selected */
+ if (NewState != DISABLE)
+ {
+ /* Enable the selected ETHERNET MMC interrupts */
+ ETH->MMCRIMR &=(~(uint32_t)ETH_MMC_IT);
+ }
+ else
+ {
+ /* Disable the selected ETHERNET MMC interrupts */
+ ETH->MMCRIMR |= ETH_MMC_IT;
+ }
+ }
+ else
+ {
+ /* ETHERNET MMC Tx interrupts selected */
+ if (NewState != DISABLE)
+ {
+ /* Enable the selected ETHERNET MMC interrupts */
+ ETH->MMCTIMR &=(~(uint32_t)ETH_MMC_IT);
+ }
+ else
+ {
+ /* Disable the selected ETHERNET MMC interrupts */
+ ETH->MMCTIMR |= ETH_MMC_IT;
+ }
+ }
+}
+
+/**
+ * @brief Checks whether the specified ETHERNET MMC IT is set or not.
+ * @param ETH_MMC_IT: specifies the ETHERNET MMC interrupt.
+ * This parameter can be one of the following values:
+ * @arg ETH_MMC_IT_TxFCGC: When Tx good frame counter reaches half the maximum value
+ * @arg ETH_MMC_IT_TxMCGC: When Tx good multi col counter reaches half the maximum value
+ * @arg ETH_MMC_IT_TxSCGC: When Tx good single col counter reaches half the maximum value
+ * @arg ETH_MMC_IT_RxUGFC: When Rx good unicast frames counter reaches half the maximum value
+ * @arg ETH_MMC_IT_RxAEC : When Rx alignment error counter reaches half the maximum value
+ * @arg ETH_MMC_IT_RxCEC : When Rx crc error counter reaches half the maximum value
+ * @retval The value of ETHERNET MMC IT (SET or RESET).
+ */
+ITStatus ETH_GetMMCITStatus(uint32_t ETH_MMC_IT)
+{
+ ITStatus bitstatus = RESET;
+ /* Check the parameters */
+ assert_param(IS_ETH_MMC_GET_IT(ETH_MMC_IT));
+
+ if ((ETH_MMC_IT & (uint32_t)0x10000000) != (uint32_t)RESET)
+ {
+ /* ETHERNET MMC Rx interrupts selected */
+ /* Check if the ETHERNET MMC Rx selected interrupt is enabled and occurred */
+ if ((((ETH->MMCRIR & ETH_MMC_IT) != (uint32_t)RESET)) && ((ETH->MMCRIMR & ETH_MMC_IT) == (uint32_t)RESET))
+ {
+ bitstatus = SET;
+ }
+ else
+ {
+ bitstatus = RESET;
+ }
+ }
+ else
+ {
+ /* ETHERNET MMC Tx interrupts selected */
+ /* Check if the ETHERNET MMC Tx selected interrupt is enabled and occurred */
+ if ((((ETH->MMCTIR & ETH_MMC_IT) != (uint32_t)RESET)) && ((ETH->MMCRIMR & ETH_MMC_IT) == (uint32_t)RESET))
+ {
+ bitstatus = SET;
+ }
+ else
+ {
+ bitstatus = RESET;
+ }
+ }
+
+ return bitstatus;
+}
+
+/**
+ * @brief Get the specified ETHERNET MMC register value.
+ * @param ETH_MMCReg: specifies the ETHERNET MMC register.
+ * This parameter can be one of the following values:
+ * @arg ETH_MMCCR : MMC CR register
+ * @arg ETH_MMCRIR : MMC RIR register
+ * @arg ETH_MMCTIR : MMC TIR register
+ * @arg ETH_MMCRIMR : MMC RIMR register
+ * @arg ETH_MMCTIMR : MMC TIMR register
+ * @arg ETH_MMCTGFSCCR : MMC TGFSCCR register
+ * @arg ETH_MMCTGFMSCCR: MMC TGFMSCCR register
+ * @arg ETH_MMCTGFCR : MMC TGFCR register
+ * @arg ETH_MMCRFCECR : MMC RFCECR register
+ * @arg ETH_MMCRFAECR : MMC RFAECR register
+ * @arg ETH_MMCRGUFCR : MMC RGUFCRregister
+ * @retval The value of ETHERNET MMC Register value.
+ */
+uint32_t ETH_GetMMCRegister(uint32_t ETH_MMCReg)
+{
+ /* Check the parameters */
+ assert_param(IS_ETH_MMC_REGISTER(ETH_MMCReg));
+
+ /* Return the selected register value */
+ return (*(__IO uint32_t *)(ETH_MAC_BASE + ETH_MMCReg));
+}
+
+
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
diff --git a/board/stm32f407-st-discovery/third_party_driver/include/connect_ethernet.h b/board/stm32f407-st-discovery/third_party_driver/include/connect_ethernet.h
new file mode 100644
index 00000000..25c7ce0c
--- /dev/null
+++ b/board/stm32f407-st-discovery/third_party_driver/include/connect_ethernet.h
@@ -0,0 +1,112 @@
+/**
+ ******************************************************************************
+ * @file connect_ethernet.h
+ * @author MCD Application Team
+ * @version V1.0.0
+ * @date 31-October-2011
+ * @brief STM32F4x7 Ethernet hardware configuration.
+ ******************************************************************************
+ * @attention
+ *
+ * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
+ * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
+ * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
+ * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
+ * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
+ * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
+ *
+ * © COPYRIGHT 2011 STMicroelectronics
+ ******************************************************************************
+ */
+
+ /*
+* Copyright (c) 2020 AIIT XUOS Lab
+* XiUOS is licensed under Mulan PSL v2.
+* You can use this software according to the terms and conditions of the Mulan PSL v2.
+* You may obtain a copy of Mulan PSL v2 at:
+* http://license.coscl.org.cn/MulanPSL2
+* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
+* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
+* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
+* See the Mulan PSL v2 for more details.
+*/
+
+/**
+* @file connect_ethernet.h
+* @brief Adapted network software protocol stack and hardware operation functions
+* @version 1.0
+* @author AIIT XUOS Lab
+* @date 2021-05-29
+*/
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __STM32F4x7_ETH_BSP_H
+#define __STM32F4x7_ETH_BSP_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+
+
+
+/* Exported types ------------------------------------------------------------*/
+/* Exported constants --------------------------------------------------------*/
+#define DP83848_PHY_ADDRESS 0x01 /* Relative to STM324xG-EVAL Board */
+
+/* Specific defines for EXTI line, used to manage Ethernet link status */
+#define ETH_LINK_EXTI_LINE EXTI_Line14
+#define ETH_LINK_EXTI_PORT_SOURCE EXTI_PortSourceGPIOB
+#define ETH_LINK_EXTI_PIN_SOURCE EXTI_PinSource14
+#define ETH_LINK_EXTI_IRQn EXTI15_10_IRQn
+/* PB14 */
+#define ETH_LINK_PIN GPIO_Pin_14
+#define ETH_LINK_GPIO_PORT GPIOB
+#define ETH_LINK_GPIO_CLK RCC_AHB1Periph_GPIOB
+/* PHY registers */
+#define PHY_MICR 0x11 /* MII Interrupt Control Register */
+#define PHY_MICR_INT_EN ((unsigned short)0x0002) /* PHY Enable interrupts */
+#define PHY_MICR_INT_OE ((unsigned short)0x0001) /* PHY Enable output interrupt events */
+#define PHY_MISR 0x12 /* MII Interrupt Status and Misc. Control Register */
+#define PHY_MISR_LINK_INT_EN ((unsigned short)0x0020) /* Enable Interrupt on change of link status */
+#define PHY_LINK_STATUS ((unsigned short)0x2000) /* PHY link status interrupt mask */
+
+
+
+
+#define RMII_MODE // User have to provide the 50 MHz clock by soldering a 50 MHz
+ // oscillator (ref SM7745HEV-50.0M or equivalent) on the U3
+ // footprint located under CN3 and also removing jumper on JP5.
+ // This oscillator is not provided with the board.
+ // For more details, please refer to STM3240G-EVAL evaluation
+ // board User manual (UM1461).
+
+
+//#define MII_MODE
+
+/* Uncomment the define below to clock the PHY from external 25MHz crystal (only for MII mode) */
+#ifdef MII_MODE
+ #define PHY_CLOCK_MCO
+#endif
+
+
+
+
+
+
+/* Exported macro ------------------------------------------------------------*/
+/* Exported functions ------------------------------------------------------- */
+void ETH_BSP_Config(void);
+uint32_t Eth_Link_PHYITConfig(uint16_t PHYAddress);
+void Eth_Link_EXTIConfig(void);
+void Eth_Link_ITHandler(unsigned short PHYAddress);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __STM32F4x7_ETH_BSP_H */
+
+
+/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/
diff --git a/board/stm32f407-st-discovery/third_party_driver/include/hardware_eth.h b/board/stm32f407-st-discovery/third_party_driver/include/hardware_eth.h
new file mode 100644
index 00000000..600ed771
--- /dev/null
+++ b/board/stm32f407-st-discovery/third_party_driver/include/hardware_eth.h
@@ -0,0 +1,1883 @@
+/**
+ ******************************************************************************
+ * @file hardware_eth.h
+ * @author MCD Application Team
+ * @version V1.0.0
+ * @date 14-October-2011
+ * @brief This file contains all the functions prototypes for the Ethernet
+ * firmware driver.
+ ******************************************************************************
+ * @attention
+ *
+ * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
+ * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
+ * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
+ * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
+ * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
+ * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
+ *
+ * © COPYRIGHT 2011 STMicroelectronics
+ ******************************************************************************
+ */
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __STM32F4x7_ETH_H
+#define __STM32F4x7_ETH_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+#include "hardware_eth_conf.h"
+
+/** @addtogroup STM32F4x7_ETH_Driver
+ * @{
+ */
+
+/** @defgroup ETH_Exported_Types
+ * @{
+ */
+
+/**
+ * @brief ETH MAC Init structure definition
+ * @note The user should not configure all the ETH_InitTypeDef structure's fields.
+ * By calling the ETH_StructInit function the structure�s fields are set to their default values.
+ * Only the parameters that will be set to a non-default value should be configured.
+ */
+typedef struct {
+/**
+ * @brief / * MAC
+ */
+ uint32_t ETH_AutoNegotiation; /*!< Selects or not the AutoNegotiation mode for the external PHY
+ The AutoNegotiation allows an automatic setting of the Speed (10/100Mbps)
+ and the mode (half/full-duplex).
+ This parameter can be a value of @ref ETH_AutoNegotiation */
+
+ uint32_t ETH_Watchdog; /*!< Selects or not the Watchdog timer
+ When enabled, the MAC allows no more then 2048 bytes to be received.
+ When disabled, the MAC can receive up to 16384 bytes.
+ This parameter can be a value of @ref ETH_watchdog */
+
+ uint32_t ETH_Jabber; /*!< Selects or not Jabber timer
+ When enabled, the MAC allows no more then 2048 bytes to be sent.
+ When disabled, the MAC can send up to 16384 bytes.
+ This parameter can be a value of @ref ETH_Jabber */
+
+ uint32_t ETH_InterFrameGap; /*!< Selects the minimum IFG between frames during transmission
+ This parameter can be a value of @ref ETH_Inter_Frame_Gap */
+
+ uint32_t ETH_CarrierSense; /*!< Selects or not the Carrier Sense
+ This parameter can be a value of @ref ETH_Carrier_Sense */
+
+ uint32_t ETH_Speed; /*!< Sets the Ethernet speed: 10/100 Mbps
+ This parameter can be a value of @ref ETH_Speed */
+
+ uint32_t ETH_ReceiveOwn; /*!< Selects or not the ReceiveOwn
+ ReceiveOwn allows the reception of frames when the TX_EN signal is asserted
+ in Half-Duplex mode
+ This parameter can be a value of @ref ETH_Receive_Own */
+
+ uint32_t ETH_LoopbackMode; /*!< Selects or not the internal MAC MII Loopback mode
+ This parameter can be a value of @ref ETH_Loop_Back_Mode */
+
+ uint32_t ETH_Mode; /*!< Selects the MAC duplex mode: Half-Duplex or Full-Duplex mode
+ This parameter can be a value of @ref ETH_Duplex_Mode */
+
+ uint32_t ETH_ChecksumOffload; /*!< Selects or not the IPv4 checksum checking for received frame payloads' TCP/UDP/ICMP headers.
+ This parameter can be a value of @ref ETH_Checksum_Offload */
+
+ uint32_t ETH_RetryTransmission; /*!< Selects or not the MAC attempt retries transmission, based on the settings of BL,
+ when a collision occurs (Half-Duplex mode)
+ This parameter can be a value of @ref ETH_Retry_Transmission */
+
+ uint32_t ETH_AutomaticPadCRCStrip; /*!< Selects or not the Automatic MAC Pad/CRC Stripping
+ This parameter can be a value of @ref ETH_Automatic_Pad_CRC_Strip */
+
+ uint32_t ETH_BackOffLimit; /*!< Selects the BackOff limit value
+ This parameter can be a value of @ref ETH_Back_Off_Limit */
+
+ uint32_t ETH_DeferralCheck; /*!< Selects or not the deferral check function (Half-Duplex mode)
+ This parameter can be a value of @ref ETH_Deferral_Check */
+
+ uint32_t ETH_ReceiveAll; /*!< Selects or not all frames reception by the MAC (No filtering)
+ This parameter can be a value of @ref ETH_Receive_All */
+
+ uint32_t ETH_SourceAddrFilter; /*!< Selects the Source Address Filter mode
+ This parameter can be a value of @ref ETH_Source_Addr_Filter */
+
+ uint32_t ETH_PassControlFrames; /*!< Sets the forwarding mode of the control frames (including unicast and multicast PAUSE frames)
+ This parameter can be a value of @ref ETH_Pass_Control_Frames */
+
+ uint32_t ETH_BroadcastFramesReception; /*!< Selects or not the reception of Broadcast Frames
+ This parameter can be a value of @ref ETH_Broadcast_Frames_Reception */
+
+ uint32_t ETH_DestinationAddrFilter; /*!< Sets the destination filter mode for both unicast and multicast frames
+ This parameter can be a value of @ref ETH_Destination_Addr_Filter */
+
+ uint32_t ETH_PromiscuousMode; /*!< Selects or not the Promiscuous Mode
+ This parameter can be a value of @ref ETH_Promiscuous_Mode */
+
+ uint32_t ETH_MulticastFramesFilter; /*!< Selects the Multicast Frames filter mode: None/HashTableFilter/PerfectFilter/PerfectHashTableFilter
+ This parameter can be a value of @ref ETH_Multicast_Frames_Filter */
+
+ uint32_t ETH_UnicastFramesFilter; /*!< Selects the Unicast Frames filter mode: HashTableFilter/PerfectFilter/PerfectHashTableFilter
+ This parameter can be a value of @ref ETH_Unicast_Frames_Filter */
+
+ uint32_t ETH_HashTableHigh; /*!< This field holds the higher 32 bits of Hash table. */
+
+ uint32_t ETH_HashTableLow; /*!< This field holds the lower 32 bits of Hash table. */
+
+ uint32_t ETH_PauseTime; /*!< This field holds the value to be used in the Pause Time field in the
+ transmit control frame */
+
+ uint32_t ETH_ZeroQuantaPause; /*!< Selects or not the automatic generation of Zero-Quanta Pause Control frames
+ This parameter can be a value of @ref ETH_Zero_Quanta_Pause */
+
+ uint32_t ETH_PauseLowThreshold; /*!< This field configures the threshold of the PAUSE to be checked for
+ automatic retransmission of PAUSE Frame
+ This parameter can be a value of @ref ETH_Pause_Low_Threshold */
+
+ uint32_t ETH_UnicastPauseFrameDetect; /*!< Selects or not the MAC detection of the Pause frames (with MAC Address0
+ unicast address and unique multicast address)
+ This parameter can be a value of @ref ETH_Unicast_Pause_Frame_Detect */
+
+ uint32_t ETH_ReceiveFlowControl; /*!< Enables or disables the MAC to decode the received Pause frame and
+ disable its transmitter for a specified time (Pause Time)
+ This parameter can be a value of @ref ETH_Receive_Flow_Control */
+
+ uint32_t ETH_TransmitFlowControl; /*!< Enables or disables the MAC to transmit Pause frames (Full-Duplex mode)
+ or the MAC back-pressure operation (Half-Duplex mode)
+ This parameter can be a value of @ref ETH_Transmit_Flow_Control */
+
+ uint32_t ETH_VLANTagComparison; /*!< Selects the 12-bit VLAN identifier or the complete 16-bit VLAN tag for
+ comparison and filtering
+ This parameter can be a value of @ref ETH_VLAN_Tag_Comparison */
+
+ uint32_t ETH_VLANTagIdentifier; /*!< Holds the VLAN tag identifier for receive frames */
+
+/**
+ * @brief / * DMA
+ */
+
+ uint32_t ETH_DropTCPIPChecksumErrorFrame; /*!< Selects or not the Dropping of TCP/IP Checksum Error Frames
+ This parameter can be a value of @ref ETH_Drop_TCP_IP_Checksum_Error_Frame */
+
+ uint32_t ETH_ReceiveStoreForward; /*!< Enables or disables the Receive store and forward mode
+ This parameter can be a value of @ref ETH_Receive_Store_Forward */
+
+ uint32_t ETH_FlushReceivedFrame; /*!< Enables or disables the flushing of received frames
+ This parameter can be a value of @ref ETH_Flush_Received_Frame */
+
+ uint32_t ETH_TransmitStoreForward; /*!< Enables or disables Transmit store and forward mode
+ This parameter can be a value of @ref ETH_Transmit_Store_Forward */
+
+ uint32_t ETH_TransmitThresholdControl; /*!< Selects or not the Transmit Threshold Control
+ This parameter can be a value of @ref ETH_Transmit_Threshold_Control */
+
+ uint32_t ETH_ForwardErrorFrames; /*!< Selects or not the forward to the DMA of erroneous frames
+ This parameter can be a value of @ref ETH_Forward_Error_Frames */
+
+ uint32_t ETH_ForwardUndersizedGoodFrames; /*!< Enables or disables the Rx FIFO to forward Undersized frames (frames with no Error
+ and length less than 64 bytes) including pad-bytes and CRC)
+ This parameter can be a value of @ref ETH_Forward_Undersized_Good_Frames */
+
+ uint32_t ETH_ReceiveThresholdControl; /*!< Selects the threshold level of the Receive FIFO
+ This parameter can be a value of @ref ETH_Receive_Threshold_Control */
+
+ uint32_t ETH_SecondFrameOperate; /*!< Selects or not the Operate on second frame mode, which allows the DMA to process a second
+ frame of Transmit data even before obtaining the status for the first frame.
+ This parameter can be a value of @ref ETH_Second_Frame_Operate */
+
+ uint32_t ETH_AddressAlignedBeats; /*!< Enables or disables the Address Aligned Beats
+ This parameter can be a value of @ref ETH_Address_Aligned_Beats */
+
+ uint32_t ETH_FixedBurst; /*!< Enables or disables the AHB Master interface fixed burst transfers
+ This parameter can be a value of @ref ETH_Fixed_Burst */
+
+ uint32_t ETH_RxDMABurstLength; /*!< Indicates the maximum number of beats to be transferred in one Rx DMA transaction
+ This parameter can be a value of @ref ETH_Rx_DMA_Burst_Length */
+
+ uint32_t ETH_TxDMABurstLength; /*!< Indicates the maximum number of beats to be transferred in one Tx DMA transaction
+ This parameter can be a value of @ref ETH_Tx_DMA_Burst_Length */
+
+ uint32_t ETH_DescriptorSkipLength; /*!< Specifies the number of word to skip between two unchained descriptors (Ring mode) */
+
+ uint32_t ETH_DMAArbitration; /*!< Selects the DMA Tx/Rx arbitration
+ This parameter can be a value of @ref ETH_DMA_Arbitration */
+}ETH_InitTypeDef;
+
+/**--------------------------------------------------------------------------**/
+/**
+ * @brief DMA descriptors types
+ */
+/**--------------------------------------------------------------------------**/
+
+/**
+ * @brief ETH DMA Descriptors data structure definition
+ */
+typedef struct {
+ __IO uint32_t Status; /*!< Status */
+ uint32_t ControlBufferSize; /*!< Control and Buffer1, Buffer2 lengths */
+ uint32_t Buffer1Addr; /*!< Buffer1 address pointer */
+ uint32_t Buffer2NextDescAddr; /*!< Buffer2 or next descriptor address pointer */
+/* Enhanced ETHERNET DMA PTP Descriptors */
+#ifdef USE_ENHANCED_DMA_DESCRIPTORS
+ uint32_t ExtendedStatus; /* Extended status for PTP receive descriptor */
+ uint32_t Reserved1; /* Reserved */
+ uint32_t TimeStampLow; /* Time Stamp Low value for transmit and receive */
+ uint32_t TimeStampHigh; /* Time Stamp High value for transmit and receive */
+#endif /* USE_ENHANCED_DMA_DESCRIPTORS */
+} ETH_DMADESCTypeDef;
+
+
+typedef struct{
+ u32 length;
+ u32 buffer;
+ __IO ETH_DMADESCTypeDef *descriptor;
+}FrameTypeDef;
+
+
+typedef struct {
+ __IO ETH_DMADESCTypeDef *FS_Rx_Desc; /*!< First Segment Rx Desc */
+ __IO ETH_DMADESCTypeDef *LS_Rx_Desc; /*!< Last Segment Rx Desc */
+ __IO uint32_t Seg_Count; /*!< Segment count */
+} ETH_DMA_Rx_Frame_infos;
+
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Exported_Constants
+ * @{
+ */
+
+/**--------------------------------------------------------------------------**/
+/**
+ * @brief ETH Frames defines
+ */
+/**--------------------------------------------------------------------------**/
+
+/** @defgroup ENET_Buffers_setting
+ * @{
+ */
+#define ETH_MAX_PACKET_SIZE 1524 /*!< ETH_HEADER + ETH_EXTRA + VLAN_TAG + MAX_ETH_PAYLOAD + ETH_CRC */
+#define ETH_HEADER 14 /*!< 6 byte Dest addr, 6 byte Src addr, 2 byte length/type */
+#define ETH_CRC 4 /*!< Ethernet CRC */
+#define ETH_EXTRA 2 /*!< Extra bytes in some cases */
+#define VLAN_TAG 4 /*!< optional 802.1q VLAN Tag */
+#define MIN_ETH_PAYLOAD 46 /*!< Minimum Ethernet payload size */
+#define MAX_ETH_PAYLOAD 1500 /*!< Maximum Ethernet payload size */
+#define JUMBO_FRAME_PAYLOAD 9000 /*!< Jumbo frame payload size */
+
+ /* Ethernet driver receive buffers are organized in a chained linked-list, when
+ an ethernet packet is received, the Rx-DMA will transfer the packet from RxFIFO
+ to the driver receive buffers memory.
+
+ Depending on the size of the received ethernet packet and the size of
+ each ethernet driver receive buffer, the received packet can take one or more
+ ethernet driver receive buffer.
+
+ In below are defined the size of one ethernet driver receive buffer ETH_RX_BUF_SIZE
+ and the total count of the driver receive buffers ETH_RXBUFNB.
+
+ The configured value for ETH_RX_BUF_SIZE and ETH_RXBUFNB are only provided as
+ example, they can be reconfigured in the application layer to fit the application
+ needs */
+
+/* Here we configure each Ethernet driver receive buffer to fit the Max size Ethernet
+ packet */
+#ifndef ETH_RX_BUF_SIZE
+ #define ETH_RX_BUF_SIZE ETH_MAX_PACKET_SIZE
+#endif
+
+/* 5 Ethernet driver receive buffers are used (in a chained linked list)*/
+#ifndef ETH_RXBUFNB
+ #define ETH_RXBUFNB 5 /* 5 Rx buffers of size ETH_RX_BUF_SIZE */
+#endif
+
+
+ /* Ethernet driver transmit buffers are organized in a chained linked-list, when
+ an ethernet packet is transmitted, Tx-DMA will transfer the packet from the
+ driver transmit buffers memory to the TxFIFO.
+
+ Depending on the size of the Ethernet packet to be transmitted and the size of
+ each ethernet driver transmit buffer, the packet to be transmitted can take
+ one or more ethernet driver transmit buffer.
+
+ In below are defined the size of one ethernet driver transmit buffer ETH_TX_BUF_SIZE
+ and the total count of the driver transmit buffers ETH_TXBUFNB.
+
+ The configured value for ETH_TX_BUF_SIZE and ETH_TXBUFNB are only provided as
+ example, they can be reconfigured in the application layer to fit the application
+ needs */
+
+/* Here we configure each Ethernet driver transmit buffer to fit the Max size Ethernet
+ packet */
+#ifndef ETH_TX_BUF_SIZE
+ #define ETH_TX_BUF_SIZE ETH_MAX_PACKET_SIZE
+#endif
+
+/* 5 ethernet driver transmit buffers are used (in a chained linked list)*/
+#ifndef ETH_TXBUFNB
+ #define ETH_TXBUFNB 5 /* 5 Tx buffers of size ETH_TX_BUF_SIZE */
+#endif
+
+#define ETH_DMARxDesc_FrameLengthShift 16
+
+/**--------------------------------------------------------------------------**/
+/**
+ * @brief Ethernet DMA descriptors registers bits definition
+ */
+/**--------------------------------------------------------------------------**/
+
+/**
+@code
+ DMA Tx Desciptor
+ -----------------------------------------------------------------------------------------------
+ TDES0 | OWN(31) | CTRL[30:26] | Reserved[25:24] | CTRL[23:20] | Reserved[19:17] | Status[16:0] |
+ -----------------------------------------------------------------------------------------------
+ TDES1 | Reserved[31:29] | Buffer2 ByteCount[28:16] | Reserved[15:13] | Buffer1 ByteCount[12:0] |
+ -----------------------------------------------------------------------------------------------
+ TDES2 | Buffer1 Address [31:0] |
+ -----------------------------------------------------------------------------------------------
+ TDES3 | Buffer2 Address [31:0] / Next Descriptor Address [31:0] |
+ -----------------------------------------------------------------------------------------------
+@endcode
+*/
+
+/**
+ * @brief Bit definition of TDES0 register: DMA Tx descriptor status register
+ */
+#define ETH_DMATxDesc_OWN ((uint32_t)0x80000000) /*!< OWN bit: descriptor is owned by DMA engine */
+#define ETH_DMATxDesc_IC ((uint32_t)0x40000000) /*!< Interrupt on Completion */
+#define ETH_DMATxDesc_LS ((uint32_t)0x20000000) /*!< Last Segment */
+#define ETH_DMATxDesc_FS ((uint32_t)0x10000000) /*!< First Segment */
+#define ETH_DMATxDesc_DC ((uint32_t)0x08000000) /*!< Disable CRC */
+#define ETH_DMATxDesc_DP ((uint32_t)0x04000000) /*!< Disable Padding */
+#define ETH_DMATxDesc_TTSE ((uint32_t)0x02000000) /*!< Transmit Time Stamp Enable */
+#define ETH_DMATxDesc_CIC ((uint32_t)0x00C00000) /*!< Checksum Insertion Control: 4 cases */
+#define ETH_DMATxDesc_CIC_ByPass ((uint32_t)0x00000000) /*!< Do Nothing: Checksum Engine is bypassed */
+#define ETH_DMATxDesc_CIC_IPV4Header ((uint32_t)0x00400000) /*!< IPV4 header Checksum Insertion */
+#define ETH_DMATxDesc_CIC_TCPUDPICMP_Segment ((uint32_t)0x00800000) /*!< TCP/UDP/ICMP Checksum Insertion calculated over segment only */
+#define ETH_DMATxDesc_CIC_TCPUDPICMP_Full ((uint32_t)0x00C00000) /*!< TCP/UDP/ICMP Checksum Insertion fully calculated */
+#define ETH_DMATxDesc_TER ((uint32_t)0x00200000) /*!< Transmit End of Ring */
+#define ETH_DMATxDesc_TCH ((uint32_t)0x00100000) /*!< Second Address Chained */
+#define ETH_DMATxDesc_TTSS ((uint32_t)0x00020000) /*!< Tx Time Stamp Status */
+#define ETH_DMATxDesc_IHE ((uint32_t)0x00010000) /*!< IP Header Error */
+#define ETH_DMATxDesc_ES ((uint32_t)0x00008000) /*!< Error summary: OR of the following bits: UE || ED || EC || LCO || NC || LCA || FF || JT */
+#define ETH_DMATxDesc_JT ((uint32_t)0x00004000) /*!< Jabber Timeout */
+#define ETH_DMATxDesc_FF ((uint32_t)0x00002000) /*!< Frame Flushed: DMA/MTL flushed the frame due to SW flush */
+#define ETH_DMATxDesc_PCE ((uint32_t)0x00001000) /*!< Payload Checksum Error */
+#define ETH_DMATxDesc_LCA ((uint32_t)0x00000800) /*!< Loss of Carrier: carrier lost during transmission */
+#define ETH_DMATxDesc_NC ((uint32_t)0x00000400) /*!< No Carrier: no carrier signal from the transceiver */
+#define ETH_DMATxDesc_LCO ((uint32_t)0x00000200) /*!< Late Collision: transmission aborted due to collision */
+#define ETH_DMATxDesc_EC ((uint32_t)0x00000100) /*!< Excessive Collision: transmission aborted after 16 collisions */
+#define ETH_DMATxDesc_VF ((uint32_t)0x00000080) /*!< VLAN Frame */
+#define ETH_DMATxDesc_CC ((uint32_t)0x00000078) /*!< Collision Count */
+#define ETH_DMATxDesc_ED ((uint32_t)0x00000004) /*!< Excessive Deferral */
+#define ETH_DMATxDesc_UF ((uint32_t)0x00000002) /*!< Underflow Error: late data arrival from the memory */
+#define ETH_DMATxDesc_DB ((uint32_t)0x00000001) /*!< Deferred Bit */
+
+/**
+ * @brief Bit definition of TDES1 register
+ */
+#define ETH_DMATxDesc_TBS2 ((uint32_t)0x1FFF0000) /*!< Transmit Buffer2 Size */
+#define ETH_DMATxDesc_TBS1 ((uint32_t)0x00001FFF) /*!< Transmit Buffer1 Size */
+
+/**
+ * @brief Bit definition of TDES2 register
+ */
+#define ETH_DMATxDesc_B1AP ((uint32_t)0xFFFFFFFF) /*!< Buffer1 Address Pointer */
+
+/**
+ * @brief Bit definition of TDES3 register
+ */
+#define ETH_DMATxDesc_B2AP ((uint32_t)0xFFFFFFFF) /*!< Buffer2 Address Pointer */
+
+ /*---------------------------------------------------------------------------------------------
+ TDES6 | Transmit Time Stamp Low [31:0] |
+ -----------------------------------------------------------------------------------------------
+ TDES7 | Transmit Time Stamp High [31:0] |
+ ----------------------------------------------------------------------------------------------*/
+
+/* Bit definition of TDES6 register */
+ #define ETH_DMAPTPTxDesc_TTSL ((uint32_t)0xFFFFFFFF) /* Transmit Time Stamp Low */
+
+/* Bit definition of TDES7 register */
+ #define ETH_DMAPTPTxDesc_TTSH ((uint32_t)0xFFFFFFFF) /* Transmit Time Stamp High */
+
+/**
+ * @}
+ */
+
+
+/** @defgroup DMA_Rx_descriptor
+ * @{
+ */
+
+/**
+@code
+ DMA Rx Descriptor
+ --------------------------------------------------------------------------------------------------------------------
+ RDES0 | OWN(31) | Status [30:0] |
+ ---------------------------------------------------------------------------------------------------------------------
+ RDES1 | CTRL(31) | Reserved[30:29] | Buffer2 ByteCount[28:16] | CTRL[15:14] | Reserved(13) | Buffer1 ByteCount[12:0] |
+ ---------------------------------------------------------------------------------------------------------------------
+ RDES2 | Buffer1 Address [31:0] |
+ ---------------------------------------------------------------------------------------------------------------------
+ RDES3 | Buffer2 Address [31:0] / Next Descriptor Address [31:0] |
+ ---------------------------------------------------------------------------------------------------------------------
+@endcode
+*/
+
+/**
+ * @brief Bit definition of RDES0 register: DMA Rx descriptor status register
+ */
+#define ETH_DMARxDesc_OWN ((uint32_t)0x80000000) /*!< OWN bit: descriptor is owned by DMA engine */
+#define ETH_DMARxDesc_AFM ((uint32_t)0x40000000) /*!< DA Filter Fail for the rx frame */
+#define ETH_DMARxDesc_FL ((uint32_t)0x3FFF0000) /*!< Receive descriptor frame length */
+#define ETH_DMARxDesc_ES ((uint32_t)0x00008000) /*!< Error summary: OR of the following bits: DE || OE || IPC || LC || RWT || RE || CE */
+#define ETH_DMARxDesc_DE ((uint32_t)0x00004000) /*!< Descriptor error: no more descriptors for receive frame */
+#define ETH_DMARxDesc_SAF ((uint32_t)0x00002000) /*!< SA Filter Fail for the received frame */
+#define ETH_DMARxDesc_LE ((uint32_t)0x00001000) /*!< Frame size not matching with length field */
+#define ETH_DMARxDesc_OE ((uint32_t)0x00000800) /*!< Overflow Error: Frame was damaged due to buffer overflow */
+#define ETH_DMARxDesc_VLAN ((uint32_t)0x00000400) /*!< VLAN Tag: received frame is a VLAN frame */
+#define ETH_DMARxDesc_FS ((uint32_t)0x00000200) /*!< First descriptor of the frame */
+#define ETH_DMARxDesc_LS ((uint32_t)0x00000100) /*!< Last descriptor of the frame */
+#define ETH_DMARxDesc_IPV4HCE ((uint32_t)0x00000080) /*!< IPC Checksum Error: Rx Ipv4 header checksum error */
+#define ETH_DMARxDesc_LC ((uint32_t)0x00000040) /*!< Late collision occurred during reception */
+#define ETH_DMARxDesc_FT ((uint32_t)0x00000020) /*!< Frame type - Ethernet, otherwise 802.3 */
+#define ETH_DMARxDesc_RWT ((uint32_t)0x00000010) /*!< Receive Watchdog Timeout: watchdog timer expired during reception */
+#define ETH_DMARxDesc_RE ((uint32_t)0x00000008) /*!< Receive error: error reported by MII interface */
+#define ETH_DMARxDesc_DBE ((uint32_t)0x00000004) /*!< Dribble bit error: frame contains non int multiple of 8 bits */
+#define ETH_DMARxDesc_CE ((uint32_t)0x00000002) /*!< CRC error */
+#define ETH_DMARxDesc_MAMPCE ((uint32_t)0x00000001) /*!< Rx MAC Address/Payload Checksum Error: Rx MAC address matched/ Rx Payload Checksum Error */
+
+/**
+ * @brief Bit definition of RDES1 register
+ */
+#define ETH_DMARxDesc_DIC ((uint32_t)0x80000000) /*!< Disable Interrupt on Completion */
+#define ETH_DMARxDesc_RBS2 ((uint32_t)0x1FFF0000) /*!< Receive Buffer2 Size */
+#define ETH_DMARxDesc_RER ((uint32_t)0x00008000) /*!< Receive End of Ring */
+#define ETH_DMARxDesc_RCH ((uint32_t)0x00004000) /*!< Second Address Chained */
+#define ETH_DMARxDesc_RBS1 ((uint32_t)0x00001FFF) /*!< Receive Buffer1 Size */
+
+/**
+ * @brief Bit definition of RDES2 register
+ */
+#define ETH_DMARxDesc_B1AP ((uint32_t)0xFFFFFFFF) /*!< Buffer1 Address Pointer */
+
+/**
+ * @brief Bit definition of RDES3 register
+ */
+#define ETH_DMARxDesc_B2AP ((uint32_t)0xFFFFFFFF) /*!< Buffer2 Address Pointer */
+
+/*---------------------------------------------------------------------------------------------------------------------
+ RDES4 | Reserved[31:15] | Extended Status [14:0] |
+ ---------------------------------------------------------------------------------------------------------------------
+ RDES5 | Reserved[31:0] |
+ ---------------------------------------------------------------------------------------------------------------------
+ RDES6 | Receive Time Stamp Low [31:0] |
+ ---------------------------------------------------------------------------------------------------------------------
+ RDES7 | Receive Time Stamp High [31:0] |
+ --------------------------------------------------------------------------------------------------------------------*/
+
+/* Bit definition of RDES4 register */
+#define ETH_DMAPTPRxDesc_PTPV ((uint32_t)0x00002000) /* PTP Version */
+#define ETH_DMAPTPRxDesc_PTPFT ((uint32_t)0x00001000) /* PTP Frame Type */
+#define ETH_DMAPTPRxDesc_PTPMT ((uint32_t)0x00000F00) /* PTP Message Type */
+ #define ETH_DMAPTPRxDesc_PTPMT_Sync ((uint32_t)0x00000100) /* SYNC message (all clock types) */
+ #define ETH_DMAPTPRxDesc_PTPMT_FollowUp ((uint32_t)0x00000200) /* FollowUp message (all clock types) */
+ #define ETH_DMAPTPRxDesc_PTPMT_DelayReq ((uint32_t)0x00000300) /* DelayReq message (all clock types) */
+ #define ETH_DMAPTPRxDesc_PTPMT_DelayResp ((uint32_t)0x00000400) /* DelayResp message (all clock types) */
+ #define ETH_DMAPTPRxDesc_PTPMT_PdelayReq_Announce ((uint32_t)0x00000500) /* PdelayReq message (peer-to-peer transparent clock) or Announce message (Ordinary or Boundary clock) */
+ #define ETH_DMAPTPRxDesc_PTPMT_PdelayResp_Manag ((uint32_t)0x00000600) /* PdelayResp message (peer-to-peer transparent clock) or Management message (Ordinary or Boundary clock) */
+ #define ETH_DMAPTPRxDesc_PTPMT_PdelayRespFollowUp_Signal ((uint32_t)0x00000700) /* PdelayRespFollowUp message (peer-to-peer transparent clock) or Signaling message (Ordinary or Boundary clock) */
+#define ETH_DMAPTPRxDesc_IPV6PR ((uint32_t)0x00000080) /* IPv6 Packet Received */
+#define ETH_DMAPTPRxDesc_IPV4PR ((uint32_t)0x00000040) /* IPv4 Packet Received */
+#define ETH_DMAPTPRxDesc_IPCB ((uint32_t)0x00000020) /* IP Checksum Bypassed */
+#define ETH_DMAPTPRxDesc_IPPE ((uint32_t)0x00000010) /* IP Payload Error */
+#define ETH_DMAPTPRxDesc_IPHE ((uint32_t)0x00000008) /* IP Header Error */
+#define ETH_DMAPTPRxDesc_IPPT ((uint32_t)0x00000007) /* IP Payload Type */
+ #define ETH_DMAPTPRxDesc_IPPT_UDP ((uint32_t)0x00000001) /* UDP payload encapsulated in the IP datagram */
+ #define ETH_DMAPTPRxDesc_IPPT_TCP ((uint32_t)0x00000002) /* TCP payload encapsulated in the IP datagram */
+ #define ETH_DMAPTPRxDesc_IPPT_ICMP ((uint32_t)0x00000003) /* ICMP payload encapsulated in the IP datagram */
+
+/* Bit definition of RDES6 register */
+#define ETH_DMAPTPRxDesc_RTSL ((uint32_t)0xFFFFFFFF) /* Receive Time Stamp Low */
+
+/* Bit definition of RDES7 register */
+#define ETH_DMAPTPRxDesc_RTSH ((uint32_t)0xFFFFFFFF) /* Receive Time Stamp High */
+
+
+/**--------------------------------------------------------------------------**/
+/**
+ * @brief Description of common PHY registers
+ */
+/**--------------------------------------------------------------------------**/
+
+/**
+ * @}
+ */
+
+/** @defgroup PHY_Read_write_Timeouts
+ * @{
+ */
+#define PHY_READ_TO ((uint32_t)0x0004FFFF)
+#define PHY_WRITE_TO ((uint32_t)0x0004FFFF)
+
+/**
+ * @}
+ */
+
+/** @defgroup PHY_Register_address
+ * @{
+ */
+#define PHY_BCR 0 /*!< Transceiver Basic Control Register */
+#define PHY_BSR 1 /*!< Transceiver Basic Status Register */
+
+#define IS_ETH_PHY_ADDRESS(ADDRESS) ((ADDRESS) <= 0x20)
+#define IS_ETH_PHY_REG(REG) (((REG) == PHY_BCR) || \
+ ((REG) == PHY_BSR) || \
+ ((REG) == PHY_SR))
+/**
+ * @}
+ */
+
+/** @defgroup PHY_basic_Control_register
+ * @{
+ */
+#define PHY_Reset ((uint16_t)0x8000) /*!< PHY Reset */
+#define PHY_Loopback ((uint16_t)0x4000) /*!< Select loop-back mode */
+#define PHY_FULLDUPLEX_100M ((uint16_t)0x2100) /*!< Set the full-duplex mode at 100 Mb/s */
+#define PHY_HALFDUPLEX_100M ((uint16_t)0x2000) /*!< Set the half-duplex mode at 100 Mb/s */
+#define PHY_FULLDUPLEX_10M ((uint16_t)0x0100) /*!< Set the full-duplex mode at 10 Mb/s */
+#define PHY_HALFDUPLEX_10M ((uint16_t)0x0000) /*!< Set the half-duplex mode at 10 Mb/s */
+#define PHY_AutoNegotiation ((uint16_t)0x1000) /*!< Enable auto-negotiation function */
+#define PHY_Restart_AutoNegotiation ((uint16_t)0x0200) /*!< Restart auto-negotiation function */
+#define PHY_Powerdown ((uint16_t)0x0800) /*!< Select the power down mode */
+#define PHY_Isolate ((uint16_t)0x0400) /*!< Isolate PHY from MII */
+
+/**
+ * @}
+ */
+
+/** @defgroup PHY_basic_status_register
+ * @{
+ */
+#define PHY_AutoNego_Complete ((uint16_t)0x0020) /*!< Auto-Negotiation process completed */
+#define PHY_Linked_Status ((uint16_t)0x0004) /*!< Valid link established */
+#define PHY_Jabber_detection ((uint16_t)0x0002) /*!< Jabber condition detected */
+
+/**
+ * @}
+ */
+
+/**--------------------------------------------------------------------------**/
+/**
+ * @brief MAC defines
+ */
+/**--------------------------------------------------------------------------**/
+
+/** @defgroup ETH_AutoNegotiation
+ * @{
+ */
+#define ETH_AutoNegotiation_Enable ((uint32_t)0x00000001)
+#define ETH_AutoNegotiation_Disable ((uint32_t)0x00000000)
+#define IS_ETH_AUTONEGOTIATION(CMD) (((CMD) == ETH_AutoNegotiation_Enable) || \
+ ((CMD) == ETH_AutoNegotiation_Disable))
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_watchdog
+ * @{
+ */
+#define ETH_Watchdog_Enable ((uint32_t)0x00000000)
+#define ETH_Watchdog_Disable ((uint32_t)0x00800000)
+#define IS_ETH_WATCHDOG(CMD) (((CMD) == ETH_Watchdog_Enable) || \
+ ((CMD) == ETH_Watchdog_Disable))
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Jabber
+ * @{
+ */
+#define ETH_Jabber_Enable ((uint32_t)0x00000000)
+#define ETH_Jabber_Disable ((uint32_t)0x00400000)
+#define IS_ETH_JABBER(CMD) (((CMD) == ETH_Jabber_Enable) || \
+ ((CMD) == ETH_Jabber_Disable))
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Inter_Frame_Gap
+ * @{
+ */
+#define ETH_InterFrameGap_96Bit ((uint32_t)0x00000000) /*!< minimum IFG between frames during transmission is 96Bit */
+#define ETH_InterFrameGap_88Bit ((uint32_t)0x00020000) /*!< minimum IFG between frames during transmission is 88Bit */
+#define ETH_InterFrameGap_80Bit ((uint32_t)0x00040000) /*!< minimum IFG between frames during transmission is 80Bit */
+#define ETH_InterFrameGap_72Bit ((uint32_t)0x00060000) /*!< minimum IFG between frames during transmission is 72Bit */
+#define ETH_InterFrameGap_64Bit ((uint32_t)0x00080000) /*!< minimum IFG between frames during transmission is 64Bit */
+#define ETH_InterFrameGap_56Bit ((uint32_t)0x000A0000) /*!< minimum IFG between frames during transmission is 56Bit */
+#define ETH_InterFrameGap_48Bit ((uint32_t)0x000C0000) /*!< minimum IFG between frames during transmission is 48Bit */
+#define ETH_InterFrameGap_40Bit ((uint32_t)0x000E0000) /*!< minimum IFG between frames during transmission is 40Bit */
+#define IS_ETH_INTER_FRAME_GAP(GAP) (((GAP) == ETH_InterFrameGap_96Bit) || \
+ ((GAP) == ETH_InterFrameGap_88Bit) || \
+ ((GAP) == ETH_InterFrameGap_80Bit) || \
+ ((GAP) == ETH_InterFrameGap_72Bit) || \
+ ((GAP) == ETH_InterFrameGap_64Bit) || \
+ ((GAP) == ETH_InterFrameGap_56Bit) || \
+ ((GAP) == ETH_InterFrameGap_48Bit) || \
+ ((GAP) == ETH_InterFrameGap_40Bit))
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Carrier_Sense
+ * @{
+ */
+#define ETH_CarrierSense_Enable ((uint32_t)0x00000000)
+#define ETH_CarrierSense_Disable ((uint32_t)0x00010000)
+#define IS_ETH_CARRIER_SENSE(CMD) (((CMD) == ETH_CarrierSense_Enable) || \
+ ((CMD) == ETH_CarrierSense_Disable))
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Speed
+ * @{
+ */
+#define ETH_Speed_10M ((uint32_t)0x00000000)
+#define ETH_Speed_100M ((uint32_t)0x00004000)
+#define IS_ETH_SPEED(SPEED) (((SPEED) == ETH_Speed_10M) || \
+ ((SPEED) == ETH_Speed_100M))
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Receive_Own
+ * @{
+ */
+#define ETH_ReceiveOwn_Enable ((uint32_t)0x00000000)
+#define ETH_ReceiveOwn_Disable ((uint32_t)0x00002000)
+#define IS_ETH_RECEIVE_OWN(CMD) (((CMD) == ETH_ReceiveOwn_Enable) || \
+ ((CMD) == ETH_ReceiveOwn_Disable))
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Loop_Back_Mode
+ * @{
+ */
+#define ETH_LoopbackMode_Enable ((uint32_t)0x00001000)
+#define ETH_LoopbackMode_Disable ((uint32_t)0x00000000)
+#define IS_ETH_LOOPBACK_MODE(CMD) (((CMD) == ETH_LoopbackMode_Enable) || \
+ ((CMD) == ETH_LoopbackMode_Disable))
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Duplex_Mode
+ * @{
+ */
+#define ETH_Mode_FullDuplex ((uint32_t)0x00000800)
+#define ETH_Mode_HalfDuplex ((uint32_t)0x00000000)
+#define IS_ETH_DUPLEX_MODE(MODE) (((MODE) == ETH_Mode_FullDuplex) || \
+ ((MODE) == ETH_Mode_HalfDuplex))
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Checksum_Offload
+ * @{
+ */
+#define ETH_ChecksumOffload_Enable ((uint32_t)0x00000400)
+#define ETH_ChecksumOffload_Disable ((uint32_t)0x00000000)
+#define IS_ETH_CHECKSUM_OFFLOAD(CMD) (((CMD) == ETH_ChecksumOffload_Enable) || \
+ ((CMD) == ETH_ChecksumOffload_Disable))
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Retry_Transmission
+ * @{
+ */
+#define ETH_RetryTransmission_Enable ((uint32_t)0x00000000)
+#define ETH_RetryTransmission_Disable ((uint32_t)0x00000200)
+#define IS_ETH_RETRY_TRANSMISSION(CMD) (((CMD) == ETH_RetryTransmission_Enable) || \
+ ((CMD) == ETH_RetryTransmission_Disable))
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Automatic_Pad_CRC_Strip
+ * @{
+ */
+#define ETH_AutomaticPadCRCStrip_Enable ((uint32_t)0x00000080)
+#define ETH_AutomaticPadCRCStrip_Disable ((uint32_t)0x00000000)
+#define IS_ETH_AUTOMATIC_PADCRC_STRIP(CMD) (((CMD) == ETH_AutomaticPadCRCStrip_Enable) || \
+ ((CMD) == ETH_AutomaticPadCRCStrip_Disable))
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Back_Off_Limit
+ * @{
+ */
+#define ETH_BackOffLimit_10 ((uint32_t)0x00000000)
+#define ETH_BackOffLimit_8 ((uint32_t)0x00000020)
+#define ETH_BackOffLimit_4 ((uint32_t)0x00000040)
+#define ETH_BackOffLimit_1 ((uint32_t)0x00000060)
+#define IS_ETH_BACKOFF_LIMIT(LIMIT) (((LIMIT) == ETH_BackOffLimit_10) || \
+ ((LIMIT) == ETH_BackOffLimit_8) || \
+ ((LIMIT) == ETH_BackOffLimit_4) || \
+ ((LIMIT) == ETH_BackOffLimit_1))
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Deferral_Check
+ * @{
+ */
+#define ETH_DeferralCheck_Enable ((uint32_t)0x00000010)
+#define ETH_DeferralCheck_Disable ((uint32_t)0x00000000)
+#define IS_ETH_DEFERRAL_CHECK(CMD) (((CMD) == ETH_DeferralCheck_Enable) || \
+ ((CMD) == ETH_DeferralCheck_Disable))
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Receive_All
+ * @{
+ */
+#define ETH_ReceiveAll_Enable ((uint32_t)0x80000000)
+#define ETH_ReceiveAll_Disable ((uint32_t)0x00000000)
+#define IS_ETH_RECEIVE_ALL(CMD) (((CMD) == ETH_ReceiveAll_Enable) || \
+ ((CMD) == ETH_ReceiveAll_Disable))
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Source_Addr_Filter
+ * @{
+ */
+#define ETH_SourceAddrFilter_Normal_Enable ((uint32_t)0x00000200)
+#define ETH_SourceAddrFilter_Inverse_Enable ((uint32_t)0x00000300)
+#define ETH_SourceAddrFilter_Disable ((uint32_t)0x00000000)
+#define IS_ETH_SOURCE_ADDR_FILTER(CMD) (((CMD) == ETH_SourceAddrFilter_Normal_Enable) || \
+ ((CMD) == ETH_SourceAddrFilter_Inverse_Enable) || \
+ ((CMD) == ETH_SourceAddrFilter_Disable))
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Pass_Control_Frames
+ * @{
+ */
+#define ETH_PassControlFrames_BlockAll ((uint32_t)0x00000040) /*!< MAC filters all control frames from reaching the application */
+#define ETH_PassControlFrames_ForwardAll ((uint32_t)0x00000080) /*!< MAC forwards all control frames to application even if they fail the Address Filter */
+#define ETH_PassControlFrames_ForwardPassedAddrFilter ((uint32_t)0x000000C0) /*!< MAC forwards control frames that pass the Address Filter. */
+#define IS_ETH_CONTROL_FRAMES(PASS) (((PASS) == ETH_PassControlFrames_BlockAll) || \
+ ((PASS) == ETH_PassControlFrames_ForwardAll) || \
+ ((PASS) == ETH_PassControlFrames_ForwardPassedAddrFilter))
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Broadcast_Frames_Reception
+ * @{
+ */
+#define ETH_BroadcastFramesReception_Enable ((uint32_t)0x00000000)
+#define ETH_BroadcastFramesReception_Disable ((uint32_t)0x00000020)
+#define IS_ETH_BROADCAST_FRAMES_RECEPTION(CMD) (((CMD) == ETH_BroadcastFramesReception_Enable) || \
+ ((CMD) == ETH_BroadcastFramesReception_Disable))
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Destination_Addr_Filter
+ * @{
+ */
+#define ETH_DestinationAddrFilter_Normal ((uint32_t)0x00000000)
+#define ETH_DestinationAddrFilter_Inverse ((uint32_t)0x00000008)
+#define IS_ETH_DESTINATION_ADDR_FILTER(FILTER) (((FILTER) == ETH_DestinationAddrFilter_Normal) || \
+ ((FILTER) == ETH_DestinationAddrFilter_Inverse))
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Promiscuous_Mode
+ * @{
+ */
+#define ETH_PromiscuousMode_Enable ((uint32_t)0x00000001)
+#define ETH_PromiscuousMode_Disable ((uint32_t)0x00000000)
+#define IS_ETH_PROMISCUOUS_MODE(CMD) (((CMD) == ETH_PromiscuousMode_Enable) || \
+ ((CMD) == ETH_PromiscuousMode_Disable))
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Multicast_Frames_Filter
+ * @{
+ */
+#define ETH_MulticastFramesFilter_PerfectHashTable ((uint32_t)0x00000404)
+#define ETH_MulticastFramesFilter_HashTable ((uint32_t)0x00000004)
+#define ETH_MulticastFramesFilter_Perfect ((uint32_t)0x00000000)
+#define ETH_MulticastFramesFilter_None ((uint32_t)0x00000010)
+#define IS_ETH_MULTICAST_FRAMES_FILTER(FILTER) (((FILTER) == ETH_MulticastFramesFilter_PerfectHashTable) || \
+ ((FILTER) == ETH_MulticastFramesFilter_HashTable) || \
+ ((FILTER) == ETH_MulticastFramesFilter_Perfect) || \
+ ((FILTER) == ETH_MulticastFramesFilter_None))
+
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Unicast_Frames_Filter
+ * @{
+ */
+#define ETH_UnicastFramesFilter_PerfectHashTable ((uint32_t)0x00000402)
+#define ETH_UnicastFramesFilter_HashTable ((uint32_t)0x00000002)
+#define ETH_UnicastFramesFilter_Perfect ((uint32_t)0x00000000)
+#define IS_ETH_UNICAST_FRAMES_FILTER(FILTER) (((FILTER) == ETH_UnicastFramesFilter_PerfectHashTable) || \
+ ((FILTER) == ETH_UnicastFramesFilter_HashTable) || \
+ ((FILTER) == ETH_UnicastFramesFilter_Perfect))
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Pause_Time
+ * @{
+ */
+#define IS_ETH_PAUSE_TIME(TIME) ((TIME) <= 0xFFFF)
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Zero_Quanta_Pause
+ * @{
+ */
+#define ETH_ZeroQuantaPause_Enable ((uint32_t)0x00000000)
+#define ETH_ZeroQuantaPause_Disable ((uint32_t)0x00000080)
+#define IS_ETH_ZEROQUANTA_PAUSE(CMD) (((CMD) == ETH_ZeroQuantaPause_Enable) || \
+ ((CMD) == ETH_ZeroQuantaPause_Disable))
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Pause_Low_Threshold
+ * @{
+ */
+#define ETH_PauseLowThreshold_Minus4 ((uint32_t)0x00000000) /*!< Pause time minus 4 slot times */
+#define ETH_PauseLowThreshold_Minus28 ((uint32_t)0x00000010) /*!< Pause time minus 28 slot times */
+#define ETH_PauseLowThreshold_Minus144 ((uint32_t)0x00000020) /*!< Pause time minus 144 slot times */
+#define ETH_PauseLowThreshold_Minus256 ((uint32_t)0x00000030) /*!< Pause time minus 256 slot times */
+#define IS_ETH_PAUSE_LOW_THRESHOLD(THRESHOLD) (((THRESHOLD) == ETH_PauseLowThreshold_Minus4) || \
+ ((THRESHOLD) == ETH_PauseLowThreshold_Minus28) || \
+ ((THRESHOLD) == ETH_PauseLowThreshold_Minus144) || \
+ ((THRESHOLD) == ETH_PauseLowThreshold_Minus256))
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Unicast_Pause_Frame_Detect
+ * @{
+ */
+#define ETH_UnicastPauseFrameDetect_Enable ((uint32_t)0x00000008)
+#define ETH_UnicastPauseFrameDetect_Disable ((uint32_t)0x00000000)
+#define IS_ETH_UNICAST_PAUSE_FRAME_DETECT(CMD) (((CMD) == ETH_UnicastPauseFrameDetect_Enable) || \
+ ((CMD) == ETH_UnicastPauseFrameDetect_Disable))
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Receive_Flow_Control
+ * @{
+ */
+#define ETH_ReceiveFlowControl_Enable ((uint32_t)0x00000004)
+#define ETH_ReceiveFlowControl_Disable ((uint32_t)0x00000000)
+#define IS_ETH_RECEIVE_FLOWCONTROL(CMD) (((CMD) == ETH_ReceiveFlowControl_Enable) || \
+ ((CMD) == ETH_ReceiveFlowControl_Disable))
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Transmit_Flow_Control
+ * @{
+ */
+#define ETH_TransmitFlowControl_Enable ((uint32_t)0x00000002)
+#define ETH_TransmitFlowControl_Disable ((uint32_t)0x00000000)
+#define IS_ETH_TRANSMIT_FLOWCONTROL(CMD) (((CMD) == ETH_TransmitFlowControl_Enable) || \
+ ((CMD) == ETH_TransmitFlowControl_Disable))
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_VLAN_Tag_Comparison
+ * @{
+ */
+#define ETH_VLANTagComparison_12Bit ((uint32_t)0x00010000)
+#define ETH_VLANTagComparison_16Bit ((uint32_t)0x00000000)
+#define IS_ETH_VLAN_TAG_COMPARISON(COMPARISON) (((COMPARISON) == ETH_VLANTagComparison_12Bit) || \
+ ((COMPARISON) == ETH_VLANTagComparison_16Bit))
+#define IS_ETH_VLAN_TAG_IDENTIFIER(IDENTIFIER) ((IDENTIFIER) <= 0xFFFF)
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_MAC_Flags
+ * @{
+ */
+#define ETH_MAC_FLAG_TST ((uint32_t)0x00000200) /*!< Time stamp trigger flag (on MAC) */
+#define ETH_MAC_FLAG_MMCT ((uint32_t)0x00000040) /*!< MMC transmit flag */
+#define ETH_MAC_FLAG_MMCR ((uint32_t)0x00000020) /*!< MMC receive flag */
+#define ETH_MAC_FLAG_MMC ((uint32_t)0x00000010) /*!< MMC flag (on MAC) */
+#define ETH_MAC_FLAG_PMT ((uint32_t)0x00000008) /*!< PMT flag (on MAC) */
+#define IS_ETH_MAC_GET_FLAG(FLAG) (((FLAG) == ETH_MAC_FLAG_TST) || ((FLAG) == ETH_MAC_FLAG_MMCT) || \
+ ((FLAG) == ETH_MAC_FLAG_MMCR) || ((FLAG) == ETH_MAC_FLAG_MMC) || \
+ ((FLAG) == ETH_MAC_FLAG_PMT))
+/**
+ * @}
+ */
+
+/** @defgroup ETH_MAC_Interrupts
+ * @{
+ */
+#define ETH_MAC_IT_TST ((uint32_t)0x00000200) /*!< Time stamp trigger interrupt (on MAC) */
+#define ETH_MAC_IT_MMCT ((uint32_t)0x00000040) /*!< MMC transmit interrupt */
+#define ETH_MAC_IT_MMCR ((uint32_t)0x00000020) /*!< MMC receive interrupt */
+#define ETH_MAC_IT_MMC ((uint32_t)0x00000010) /*!< MMC interrupt (on MAC) */
+#define ETH_MAC_IT_PMT ((uint32_t)0x00000008) /*!< PMT interrupt (on MAC) */
+#define IS_ETH_MAC_IT(IT) ((((IT) & (uint32_t)0xFFFFFDF7) == 0x00) && ((IT) != 0x00))
+#define IS_ETH_MAC_GET_IT(IT) (((IT) == ETH_MAC_IT_TST) || ((IT) == ETH_MAC_IT_MMCT) || \
+ ((IT) == ETH_MAC_IT_MMCR) || ((IT) == ETH_MAC_IT_MMC) || \
+ ((IT) == ETH_MAC_IT_PMT))
+/**
+ * @}
+ */
+
+/** @defgroup ETH_MAC_addresses
+ * @{
+ */
+#define ETH_MAC_Address0 ((uint32_t)0x00000000)
+#define ETH_MAC_Address1 ((uint32_t)0x00000008)
+#define ETH_MAC_Address2 ((uint32_t)0x00000010)
+#define ETH_MAC_Address3 ((uint32_t)0x00000018)
+#define IS_ETH_MAC_ADDRESS0123(ADDRESS) (((ADDRESS) == ETH_MAC_Address0) || \
+ ((ADDRESS) == ETH_MAC_Address1) || \
+ ((ADDRESS) == ETH_MAC_Address2) || \
+ ((ADDRESS) == ETH_MAC_Address3))
+#define IS_ETH_MAC_ADDRESS123(ADDRESS) (((ADDRESS) == ETH_MAC_Address1) || \
+ ((ADDRESS) == ETH_MAC_Address2) || \
+ ((ADDRESS) == ETH_MAC_Address3))
+/**
+ * @}
+ */
+
+/** @defgroup ETH_MAC_addresses_filter_SA_DA_filed_of_received_frames
+ * @{
+ */
+#define ETH_MAC_AddressFilter_SA ((uint32_t)0x00000000)
+#define ETH_MAC_AddressFilter_DA ((uint32_t)0x00000008)
+#define IS_ETH_MAC_ADDRESS_FILTER(FILTER) (((FILTER) == ETH_MAC_AddressFilter_SA) || \
+ ((FILTER) == ETH_MAC_AddressFilter_DA))
+/**
+ * @}
+ */
+
+/** @defgroup ETH_MAC_addresses_filter_Mask_bytes
+ * @{
+ */
+#define ETH_MAC_AddressMask_Byte6 ((uint32_t)0x20000000) /*!< Mask MAC Address high reg bits [15:8] */
+#define ETH_MAC_AddressMask_Byte5 ((uint32_t)0x10000000) /*!< Mask MAC Address high reg bits [7:0] */
+#define ETH_MAC_AddressMask_Byte4 ((uint32_t)0x08000000) /*!< Mask MAC Address low reg bits [31:24] */
+#define ETH_MAC_AddressMask_Byte3 ((uint32_t)0x04000000) /*!< Mask MAC Address low reg bits [23:16] */
+#define ETH_MAC_AddressMask_Byte2 ((uint32_t)0x02000000) /*!< Mask MAC Address low reg bits [15:8] */
+#define ETH_MAC_AddressMask_Byte1 ((uint32_t)0x01000000) /*!< Mask MAC Address low reg bits [70] */
+#define IS_ETH_MAC_ADDRESS_MASK(MASK) (((MASK) == ETH_MAC_AddressMask_Byte6) || \
+ ((MASK) == ETH_MAC_AddressMask_Byte5) || \
+ ((MASK) == ETH_MAC_AddressMask_Byte4) || \
+ ((MASK) == ETH_MAC_AddressMask_Byte3) || \
+ ((MASK) == ETH_MAC_AddressMask_Byte2) || \
+ ((MASK) == ETH_MAC_AddressMask_Byte1))
+
+/**--------------------------------------------------------------------------**/
+/**
+ * @brief Ethernet DMA Descriptors defines
+ */
+/**--------------------------------------------------------------------------**/
+/**
+ * @}
+ */
+
+/** @defgroup ETH_DMA_Tx_descriptor_flags
+ * @{
+ */
+#define IS_ETH_DMATxDESC_GET_FLAG(FLAG) (((FLAG) == ETH_DMATxDesc_OWN) || \
+ ((FLAG) == ETH_DMATxDesc_IC) || \
+ ((FLAG) == ETH_DMATxDesc_LS) || \
+ ((FLAG) == ETH_DMATxDesc_FS) || \
+ ((FLAG) == ETH_DMATxDesc_DC) || \
+ ((FLAG) == ETH_DMATxDesc_DP) || \
+ ((FLAG) == ETH_DMATxDesc_TTSE) || \
+ ((FLAG) == ETH_DMATxDesc_TER) || \
+ ((FLAG) == ETH_DMATxDesc_TCH) || \
+ ((FLAG) == ETH_DMATxDesc_TTSS) || \
+ ((FLAG) == ETH_DMATxDesc_IHE) || \
+ ((FLAG) == ETH_DMATxDesc_ES) || \
+ ((FLAG) == ETH_DMATxDesc_JT) || \
+ ((FLAG) == ETH_DMATxDesc_FF) || \
+ ((FLAG) == ETH_DMATxDesc_PCE) || \
+ ((FLAG) == ETH_DMATxDesc_LCA) || \
+ ((FLAG) == ETH_DMATxDesc_NC) || \
+ ((FLAG) == ETH_DMATxDesc_LCO) || \
+ ((FLAG) == ETH_DMATxDesc_EC) || \
+ ((FLAG) == ETH_DMATxDesc_VF) || \
+ ((FLAG) == ETH_DMATxDesc_CC) || \
+ ((FLAG) == ETH_DMATxDesc_ED) || \
+ ((FLAG) == ETH_DMATxDesc_UF) || \
+ ((FLAG) == ETH_DMATxDesc_DB))
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_DMA_Tx_descriptor_segment
+ * @{
+ */
+#define ETH_DMATxDesc_LastSegment ((uint32_t)0x40000000) /*!< Last Segment */
+#define ETH_DMATxDesc_FirstSegment ((uint32_t)0x20000000) /*!< First Segment */
+#define IS_ETH_DMA_TXDESC_SEGMENT(SEGMENT) (((SEGMENT) == ETH_DMATxDesc_LastSegment) || \
+ ((SEGMENT) == ETH_DMATxDesc_FirstSegment))
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_DMA_Tx_descriptor_Checksum_Insertion_Control
+ * @{
+ */
+#define ETH_DMATxDesc_ChecksumByPass ((uint32_t)0x00000000) /*!< Checksum engine bypass */
+#define ETH_DMATxDesc_ChecksumIPV4Header ((uint32_t)0x00400000) /*!< IPv4 header checksum insertion */
+#define ETH_DMATxDesc_ChecksumTCPUDPICMPSegment ((uint32_t)0x00800000) /*!< TCP/UDP/ICMP checksum insertion. Pseudo header checksum is assumed to be present */
+#define ETH_DMATxDesc_ChecksumTCPUDPICMPFull ((uint32_t)0x00C00000) /*!< TCP/UDP/ICMP checksum fully in hardware including pseudo header */
+#define IS_ETH_DMA_TXDESC_CHECKSUM(CHECKSUM) (((CHECKSUM) == ETH_DMATxDesc_ChecksumByPass) || \
+ ((CHECKSUM) == ETH_DMATxDesc_ChecksumIPV4Header) || \
+ ((CHECKSUM) == ETH_DMATxDesc_ChecksumTCPUDPICMPSegment) || \
+ ((CHECKSUM) == ETH_DMATxDesc_ChecksumTCPUDPICMPFull))
+/**
+ * @brief ETH DMA Tx Desciptor buffer size
+ */
+#define IS_ETH_DMATxDESC_BUFFER_SIZE(SIZE) ((SIZE) <= 0x1FFF)
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_DMA_Rx_descriptor_flags
+ * @{
+ */
+#define IS_ETH_DMARxDESC_GET_FLAG(FLAG) (((FLAG) == ETH_DMARxDesc_OWN) || \
+ ((FLAG) == ETH_DMARxDesc_AFM) || \
+ ((FLAG) == ETH_DMARxDesc_ES) || \
+ ((FLAG) == ETH_DMARxDesc_DE) || \
+ ((FLAG) == ETH_DMARxDesc_SAF) || \
+ ((FLAG) == ETH_DMARxDesc_LE) || \
+ ((FLAG) == ETH_DMARxDesc_OE) || \
+ ((FLAG) == ETH_DMARxDesc_VLAN) || \
+ ((FLAG) == ETH_DMARxDesc_FS) || \
+ ((FLAG) == ETH_DMARxDesc_LS) || \
+ ((FLAG) == ETH_DMARxDesc_IPV4HCE) || \
+ ((FLAG) == ETH_DMARxDesc_LC) || \
+ ((FLAG) == ETH_DMARxDesc_FT) || \
+ ((FLAG) == ETH_DMARxDesc_RWT) || \
+ ((FLAG) == ETH_DMARxDesc_RE) || \
+ ((FLAG) == ETH_DMARxDesc_DBE) || \
+ ((FLAG) == ETH_DMARxDesc_CE) || \
+ ((FLAG) == ETH_DMARxDesc_MAMPCE))
+
+/* ETHERNET DMA PTP Rx descriptor extended flags --------------------------------*/
+#define IS_ETH_DMAPTPRxDESC_GET_EXTENDED_FLAG(FLAG) (((FLAG) == ETH_DMAPTPRxDesc_PTPV) || \
+ ((FLAG) == ETH_DMAPTPRxDesc_PTPFT) || \
+ ((FLAG) == ETH_DMAPTPRxDesc_PTPMT) || \
+ ((FLAG) == ETH_DMAPTPRxDesc_IPV6PR) || \
+ ((FLAG) == ETH_DMAPTPRxDesc_IPV4PR) || \
+ ((FLAG) == ETH_DMAPTPRxDesc_IPCB) || \
+ ((FLAG) == ETH_DMAPTPRxDesc_IPPE) || \
+ ((FLAG) == ETH_DMAPTPRxDesc_IPHE) || \
+ ((FLAG) == ETH_DMAPTPRxDesc_IPPT))
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_DMA_Rx_descriptor_buffers_
+ * @{
+ */
+#define ETH_DMARxDesc_Buffer1 ((uint32_t)0x00000000) /*!< DMA Rx Desc Buffer1 */
+#define ETH_DMARxDesc_Buffer2 ((uint32_t)0x00000001) /*!< DMA Rx Desc Buffer2 */
+#define IS_ETH_DMA_RXDESC_BUFFER(BUFFER) (((BUFFER) == ETH_DMARxDesc_Buffer1) || \
+ ((BUFFER) == ETH_DMARxDesc_Buffer2))
+
+/**--------------------------------------------------------------------------**/
+/**
+ * @brief Ethernet DMA defines
+ */
+/**--------------------------------------------------------------------------**/
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Drop_TCP_IP_Checksum_Error_Frame
+ * @{
+ */
+#define ETH_DropTCPIPChecksumErrorFrame_Enable ((uint32_t)0x00000000)
+#define ETH_DropTCPIPChecksumErrorFrame_Disable ((uint32_t)0x04000000)
+#define IS_ETH_DROP_TCPIP_CHECKSUM_FRAME(CMD) (((CMD) == ETH_DropTCPIPChecksumErrorFrame_Enable) || \
+ ((CMD) == ETH_DropTCPIPChecksumErrorFrame_Disable))
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Receive_Store_Forward
+ * @{
+ */
+#define ETH_ReceiveStoreForward_Enable ((uint32_t)0x02000000)
+#define ETH_ReceiveStoreForward_Disable ((uint32_t)0x00000000)
+#define IS_ETH_RECEIVE_STORE_FORWARD(CMD) (((CMD) == ETH_ReceiveStoreForward_Enable) || \
+ ((CMD) == ETH_ReceiveStoreForward_Disable))
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Flush_Received_Frame
+ * @{
+ */
+#define ETH_FlushReceivedFrame_Enable ((uint32_t)0x00000000)
+#define ETH_FlushReceivedFrame_Disable ((uint32_t)0x01000000)
+#define IS_ETH_FLUSH_RECEIVE_FRAME(CMD) (((CMD) == ETH_FlushReceivedFrame_Enable) || \
+ ((CMD) == ETH_FlushReceivedFrame_Disable))
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Transmit_Store_Forward
+ * @{
+ */
+#define ETH_TransmitStoreForward_Enable ((uint32_t)0x00200000)
+#define ETH_TransmitStoreForward_Disable ((uint32_t)0x00000000)
+#define IS_ETH_TRANSMIT_STORE_FORWARD(CMD) (((CMD) == ETH_TransmitStoreForward_Enable) || \
+ ((CMD) == ETH_TransmitStoreForward_Disable))
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Transmit_Threshold_Control
+ * @{
+ */
+#define ETH_TransmitThresholdControl_64Bytes ((uint32_t)0x00000000) /*!< threshold level of the MTL Transmit FIFO is 64 Bytes */
+#define ETH_TransmitThresholdControl_128Bytes ((uint32_t)0x00004000) /*!< threshold level of the MTL Transmit FIFO is 128 Bytes */
+#define ETH_TransmitThresholdControl_192Bytes ((uint32_t)0x00008000) /*!< threshold level of the MTL Transmit FIFO is 192 Bytes */
+#define ETH_TransmitThresholdControl_256Bytes ((uint32_t)0x0000C000) /*!< threshold level of the MTL Transmit FIFO is 256 Bytes */
+#define ETH_TransmitThresholdControl_40Bytes ((uint32_t)0x00010000) /*!< threshold level of the MTL Transmit FIFO is 40 Bytes */
+#define ETH_TransmitThresholdControl_32Bytes ((uint32_t)0x00014000) /*!< threshold level of the MTL Transmit FIFO is 32 Bytes */
+#define ETH_TransmitThresholdControl_24Bytes ((uint32_t)0x00018000) /*!< threshold level of the MTL Transmit FIFO is 24 Bytes */
+#define ETH_TransmitThresholdControl_16Bytes ((uint32_t)0x0001C000) /*!< threshold level of the MTL Transmit FIFO is 16 Bytes */
+#define IS_ETH_TRANSMIT_THRESHOLD_CONTROL(THRESHOLD) (((THRESHOLD) == ETH_TransmitThresholdControl_64Bytes) || \
+ ((THRESHOLD) == ETH_TransmitThresholdControl_128Bytes) || \
+ ((THRESHOLD) == ETH_TransmitThresholdControl_192Bytes) || \
+ ((THRESHOLD) == ETH_TransmitThresholdControl_256Bytes) || \
+ ((THRESHOLD) == ETH_TransmitThresholdControl_40Bytes) || \
+ ((THRESHOLD) == ETH_TransmitThresholdControl_32Bytes) || \
+ ((THRESHOLD) == ETH_TransmitThresholdControl_24Bytes) || \
+ ((THRESHOLD) == ETH_TransmitThresholdControl_16Bytes))
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Forward_Error_Frames
+ * @{
+ */
+#define ETH_ForwardErrorFrames_Enable ((uint32_t)0x00000080)
+#define ETH_ForwardErrorFrames_Disable ((uint32_t)0x00000000)
+#define IS_ETH_FORWARD_ERROR_FRAMES(CMD) (((CMD) == ETH_ForwardErrorFrames_Enable) || \
+ ((CMD) == ETH_ForwardErrorFrames_Disable))
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Forward_Undersized_Good_Frames
+ * @{
+ */
+#define ETH_ForwardUndersizedGoodFrames_Enable ((uint32_t)0x00000040)
+#define ETH_ForwardUndersizedGoodFrames_Disable ((uint32_t)0x00000000)
+#define IS_ETH_FORWARD_UNDERSIZED_GOOD_FRAMES(CMD) (((CMD) == ETH_ForwardUndersizedGoodFrames_Enable) || \
+ ((CMD) == ETH_ForwardUndersizedGoodFrames_Disable))
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Receive_Threshold_Control
+ * @{
+ */
+#define ETH_ReceiveThresholdControl_64Bytes ((uint32_t)0x00000000) /*!< threshold level of the MTL Receive FIFO is 64 Bytes */
+#define ETH_ReceiveThresholdControl_32Bytes ((uint32_t)0x00000008) /*!< threshold level of the MTL Receive FIFO is 32 Bytes */
+#define ETH_ReceiveThresholdControl_96Bytes ((uint32_t)0x00000010) /*!< threshold level of the MTL Receive FIFO is 96 Bytes */
+#define ETH_ReceiveThresholdControl_128Bytes ((uint32_t)0x00000018) /*!< threshold level of the MTL Receive FIFO is 128 Bytes */
+#define IS_ETH_RECEIVE_THRESHOLD_CONTROL(THRESHOLD) (((THRESHOLD) == ETH_ReceiveThresholdControl_64Bytes) || \
+ ((THRESHOLD) == ETH_ReceiveThresholdControl_32Bytes) || \
+ ((THRESHOLD) == ETH_ReceiveThresholdControl_96Bytes) || \
+ ((THRESHOLD) == ETH_ReceiveThresholdControl_128Bytes))
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Second_Frame_Operate
+ * @{
+ */
+#define ETH_SecondFrameOperate_Enable ((uint32_t)0x00000004)
+#define ETH_SecondFrameOperate_Disable ((uint32_t)0x00000000)
+#define IS_ETH_SECOND_FRAME_OPERATE(CMD) (((CMD) == ETH_SecondFrameOperate_Enable) || \
+ ((CMD) == ETH_SecondFrameOperate_Disable))
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Address_Aligned_Beats
+ * @{
+ */
+#define ETH_AddressAlignedBeats_Enable ((uint32_t)0x02000000)
+#define ETH_AddressAlignedBeats_Disable ((uint32_t)0x00000000)
+#define IS_ETH_ADDRESS_ALIGNED_BEATS(CMD) (((CMD) == ETH_AddressAlignedBeats_Enable) || \
+ ((CMD) == ETH_AddressAlignedBeats_Disable))
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Fixed_Burst
+ * @{
+ */
+#define ETH_FixedBurst_Enable ((uint32_t)0x00010000)
+#define ETH_FixedBurst_Disable ((uint32_t)0x00000000)
+#define IS_ETH_FIXED_BURST(CMD) (((CMD) == ETH_FixedBurst_Enable) || \
+ ((CMD) == ETH_FixedBurst_Disable))
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Rx_DMA_Burst_Length
+ * @{
+ */
+#define ETH_RxDMABurstLength_1Beat ((uint32_t)0x00020000) /*!< maximum number of beats to be transferred in one RxDMA transaction is 1 */
+#define ETH_RxDMABurstLength_2Beat ((uint32_t)0x00040000) /*!< maximum number of beats to be transferred in one RxDMA transaction is 2 */
+#define ETH_RxDMABurstLength_4Beat ((uint32_t)0x00080000) /*!< maximum number of beats to be transferred in one RxDMA transaction is 4 */
+#define ETH_RxDMABurstLength_8Beat ((uint32_t)0x00100000) /*!< maximum number of beats to be transferred in one RxDMA transaction is 8 */
+#define ETH_RxDMABurstLength_16Beat ((uint32_t)0x00200000) /*!< maximum number of beats to be transferred in one RxDMA transaction is 16 */
+#define ETH_RxDMABurstLength_32Beat ((uint32_t)0x00400000) /*!< maximum number of beats to be transferred in one RxDMA transaction is 32 */
+#define ETH_RxDMABurstLength_4xPBL_4Beat ((uint32_t)0x01020000) /*!< maximum number of beats to be transferred in one RxDMA transaction is 4 */
+#define ETH_RxDMABurstLength_4xPBL_8Beat ((uint32_t)0x01040000) /*!< maximum number of beats to be transferred in one RxDMA transaction is 8 */
+#define ETH_RxDMABurstLength_4xPBL_16Beat ((uint32_t)0x01080000) /*!< maximum number of beats to be transferred in one RxDMA transaction is 16 */
+#define ETH_RxDMABurstLength_4xPBL_32Beat ((uint32_t)0x01100000) /*!< maximum number of beats to be transferred in one RxDMA transaction is 32 */
+#define ETH_RxDMABurstLength_4xPBL_64Beat ((uint32_t)0x01200000) /*!< maximum number of beats to be transferred in one RxDMA transaction is 64 */
+#define ETH_RxDMABurstLength_4xPBL_128Beat ((uint32_t)0x01400000) /*!< maximum number of beats to be transferred in one RxDMA transaction is 128 */
+
+#define IS_ETH_RXDMA_BURST_LENGTH(LENGTH) (((LENGTH) == ETH_RxDMABurstLength_1Beat) || \
+ ((LENGTH) == ETH_RxDMABurstLength_2Beat) || \
+ ((LENGTH) == ETH_RxDMABurstLength_4Beat) || \
+ ((LENGTH) == ETH_RxDMABurstLength_8Beat) || \
+ ((LENGTH) == ETH_RxDMABurstLength_16Beat) || \
+ ((LENGTH) == ETH_RxDMABurstLength_32Beat) || \
+ ((LENGTH) == ETH_RxDMABurstLength_4xPBL_4Beat) || \
+ ((LENGTH) == ETH_RxDMABurstLength_4xPBL_8Beat) || \
+ ((LENGTH) == ETH_RxDMABurstLength_4xPBL_16Beat) || \
+ ((LENGTH) == ETH_RxDMABurstLength_4xPBL_32Beat) || \
+ ((LENGTH) == ETH_RxDMABurstLength_4xPBL_64Beat) || \
+ ((LENGTH) == ETH_RxDMABurstLength_4xPBL_128Beat))
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Tx_DMA_Burst_Length
+ * @{
+ */
+#define ETH_TxDMABurstLength_1Beat ((uint32_t)0x00000100) /*!< maximum number of beats to be transferred in one TxDMA (or both) transaction is 1 */
+#define ETH_TxDMABurstLength_2Beat ((uint32_t)0x00000200) /*!< maximum number of beats to be transferred in one TxDMA (or both) transaction is 2 */
+#define ETH_TxDMABurstLength_4Beat ((uint32_t)0x00000400) /*!< maximum number of beats to be transferred in one TxDMA (or both) transaction is 4 */
+#define ETH_TxDMABurstLength_8Beat ((uint32_t)0x00000800) /*!< maximum number of beats to be transferred in one TxDMA (or both) transaction is 8 */
+#define ETH_TxDMABurstLength_16Beat ((uint32_t)0x00001000) /*!< maximum number of beats to be transferred in one TxDMA (or both) transaction is 16 */
+#define ETH_TxDMABurstLength_32Beat ((uint32_t)0x00002000) /*!< maximum number of beats to be transferred in one TxDMA (or both) transaction is 32 */
+#define ETH_TxDMABurstLength_4xPBL_4Beat ((uint32_t)0x01000100) /*!< maximum number of beats to be transferred in one TxDMA (or both) transaction is 4 */
+#define ETH_TxDMABurstLength_4xPBL_8Beat ((uint32_t)0x01000200) /*!< maximum number of beats to be transferred in one TxDMA (or both) transaction is 8 */
+#define ETH_TxDMABurstLength_4xPBL_16Beat ((uint32_t)0x01000400) /*!< maximum number of beats to be transferred in one TxDMA (or both) transaction is 16 */
+#define ETH_TxDMABurstLength_4xPBL_32Beat ((uint32_t)0x01000800) /*!< maximum number of beats to be transferred in one TxDMA (or both) transaction is 32 */
+#define ETH_TxDMABurstLength_4xPBL_64Beat ((uint32_t)0x01001000) /*!< maximum number of beats to be transferred in one TxDMA (or both) transaction is 64 */
+#define ETH_TxDMABurstLength_4xPBL_128Beat ((uint32_t)0x01002000) /*!< maximum number of beats to be transferred in one TxDMA (or both) transaction is 128 */
+
+#define IS_ETH_TXDMA_BURST_LENGTH(LENGTH) (((LENGTH) == ETH_TxDMABurstLength_1Beat) || \
+ ((LENGTH) == ETH_TxDMABurstLength_2Beat) || \
+ ((LENGTH) == ETH_TxDMABurstLength_4Beat) || \
+ ((LENGTH) == ETH_TxDMABurstLength_8Beat) || \
+ ((LENGTH) == ETH_TxDMABurstLength_16Beat) || \
+ ((LENGTH) == ETH_TxDMABurstLength_32Beat) || \
+ ((LENGTH) == ETH_TxDMABurstLength_4xPBL_4Beat) || \
+ ((LENGTH) == ETH_TxDMABurstLength_4xPBL_8Beat) || \
+ ((LENGTH) == ETH_TxDMABurstLength_4xPBL_16Beat) || \
+ ((LENGTH) == ETH_TxDMABurstLength_4xPBL_32Beat) || \
+ ((LENGTH) == ETH_TxDMABurstLength_4xPBL_64Beat) || \
+ ((LENGTH) == ETH_TxDMABurstLength_4xPBL_128Beat))
+/**
+ * @brief ETH DMA Descriptor SkipLength
+ */
+#define IS_ETH_DMA_DESC_SKIP_LENGTH(LENGTH) ((LENGTH) <= 0x1F)
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_DMA_Arbitration
+ * @{
+ */
+#define ETH_DMAArbitration_RoundRobin_RxTx_1_1 ((uint32_t)0x00000000)
+#define ETH_DMAArbitration_RoundRobin_RxTx_2_1 ((uint32_t)0x00004000)
+#define ETH_DMAArbitration_RoundRobin_RxTx_3_1 ((uint32_t)0x00008000)
+#define ETH_DMAArbitration_RoundRobin_RxTx_4_1 ((uint32_t)0x0000C000)
+#define ETH_DMAArbitration_RxPriorTx ((uint32_t)0x00000002)
+#define IS_ETH_DMA_ARBITRATION_ROUNDROBIN_RXTX(RATIO) (((RATIO) == ETH_DMAArbitration_RoundRobin_RxTx_1_1) || \
+ ((RATIO) == ETH_DMAArbitration_RoundRobin_RxTx_2_1) || \
+ ((RATIO) == ETH_DMAArbitration_RoundRobin_RxTx_3_1) || \
+ ((RATIO) == ETH_DMAArbitration_RoundRobin_RxTx_4_1) || \
+ ((RATIO) == ETH_DMAArbitration_RxPriorTx))
+/**
+ * @}
+ */
+
+/** @defgroup ETH_DMA_Flags
+ * @{
+ */
+#define ETH_DMA_FLAG_TST ((uint32_t)0x20000000) /*!< Time-stamp trigger interrupt (on DMA) */
+#define ETH_DMA_FLAG_PMT ((uint32_t)0x10000000) /*!< PMT interrupt (on DMA) */
+#define ETH_DMA_FLAG_MMC ((uint32_t)0x08000000) /*!< MMC interrupt (on DMA) */
+#define ETH_DMA_FLAG_DataTransferError ((uint32_t)0x00800000) /*!< Error bits 0-Rx DMA, 1-Tx DMA */
+#define ETH_DMA_FLAG_ReadWriteError ((uint32_t)0x01000000) /*!< Error bits 0-write trnsf, 1-read transfr */
+#define ETH_DMA_FLAG_AccessError ((uint32_t)0x02000000) /*!< Error bits 0-data buffer, 1-desc. access */
+#define ETH_DMA_FLAG_NIS ((uint32_t)0x00010000) /*!< Normal interrupt summary flag */
+#define ETH_DMA_FLAG_AIS ((uint32_t)0x00008000) /*!< Abnormal interrupt summary flag */
+#define ETH_DMA_FLAG_ER ((uint32_t)0x00004000) /*!< Early receive flag */
+#define ETH_DMA_FLAG_FBE ((uint32_t)0x00002000) /*!< Fatal bus error flag */
+#define ETH_DMA_FLAG_ET ((uint32_t)0x00000400) /*!< Early transmit flag */
+#define ETH_DMA_FLAG_RWT ((uint32_t)0x00000200) /*!< Receive watchdog timeout flag */
+#define ETH_DMA_FLAG_RPS ((uint32_t)0x00000100) /*!< Receive process stopped flag */
+#define ETH_DMA_FLAG_RBU ((uint32_t)0x00000080) /*!< Receive buffer unavailable flag */
+#define ETH_DMA_FLAG_R ((uint32_t)0x00000040) /*!< Receive flag */
+#define ETH_DMA_FLAG_TU ((uint32_t)0x00000020) /*!< Underflow flag */
+#define ETH_DMA_FLAG_RO ((uint32_t)0x00000010) /*!< Overflow flag */
+#define ETH_DMA_FLAG_TJT ((uint32_t)0x00000008) /*!< Transmit jabber timeout flag */
+#define ETH_DMA_FLAG_TBU ((uint32_t)0x00000004) /*!< Transmit buffer unavailable flag */
+#define ETH_DMA_FLAG_TPS ((uint32_t)0x00000002) /*!< Transmit process stopped flag */
+#define ETH_DMA_FLAG_T ((uint32_t)0x00000001) /*!< Transmit flag */
+
+#define IS_ETH_DMA_FLAG(FLAG) ((((FLAG) & (uint32_t)0xFFFE1800) == 0x00) && ((FLAG) != 0x00))
+#define IS_ETH_DMA_GET_FLAG(FLAG) (((FLAG) == ETH_DMA_FLAG_TST) || ((FLAG) == ETH_DMA_FLAG_PMT) || \
+ ((FLAG) == ETH_DMA_FLAG_MMC) || ((FLAG) == ETH_DMA_FLAG_DataTransferError) || \
+ ((FLAG) == ETH_DMA_FLAG_ReadWriteError) || ((FLAG) == ETH_DMA_FLAG_AccessError) || \
+ ((FLAG) == ETH_DMA_FLAG_NIS) || ((FLAG) == ETH_DMA_FLAG_AIS) || \
+ ((FLAG) == ETH_DMA_FLAG_ER) || ((FLAG) == ETH_DMA_FLAG_FBE) || \
+ ((FLAG) == ETH_DMA_FLAG_ET) || ((FLAG) == ETH_DMA_FLAG_RWT) || \
+ ((FLAG) == ETH_DMA_FLAG_RPS) || ((FLAG) == ETH_DMA_FLAG_RBU) || \
+ ((FLAG) == ETH_DMA_FLAG_R) || ((FLAG) == ETH_DMA_FLAG_TU) || \
+ ((FLAG) == ETH_DMA_FLAG_RO) || ((FLAG) == ETH_DMA_FLAG_TJT) || \
+ ((FLAG) == ETH_DMA_FLAG_TBU) || ((FLAG) == ETH_DMA_FLAG_TPS) || \
+ ((FLAG) == ETH_DMA_FLAG_T))
+/**
+ * @}
+ */
+
+/** @defgroup ETH_DMA_Interrupts
+ * @{
+ */
+#define ETH_DMA_IT_TST ((uint32_t)0x20000000) /*!< Time-stamp trigger interrupt (on DMA) */
+#define ETH_DMA_IT_PMT ((uint32_t)0x10000000) /*!< PMT interrupt (on DMA) */
+#define ETH_DMA_IT_MMC ((uint32_t)0x08000000) /*!< MMC interrupt (on DMA) */
+#define ETH_DMA_IT_NIS ((uint32_t)0x00010000) /*!< Normal interrupt summary */
+#define ETH_DMA_IT_AIS ((uint32_t)0x00008000) /*!< Abnormal interrupt summary */
+#define ETH_DMA_IT_ER ((uint32_t)0x00004000) /*!< Early receive interrupt */
+#define ETH_DMA_IT_FBE ((uint32_t)0x00002000) /*!< Fatal bus error interrupt */
+#define ETH_DMA_IT_ET ((uint32_t)0x00000400) /*!< Early transmit interrupt */
+#define ETH_DMA_IT_RWT ((uint32_t)0x00000200) /*!< Receive watchdog timeout interrupt */
+#define ETH_DMA_IT_RPS ((uint32_t)0x00000100) /*!< Receive process stopped interrupt */
+#define ETH_DMA_IT_RBU ((uint32_t)0x00000080) /*!< Receive buffer unavailable interrupt */
+#define ETH_DMA_IT_R ((uint32_t)0x00000040) /*!< Receive interrupt */
+#define ETH_DMA_IT_TU ((uint32_t)0x00000020) /*!< Underflow interrupt */
+#define ETH_DMA_IT_RO ((uint32_t)0x00000010) /*!< Overflow interrupt */
+#define ETH_DMA_IT_TJT ((uint32_t)0x00000008) /*!< Transmit jabber timeout interrupt */
+#define ETH_DMA_IT_TBU ((uint32_t)0x00000004) /*!< Transmit buffer unavailable interrupt */
+#define ETH_DMA_IT_TPS ((uint32_t)0x00000002) /*!< Transmit process stopped interrupt */
+#define ETH_DMA_IT_T ((uint32_t)0x00000001) /*!< Transmit interrupt */
+
+#define IS_ETH_DMA_IT(IT) ((((IT) & (uint32_t)0xFFFE1800) == 0x00) && ((IT) != 0x00))
+#define IS_ETH_DMA_GET_IT(IT) (((IT) == ETH_DMA_IT_TST) || ((IT) == ETH_DMA_IT_PMT) || \
+ ((IT) == ETH_DMA_IT_MMC) || ((IT) == ETH_DMA_IT_NIS) || \
+ ((IT) == ETH_DMA_IT_AIS) || ((IT) == ETH_DMA_IT_ER) || \
+ ((IT) == ETH_DMA_IT_FBE) || ((IT) == ETH_DMA_IT_ET) || \
+ ((IT) == ETH_DMA_IT_RWT) || ((IT) == ETH_DMA_IT_RPS) || \
+ ((IT) == ETH_DMA_IT_RBU) || ((IT) == ETH_DMA_IT_R) || \
+ ((IT) == ETH_DMA_IT_TU) || ((IT) == ETH_DMA_IT_RO) || \
+ ((IT) == ETH_DMA_IT_TJT) || ((IT) == ETH_DMA_IT_TBU) || \
+ ((IT) == ETH_DMA_IT_TPS) || ((IT) == ETH_DMA_IT_T))
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_DMA_transmit_process_state_
+ * @{
+ */
+#define ETH_DMA_TransmitProcess_Stopped ((uint32_t)0x00000000) /*!< Stopped - Reset or Stop Tx Command issued */
+#define ETH_DMA_TransmitProcess_Fetching ((uint32_t)0x00100000) /*!< Running - fetching the Tx descriptor */
+#define ETH_DMA_TransmitProcess_Waiting ((uint32_t)0x00200000) /*!< Running - waiting for status */
+#define ETH_DMA_TransmitProcess_Reading ((uint32_t)0x00300000) /*!< Running - reading the data from host memory */
+#define ETH_DMA_TransmitProcess_Suspended ((uint32_t)0x00600000) /*!< Suspended - Tx Descriptor unavailable */
+#define ETH_DMA_TransmitProcess_Closing ((uint32_t)0x00700000) /*!< Running - closing Rx descriptor */
+
+/**
+ * @}
+ */
+
+
+/** @defgroup ETH_DMA_receive_process_state_
+ * @{
+ */
+#define ETH_DMA_ReceiveProcess_Stopped ((uint32_t)0x00000000) /*!< Stopped - Reset or Stop Rx Command issued */
+#define ETH_DMA_ReceiveProcess_Fetching ((uint32_t)0x00020000) /*!< Running - fetching the Rx descriptor */
+#define ETH_DMA_ReceiveProcess_Waiting ((uint32_t)0x00060000) /*!< Running - waiting for packet */
+#define ETH_DMA_ReceiveProcess_Suspended ((uint32_t)0x00080000) /*!< Suspended - Rx Descriptor unavailable */
+#define ETH_DMA_ReceiveProcess_Closing ((uint32_t)0x000A0000) /*!< Running - closing descriptor */
+#define ETH_DMA_ReceiveProcess_Queuing ((uint32_t)0x000E0000) /*!< Running - queuing the receive frame into host memory */
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_DMA_overflow_
+ * @{
+ */
+#define ETH_DMA_Overflow_RxFIFOCounter ((uint32_t)0x10000000) /*!< Overflow bit for FIFO overflow counter */
+#define ETH_DMA_Overflow_MissedFrameCounter ((uint32_t)0x00010000) /*!< Overflow bit for missed frame counter */
+#define IS_ETH_DMA_GET_OVERFLOW(OVERFLOW) (((OVERFLOW) == ETH_DMA_Overflow_RxFIFOCounter) || \
+ ((OVERFLOW) == ETH_DMA_Overflow_MissedFrameCounter))
+
+/**--------------------------------------------------------------------------**/
+/**
+ * @brief Ethernet PMT defines
+ */
+/**--------------------------------------------------------------------------**/
+/**
+ * @}
+ */
+
+/** @defgroup ETH_PMT_Flags
+ * @{
+ */
+#define ETH_PMT_FLAG_WUFFRPR ((uint32_t)0x80000000) /*!< Wake-Up Frame Filter Register Pointer Reset */
+#define ETH_PMT_FLAG_WUFR ((uint32_t)0x00000040) /*!< Wake-Up Frame Received */
+#define ETH_PMT_FLAG_MPR ((uint32_t)0x00000020) /*!< Magic Packet Received */
+#define IS_ETH_PMT_GET_FLAG(FLAG) (((FLAG) == ETH_PMT_FLAG_WUFR) || \
+ ((FLAG) == ETH_PMT_FLAG_MPR))
+
+/**--------------------------------------------------------------------------**/
+/**
+ * @brief Ethernet MMC defines
+ */
+/**--------------------------------------------------------------------------**/
+/**
+ * @}
+ */
+
+/** @defgroup ETH_MMC_Tx_Interrupts
+ * @{
+ */
+#define ETH_MMC_IT_TGF ((uint32_t)0x00200000) /*!< When Tx good frame counter reaches half the maximum value */
+#define ETH_MMC_IT_TGFMSC ((uint32_t)0x00008000) /*!< When Tx good multi col counter reaches half the maximum value */
+#define ETH_MMC_IT_TGFSC ((uint32_t)0x00004000) /*!< When Tx good single col counter reaches half the maximum value */
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_MMC_Rx_Interrupts
+ * @{
+ */
+#define ETH_MMC_IT_RGUF ((uint32_t)0x10020000) /*!< When Rx good unicast frames counter reaches half the maximum value */
+#define ETH_MMC_IT_RFAE ((uint32_t)0x10000040) /*!< When Rx alignment error counter reaches half the maximum value */
+#define ETH_MMC_IT_RFCE ((uint32_t)0x10000020) /*!< When Rx crc error counter reaches half the maximum value */
+#define IS_ETH_MMC_IT(IT) (((((IT) & (uint32_t)0xFFDF3FFF) == 0x00) || (((IT) & (uint32_t)0xEFFDFF9F) == 0x00)) && \
+ ((IT) != 0x00))
+#define IS_ETH_MMC_GET_IT(IT) (((IT) == ETH_MMC_IT_TGF) || ((IT) == ETH_MMC_IT_TGFMSC) || \
+ ((IT) == ETH_MMC_IT_TGFSC) || ((IT) == ETH_MMC_IT_RGUF) || \
+ ((IT) == ETH_MMC_IT_RFAE) || ((IT) == ETH_MMC_IT_RFCE))
+/**
+ * @}
+ */
+
+/** @defgroup ETH_MMC_Registers
+ * @{
+ */
+#define ETH_MMCCR ((uint32_t)0x00000100) /*!< MMC CR register */
+#define ETH_MMCRIR ((uint32_t)0x00000104) /*!< MMC RIR register */
+#define ETH_MMCTIR ((uint32_t)0x00000108) /*!< MMC TIR register */
+#define ETH_MMCRIMR ((uint32_t)0x0000010C) /*!< MMC RIMR register */
+#define ETH_MMCTIMR ((uint32_t)0x00000110) /*!< MMC TIMR register */
+#define ETH_MMCTGFSCCR ((uint32_t)0x0000014C) /*!< MMC TGFSCCR register */
+#define ETH_MMCTGFMSCCR ((uint32_t)0x00000150) /*!< MMC TGFMSCCR register */
+#define ETH_MMCTGFCR ((uint32_t)0x00000168) /*!< MMC TGFCR register */
+#define ETH_MMCRFCECR ((uint32_t)0x00000194) /*!< MMC RFCECR register */
+#define ETH_MMCRFAECR ((uint32_t)0x00000198) /*!< MMC RFAECR register */
+#define ETH_MMCRGUFCR ((uint32_t)0x000001C4) /*!< MMC RGUFCR register */
+
+/**
+ * @brief ETH MMC registers
+ */
+#define IS_ETH_MMC_REGISTER(REG) (((REG) == ETH_MMCCR) || ((REG) == ETH_MMCRIR) || \
+ ((REG) == ETH_MMCTIR) || ((REG) == ETH_MMCRIMR) || \
+ ((REG) == ETH_MMCTIMR) || ((REG) == ETH_MMCTGFSCCR) || \
+ ((REG) == ETH_MMCTGFMSCCR) || ((REG) == ETH_MMCTGFCR) || \
+ ((REG) == ETH_MMCRFCECR) || ((REG) == ETH_MMCRFAECR) || \
+ ((REG) == ETH_MMCRGUFCR))
+
+/**--------------------------------------------------------------------------**/
+/**
+ * @brief Ethernet PTP defines
+ */
+/**--------------------------------------------------------------------------**/
+/**
+ * @}
+ */
+
+/** @defgroup ETH_PTP_time_update_method
+ * @{
+ */
+#define ETH_PTP_FineUpdate ((uint32_t)0x00000001) /*!< Fine Update method */
+#define ETH_PTP_CoarseUpdate ((uint32_t)0x00000000) /*!< Coarse Update method */
+#define IS_ETH_PTP_UPDATE(UPDATE) (((UPDATE) == ETH_PTP_FineUpdate) || \
+ ((UPDATE) == ETH_PTP_CoarseUpdate))
+
+/**
+ * @}
+ */
+
+
+/** @defgroup ETH_PTP_Flags
+ * @{
+ */
+#define ETH_PTP_FLAG_TSARU ((uint32_t)0x00000020) /*!< Addend Register Update */
+#define ETH_PTP_FLAG_TSITE ((uint32_t)0x00000010) /*!< Time Stamp Interrupt Trigger */
+#define ETH_PTP_FLAG_TSSTU ((uint32_t)0x00000008) /*!< Time Stamp Update */
+#define ETH_PTP_FLAG_TSSTI ((uint32_t)0x00000004) /*!< Time Stamp Initialize */
+
+#define ETH_PTP_FLAG_TSTTR ((uint32_t)0x10000002) /* Time stamp target time reached */
+#define ETH_PTP_FLAG_TSSO ((uint32_t)0x10000001) /* Time stamp seconds overflow */
+
+#define IS_ETH_PTP_GET_FLAG(FLAG) (((FLAG) == ETH_PTP_FLAG_TSARU) || \
+ ((FLAG) == ETH_PTP_FLAG_TSITE) || \
+ ((FLAG) == ETH_PTP_FLAG_TSSTU) || \
+ ((FLAG) == ETH_PTP_FLAG_TSSTI) || \
+ ((FLAG) == ETH_PTP_FLAG_TSTTR) || \
+ ((FLAG) == ETH_PTP_FLAG_TSSO))
+
+/**
+ * @brief ETH PTP subsecond increment
+ */
+#define IS_ETH_PTP_SUBSECOND_INCREMENT(SUBSECOND) ((SUBSECOND) <= 0xFF)
+
+/**
+ * @}
+ */
+
+
+/** @defgroup ETH_PTP_time_sign
+ * @{
+ */
+#define ETH_PTP_PositiveTime ((uint32_t)0x00000000) /*!< Positive time value */
+#define ETH_PTP_NegativeTime ((uint32_t)0x80000000) /*!< Negative time value */
+#define IS_ETH_PTP_TIME_SIGN(SIGN) (((SIGN) == ETH_PTP_PositiveTime) || \
+ ((SIGN) == ETH_PTP_NegativeTime))
+
+/**
+ * @brief ETH PTP time stamp low update
+ */
+#define IS_ETH_PTP_TIME_STAMP_UPDATE_SUBSECOND(SUBSECOND) ((SUBSECOND) <= 0x7FFFFFFF)
+
+/**
+ * @brief ETH PTP registers
+ */
+#define ETH_PTPTSCR ((uint32_t)0x00000700) /*!< PTP TSCR register */
+#define ETH_PTPSSIR ((uint32_t)0x00000704) /*!< PTP SSIR register */
+#define ETH_PTPTSHR ((uint32_t)0x00000708) /*!< PTP TSHR register */
+#define ETH_PTPTSLR ((uint32_t)0x0000070C) /*!< PTP TSLR register */
+#define ETH_PTPTSHUR ((uint32_t)0x00000710) /*!< PTP TSHUR register */
+#define ETH_PTPTSLUR ((uint32_t)0x00000714) /*!< PTP TSLUR register */
+#define ETH_PTPTSAR ((uint32_t)0x00000718) /*!< PTP TSAR register */
+#define ETH_PTPTTHR ((uint32_t)0x0000071C) /*!< PTP TTHR register */
+#define ETH_PTPTTLR ((uint32_t)0x00000720) /* PTP TTLR register */
+
+#define ETH_PTPTSSR ((uint32_t)0x00000728) /* PTP TSSR register */
+
+#define IS_ETH_PTP_REGISTER(REG) (((REG) == ETH_PTPTSCR) || ((REG) == ETH_PTPSSIR) || \
+ ((REG) == ETH_PTPTSHR) || ((REG) == ETH_PTPTSLR) || \
+ ((REG) == ETH_PTPTSHUR) || ((REG) == ETH_PTPTSLUR) || \
+ ((REG) == ETH_PTPTSAR) || ((REG) == ETH_PTPTTHR) || \
+ ((REG) == ETH_PTPTTLR) || ((REG) == ETH_PTPTSSR))
+
+/**
+ * @brief ETHERNET PTP clock
+ */
+#define ETH_PTP_OrdinaryClock ((uint32_t)0x00000000) /* Ordinary Clock */
+#define ETH_PTP_BoundaryClock ((uint32_t)0x00010000) /* Boundary Clock */
+#define ETH_PTP_EndToEndTransparentClock ((uint32_t)0x00020000) /* End To End Transparent Clock */
+#define ETH_PTP_PeerToPeerTransparentClock ((uint32_t)0x00030000) /* Peer To Peer Transparent Clock */
+
+#define IS_ETH_PTP_TYPE_CLOCK(CLOCK) (((CLOCK) == ETH_PTP_OrdinaryClock) || \
+ ((CLOCK) == ETH_PTP_BoundaryClock) || \
+ ((CLOCK) == ETH_PTP_EndToEndTransparentClock) || \
+ ((CLOCK) == ETH_PTP_PeerToPeerTransparentClock))
+/**
+ * @brief ETHERNET snapshot
+ */
+#define ETH_PTP_SnapshotMasterMessage ((uint32_t)0x00008000) /* Time stamp snapshot for message relevant to master enable */
+#define ETH_PTP_SnapshotEventMessage ((uint32_t)0x00004000) /* Time stamp snapshot for event message enable */
+#define ETH_PTP_SnapshotIPV4Frames ((uint32_t)0x00002000) /* Time stamp snapshot for IPv4 frames enable */
+#define ETH_PTP_SnapshotIPV6Frames ((uint32_t)0x00001000) /* Time stamp snapshot for IPv6 frames enable */
+#define ETH_PTP_SnapshotPTPOverEthernetFrames ((uint32_t)0x00000800) /* Time stamp snapshot for PTP over ethernet frames enable */
+#define ETH_PTP_SnapshotAllReceivedFrames ((uint32_t)0x00000100) /* Time stamp snapshot for all received frames enable */
+
+#define IS_ETH_PTP_SNAPSHOT(SNAPSHOT) (((SNAPSHOT) == ETH_PTP_SnapshotMasterMessage) || \
+ ((SNAPSHOT) == ETH_PTP_SnapshotEventMessage) || \
+ ((SNAPSHOT) == ETH_PTP_SnapshotIPV4Frames) || \
+ ((SNAPSHOT) == ETH_PTP_SnapshotIPV6Frames) || \
+ ((SNAPSHOT) == ETH_PTP_SnapshotPTPOverEthernetFrames) || \
+ ((SNAPSHOT) == ETH_PTP_SnapshotAllReceivedFrames))
+
+/**
+ * @}
+ */
+/* ETHERNET MAC address offsets */
+#define ETH_MAC_ADDR_HBASE (ETH_MAC_BASE + 0x40) /* ETHERNET MAC address high offset */
+#define ETH_MAC_ADDR_LBASE (ETH_MAC_BASE + 0x44) /* ETHERNET MAC address low offset */
+
+/* ETHERNET MACMIIAR register Mask */
+#define MACMIIAR_CR_MASK ((uint32_t)0xFFFFFFE3)
+
+/* ETHERNET MACCR register Mask */
+#define MACCR_CLEAR_MASK ((uint32_t)0xFF20810F)
+
+/* ETHERNET MACFCR register Mask */
+#define MACFCR_CLEAR_MASK ((uint32_t)0x0000FF41)
+
+
+/* ETHERNET DMAOMR register Mask */
+#define DMAOMR_CLEAR_MASK ((uint32_t)0xF8DE3F23)
+
+
+/* ETHERNET Remote Wake-up frame register length */
+#define ETH_WAKEUP_REGISTER_LENGTH 8
+
+/* ETHERNET Missed frames counter Shift */
+#define ETH_DMA_RX_OVERFLOW_MISSEDFRAMES_COUNTERSHIFT 17
+
+/* ETHERNET DMA Tx descriptors Collision Count Shift */
+#define ETH_DMATXDESC_COLLISION_COUNTSHIFT 3
+
+/* ETHERNET DMA Tx descriptors Buffer2 Size Shift */
+#define ETH_DMATXDESC_BUFFER2_SIZESHIFT 16
+
+/* ETHERNET DMA Rx descriptors Frame Length Shift */
+#define ETH_DMARXDESC_FRAME_LENGTHSHIFT 16
+
+/* ETHERNET DMA Rx descriptors Buffer2 Size Shift */
+#define ETH_DMARXDESC_BUFFER2_SIZESHIFT 16
+
+/* ETHERNET errors */
+#define ETH_ERROR ((uint32_t)0)
+#define ETH_SUCCESS ((uint32_t)1)
+
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Exported_Macros
+ * @{
+ */
+/**
+ * @}
+ */
+
+/** @defgroup ETH_Exported_Functions
+ * @{
+ */
+void ETH_DeInit(void);
+uint32_t ETH_Init(ETH_InitTypeDef* ETH_InitStruct, uint16_t PHYAddress);
+void ETH_StructInit(ETH_InitTypeDef* ETH_InitStruct);
+void ETH_SoftwareReset(void);
+FlagStatus ETH_GetSoftwareResetStatus(void);
+void ETH_Start(void);
+uint32_t ETH_GetRxPktSize(ETH_DMADESCTypeDef *DMARxDesc);
+
+
+#ifdef USE_ENHANCED_DMA_DESCRIPTORS
+ void ETH_EnhancedDescriptorCmd(FunctionalState NewState);
+#endif /* USE_ENHANCED_DMA_DESCRIPTORS */
+
+/**
+ * @brief PHY
+ */
+uint16_t ETH_ReadPHYRegister(uint16_t PHYAddress, uint16_t PHYReg);
+uint32_t ETH_WritePHYRegister(uint16_t PHYAddress, uint16_t PHYReg, uint16_t PHYValue);
+uint32_t ETH_PHYLoopBackCmd(uint16_t PHYAddress, FunctionalState NewState);
+
+/**
+ * @brief MAC
+ */
+void ETH_MACTransmissionCmd(FunctionalState NewState);
+void ETH_MACReceptionCmd(FunctionalState NewState);
+FlagStatus ETH_GetFlowControlBusyStatus(void);
+void ETH_InitiatePauseControlFrame(void);
+void ETH_BackPressureActivationCmd(FunctionalState NewState);
+FlagStatus ETH_GetMACFlagStatus(uint32_t ETH_MAC_FLAG);
+ITStatus ETH_GetMACITStatus(uint32_t ETH_MAC_IT);
+void ETH_MACITConfig(uint32_t ETH_MAC_IT, FunctionalState NewState);
+void ETH_MACAddressConfig(uint32_t MacAddr, uint8_t *Addr);
+void ETH_GetMACAddress(uint32_t MacAddr, uint8_t *Addr);
+void ETH_MACAddressPerfectFilterCmd(uint32_t MacAddr, FunctionalState NewState);
+void ETH_MACAddressFilterConfig(uint32_t MacAddr, uint32_t Filter);
+void ETH_MACAddressMaskBytesFilterConfig(uint32_t MacAddr, uint32_t MaskByte);
+
+/**
+ * @brief DMA Tx/Rx descriptors
+ */
+void ETH_DMARxDescChainInit(ETH_DMADESCTypeDef *DMARxDescTab, uint8_t *RxBuff, uint32_t RxBuffCount);
+void ETH_DMATxDescChainInit(ETH_DMADESCTypeDef *DMATxDescTab, uint8_t* TxBuff, uint32_t TxBuffCount);
+uint32_t ETH_CheckFrameReceived(void);
+uint32_t ETH_Prepare_Transmit_Descriptors(u16 FrameLength);
+FrameTypeDef ETH_Get_Received_Frame(void);
+FlagStatus ETH_GetDMATxDescFlagStatus(ETH_DMADESCTypeDef *DMATxDesc, uint32_t ETH_DMATxDescFlag);
+uint32_t ETH_GetDMATxDescCollisionCount(ETH_DMADESCTypeDef *DMATxDesc);
+void ETH_SetDMATxDescOwnBit(ETH_DMADESCTypeDef *DMATxDesc);
+void ETH_DMATxDescTransmitITConfig(ETH_DMADESCTypeDef *DMATxDesc, FunctionalState NewState);
+void ETH_DMATxDescFrameSegmentConfig(ETH_DMADESCTypeDef *DMATxDesc, uint32_t DMATxDesc_FrameSegment);
+void ETH_DMATxDescChecksumInsertionConfig(ETH_DMADESCTypeDef *DMATxDesc, uint32_t DMATxDesc_Checksum);
+void ETH_DMATxDescCRCCmd(ETH_DMADESCTypeDef *DMATxDesc, FunctionalState NewState);
+void ETH_DMATxDescSecondAddressChainedCmd(ETH_DMADESCTypeDef *DMATxDesc, FunctionalState NewState);
+void ETH_DMATxDescShortFramePaddingCmd(ETH_DMADESCTypeDef *DMATxDesc, FunctionalState NewState);
+void ETH_DMATxDescBufferSizeConfig(ETH_DMADESCTypeDef *DMATxDesc, uint32_t BufferSize1, uint32_t BufferSize2);
+FlagStatus ETH_GetDMARxDescFlagStatus(ETH_DMADESCTypeDef *DMARxDesc, uint32_t ETH_DMARxDescFlag);
+#ifdef USE_ENHANCED_DMA_DESCRIPTORS
+ FlagStatus ETH_GetDMAPTPRxDescExtendedFlagStatus(ETH_DMADESCTypeDef *DMAPTPRxDesc, uint32_t ETH_DMAPTPRxDescExtendedFlag);
+#endif /* USE_ENHANCED_DMA_DESCRIPTORS */
+void ETH_SetDMARxDescOwnBit(ETH_DMADESCTypeDef *DMARxDesc);
+uint32_t ETH_GetDMARxDescFrameLength(ETH_DMADESCTypeDef *DMARxDesc);
+void ETH_DMARxDescReceiveITConfig(ETH_DMADESCTypeDef *DMARxDesc, FunctionalState NewState);
+void ETH_DMARxDescSecondAddressChainedCmd(ETH_DMADESCTypeDef *DMARxDesc, FunctionalState NewState);
+uint32_t ETH_GetDMARxDescBufferSize(ETH_DMADESCTypeDef *DMARxDesc, uint32_t DMARxDesc_Buffer);
+FrameTypeDef ETH_Get_Received_Frame_interrupt(void);
+/**
+ * @brief DMA
+ */
+FlagStatus ETH_GetDMAFlagStatus(uint32_t ETH_DMA_FLAG);
+void ETH_DMAClearFlag(uint32_t ETH_DMA_FLAG);
+ITStatus ETH_GetDMAITStatus(uint32_t ETH_DMA_IT);
+void ETH_DMAClearITPendingBit(uint32_t ETH_DMA_IT);
+uint32_t ETH_GetTransmitProcessState(void);
+uint32_t ETH_GetReceiveProcessState(void);
+void ETH_FlushTransmitFIFO(void);
+FlagStatus ETH_GetFlushTransmitFIFOStatus(void);
+void ETH_DMATransmissionCmd(FunctionalState NewState);
+void ETH_DMAReceptionCmd(FunctionalState NewState);
+void ETH_DMAITConfig(uint32_t ETH_DMA_IT, FunctionalState NewState);
+FlagStatus ETH_GetDMAOverflowStatus(uint32_t ETH_DMA_Overflow);
+uint32_t ETH_GetRxOverflowMissedFrameCounter(void);
+uint32_t ETH_GetBufferUnavailableMissedFrameCounter(void);
+uint32_t ETH_GetCurrentTxDescStartAddress(void);
+uint32_t ETH_GetCurrentRxDescStartAddress(void);
+uint32_t ETH_GetCurrentTxBufferAddress(void);
+uint32_t ETH_GetCurrentRxBufferAddress(void);
+void ETH_ResumeDMATransmission(void);
+void ETH_ResumeDMAReception(void);
+void ETH_SetReceiveWatchdogTimer(uint8_t Value);
+
+
+/**
+ * @brief PMT
+ */
+void ETH_ResetWakeUpFrameFilterRegisterPointer(void);
+void ETH_SetWakeUpFrameFilterRegister(uint32_t *Buffer);
+void ETH_GlobalUnicastWakeUpCmd(FunctionalState NewState);
+FlagStatus ETH_GetPMTFlagStatus(uint32_t ETH_PMT_FLAG);
+void ETH_WakeUpFrameDetectionCmd(FunctionalState NewState);
+void ETH_MagicPacketDetectionCmd(FunctionalState NewState);
+void ETH_PowerDownCmd(FunctionalState NewState);
+
+/**
+ * @brief MMC
+ */
+void ETH_MMCCounterFullPreset(void);
+void ETH_MMCCounterHalfPreset(void);
+void ETH_MMCCounterFreezeCmd(FunctionalState NewState);
+void ETH_MMCResetOnReadCmd(FunctionalState NewState);
+void ETH_MMCCounterRolloverCmd(FunctionalState NewState);
+void ETH_MMCCountersReset(void);
+void ETH_MMCITConfig(uint32_t ETH_MMC_IT, FunctionalState NewState);
+ITStatus ETH_GetMMCITStatus(uint32_t ETH_MMC_IT);
+uint32_t ETH_GetMMCRegister(uint32_t ETH_MMCReg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __STM32F4x7_ETH_H */
+/**
+ * @}
+ */
+
+
+/**
+ * @}
+ */
+
+/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/
diff --git a/board/stm32f407-st-discovery/third_party_driver/include/hardware_eth_conf.h b/board/stm32f407-st-discovery/third_party_driver/include/hardware_eth_conf.h
new file mode 100644
index 00000000..5e6e933d
--- /dev/null
+++ b/board/stm32f407-st-discovery/third_party_driver/include/hardware_eth_conf.h
@@ -0,0 +1,91 @@
+/**
+ ******************************************************************************
+ * @file hardware_eth_conf.h
+ * @author MCD Application Team
+ * @version V1.0.0
+ * @date 31-October-2011
+ * @brief Configuration file for the STM32F4x7 Ethernet driver.
+ ******************************************************************************
+ * @attention
+ *
+ * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
+ * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
+ * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
+ * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
+ * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
+ * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
+ *
+ * © COPYRIGHT 2011 STMicroelectronics
+ ******************************************************************************
+ */
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __STM32F4x7_ETH_CONF_H
+#define __STM32F4x7_ETH_CONF_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+#include
+
+/* Exported types ------------------------------------------------------------*/
+/* Exported constants --------------------------------------------------------*/
+
+/* Uncomment the line below when using time stamping and/or IPv4 checksum offload */
+#define USE_ENHANCED_DMA_DESCRIPTORS
+
+/* Uncomment the line below if you want to use user defined Delay function
+ (for precise timing), otherwise default _eth_delay_ function defined within
+ the Ethernet driver is used (less precise timing) */
+//#define USE_Delay
+
+#ifdef USE_Delay
+ #include "main.h" /* Header file where the Delay function prototype is exported */
+ #define _eth_delay_ Delay /* User can provide more timing precise _eth_delay_ function */
+#else
+ #define _eth_delay_ ETH_Delay /* Default _eth_delay_ function with less precise timing */
+#endif
+
+
+/* Uncomment the line below to allow custom configuration of the Ethernet driver buffers */
+#define CUSTOM_DRIVER_BUFFERS_CONFIG
+
+#ifdef CUSTOM_DRIVER_BUFFERS_CONFIG
+/* Redefinition of the Ethernet driver buffers size and count */
+ #define ETH_RX_BUF_SIZE ETH_MAX_PACKET_SIZE /* buffer size for receive */
+ #define ETH_TX_BUF_SIZE ETH_MAX_PACKET_SIZE /* buffer size for transmit */
+ #define ETH_RXBUFNB 4 /* 4 Rx buffers of size ETH_RX_BUF_SIZE */
+ #define ETH_TXBUFNB 2 /* 2 Tx buffers of size ETH_TX_BUF_SIZE */
+#endif
+
+
+/* PHY configuration section **************************************************/
+/* PHY Reset delay */
+#define PHY_RESET_DELAY ((uint32_t)0x000FFFFF)
+/* PHY Configuration delay */
+#define PHY_CONFIG_DELAY ((uint32_t)0x00FFFFFF)
+
+/* The PHY status register value change from a PHY to another, so the user have
+ to update this value depending on the used external PHY */
+#define PHY_SR ((uint16_t)16) /* Value for DP83848 PHY */
+
+/* The Speed and Duplex mask values change from a PHY to another, so the user
+ have to update this value depending on the used external PHY */
+#define PHY_SPEED_STATUS ((uint16_t)0x0002) /* Value for DP83848 PHY */
+#define PHY_DUPLEX_STATUS ((uint16_t)0x0004) /* Value for DP83848 PHY */
+
+
+/* Exported macro ------------------------------------------------------------*/
+/* Exported functions ------------------------------------------------------- */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __STM32F4x7_ETH_CONF_H */
+
+
+/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/
+
diff --git a/kernel/thread/ktask.c b/kernel/thread/ktask.c
index ce9f9bf0..f966ec5a 100644
--- a/kernel/thread/ktask.c
+++ b/kernel/thread/ktask.c
@@ -236,12 +236,12 @@ x_err_t _MdelayKTask(KTaskDescriptorType task, uint32 ms)
*/
x_err_t _KTaskPrioSet(KTaskDescriptorType task, uint8 prio)
{
+ extern long ShowTask(void);
x_base lock = 0;
int ret = EOK;
uint8 task_stat = 0;
NULL_PARAM_CHECK(task);
-
lock = CriticalAreaLock();
if (0 == strncmp("ktaskidle",task->task_base_info.name, strlen("ktaskidle")))
@@ -269,11 +269,11 @@ x_err_t _KTaskPrioSet(KTaskDescriptorType task, uint8 prio)
case KTASK_INIT:
case KTASK_SUSPEND:
case KTASK_RUNNING:
- task->task_dync_sched_member.cur_prio = prio;
- __BitmapSiteMask(task);
- break;
+ task->task_dync_sched_member.cur_prio = prio; KTaskDescriptorType tid;
case KTASK_CLOSE:
+ ShowTask();
KPrintf("the close stat task is forbidden to change priority.\n");
+
ret = -ERROR;
break;
default:
diff --git a/kernel/thread/tick.c b/kernel/thread/tick.c
index e2317413..6c2c0143 100644
--- a/kernel/thread/tick.c
+++ b/kernel/thread/tick.c
@@ -23,6 +23,8 @@
#include
#include
+extern void Time_Update_LwIP(void);
+
#ifdef ARCH_SMP
static x_ticks_t heartbeat_ticks[CPU_NUMBERS] = {0};
#else
@@ -78,6 +80,10 @@ void TickAndTaskTimesliceUpdate(void)
#ifdef KERNEL_SOFTTIMER
CheckTimerList();
#endif
+#ifdef BSP_USING_LWIP
+ Time_Update_LwIP();
+#endif
+
}
/**
diff --git a/path_kernel.mk b/path_kernel.mk
index bf60db9b..f8707f85 100644
--- a/path_kernel.mk
+++ b/path_kernel.mk
@@ -33,6 +33,15 @@ KERNELPATHS :=-I$(BSP_ROOT) \
-I$(BSP_ROOT)/third_party_driver/include \
-I$(BSP_ROOT)/third_party_driver/usb/STM32_USB_OTG_Driver/inc \
-I$(KERNEL_ROOT)/include \
+ -I$(KERNEL_ROOT)/resources/ethernet/LwIP \
+ -I$(KERNEL_ROOT)/resources/ethernet/LwIP/include \
+ -I$(KERNEL_ROOT)/resources/ethernet/LwIP/include/compat \
+ -I$(KERNEL_ROOT)/resources/ethernet/LwIP/include/lwip \
+ -I$(KERNEL_ROOT)/resources/ethernet/LwIP/include/netif \
+ -I$(KERNEL_ROOT)/resources/ethernet/LwIP/include/lwip/apps \
+ -I$(KERNEL_ROOT)/resources/ethernet/LwIP/include/lwip/priv \
+ -I$(KERNEL_ROOT)/resources/ethernet/LwIP/include/lwip/prot \
+ -I$(KERNEL_ROOT)/resources/ethernet/LwIP/arch \
-I$(BSP_ROOT)/include #
endif
diff --git a/resources/Kconfig b/resources/Kconfig
index 614e2e0e..99e9409a 100644
--- a/resources/Kconfig
+++ b/resources/Kconfig
@@ -141,6 +141,12 @@ if BSP_USING_USB
endif
endif
+if BSP_USING_LWIP
+ config RESOURCES_LWIP
+ bool "Using LwIP bus drivers"
+ default n
+endif
+
if BSP_USING_WDT
config RESOURCES_WDT
bool "Using Watch Dog bus drivers"
diff --git a/resources/Makefile b/resources/Makefile
index 49be1a58..fd98f6de 100644
--- a/resources/Makefile
+++ b/resources/Makefile
@@ -49,6 +49,10 @@ ifeq ($(CONFIG_RESOURCES_USB),y)
SRC_DIR += usb
endif
+ifeq ($(CONFIG_RESOURCES_LWIP),y)
+ SRC_DIR += ethernet
+endif
+
ifeq ($(CONFIG_RESOURCES_WDT),y)
SRC_DIR += watchdog
endif
diff --git a/resources/ethernet/LwIP/FILES b/resources/ethernet/LwIP/FILES
new file mode 100644
index 00000000..300448fb
--- /dev/null
+++ b/resources/ethernet/LwIP/FILES
@@ -0,0 +1,15 @@
+api/ - The code for the high-level wrapper API. Not needed if
+ you use the lowel-level call-back/raw API.
+
+apps/ - Higher layer applications that are specifically programmed
+ with the lwIP low-level raw API.
+
+core/ - The core of the TPC/IP stack; protocol implementations,
+ memory and buffer management, and the low-level raw API.
+
+include/ - lwIP include files.
+
+netif/ - Generic network interface device drivers are kept here.
+
+For more information on the various subdirectories, check the FILES
+file in each directory.
diff --git a/resources/ethernet/LwIP/Filelists.cmake b/resources/ethernet/LwIP/Filelists.cmake
new file mode 100644
index 00000000..9448619a
--- /dev/null
+++ b/resources/ethernet/LwIP/Filelists.cmake
@@ -0,0 +1,279 @@
+# This file is indended to be included in end-user CMakeLists.txt
+# include(/path/to/Filelists.cmake)
+# It assumes the variable LWIP_DIR is defined pointing to the
+# root path of lwIP sources.
+#
+# This file is NOT designed (on purpose) to be used as cmake
+# subdir via add_subdirectory()
+# The intention is to provide greater flexibility to users to
+# create their own targets using the *_SRCS variables.
+
+set(LWIP_VERSION_MAJOR "2")
+set(LWIP_VERSION_MINOR "1")
+set(LWIP_VERSION_REVISION "2")
+# LWIP_VERSION_RC is set to LWIP_RC_RELEASE for official releases
+# LWIP_VERSION_RC is set to LWIP_RC_DEVELOPMENT for Git versions
+# Numbers 1..31 are reserved for release candidates
+set(LWIP_VERSION_RC "LWIP_RC_RELEASE")
+
+if ("${LWIP_VERSION_RC}" STREQUAL "LWIP_RC_RELEASE")
+ set(LWIP_VERSION_STRING
+ "${LWIP_VERSION_MAJOR}.${LWIP_VERSION_MINOR}.${LWIP_VERSION_REVISION}"
+ )
+elseif ("${LWIP_VERSION_RC}" STREQUAL "LWIP_RC_DEVELOPMENT")
+ set(LWIP_VERSION_STRING
+ "${LWIP_VERSION_MAJOR}.${LWIP_VERSION_MINOR}.${LWIP_VERSION_REVISION}.dev"
+ )
+else ("${LWIP_VERSION_RC}" STREQUAL "LWIP_RC_RELEASE")
+ set(LWIP_VERSION_STRING
+ "${LWIP_VERSION_MAJOR}.${LWIP_VERSION_MINOR}.${LWIP_VERSION_REVISION}.rc${LWIP_VERSION_RC}"
+ )
+endif ("${LWIP_VERSION_RC}" STREQUAL "LWIP_RC_RELEASE")
+
+# The minimum set of files needed for lwIP.
+set(lwipcore_SRCS
+ ${LWIP_DIR}/src/core/init.c
+ ${LWIP_DIR}/src/core/def.c
+ ${LWIP_DIR}/src/core/dns.c
+ ${LWIP_DIR}/src/core/inet_chksum.c
+ ${LWIP_DIR}/src/core/ip.c
+ ${LWIP_DIR}/src/core/mem.c
+ ${LWIP_DIR}/src/core/memp.c
+ ${LWIP_DIR}/src/core/netif.c
+ ${LWIP_DIR}/src/core/pbuf.c
+ ${LWIP_DIR}/src/core/raw.c
+ ${LWIP_DIR}/src/core/stats.c
+ ${LWIP_DIR}/src/core/sys.c
+ ${LWIP_DIR}/src/core/altcp.c
+ ${LWIP_DIR}/src/core/altcp_alloc.c
+ ${LWIP_DIR}/src/core/altcp_tcp.c
+ ${LWIP_DIR}/src/core/tcp.c
+ ${LWIP_DIR}/src/core/tcp_in.c
+ ${LWIP_DIR}/src/core/tcp_out.c
+ ${LWIP_DIR}/src/core/timeouts.c
+ ${LWIP_DIR}/src/core/udp.c
+)
+set(lwipcore4_SRCS
+ ${LWIP_DIR}/src/core/ipv4/autoip.c
+ ${LWIP_DIR}/src/core/ipv4/dhcp.c
+ ${LWIP_DIR}/src/core/ipv4/etharp.c
+ ${LWIP_DIR}/src/core/ipv4/icmp.c
+ ${LWIP_DIR}/src/core/ipv4/igmp.c
+ ${LWIP_DIR}/src/core/ipv4/ip4_frag.c
+ ${LWIP_DIR}/src/core/ipv4/ip4.c
+ ${LWIP_DIR}/src/core/ipv4/ip4_addr.c
+)
+set(lwipcore6_SRCS
+ ${LWIP_DIR}/src/core/ipv6/dhcp6.c
+ ${LWIP_DIR}/src/core/ipv6/ethip6.c
+ ${LWIP_DIR}/src/core/ipv6/icmp6.c
+ ${LWIP_DIR}/src/core/ipv6/inet6.c
+ ${LWIP_DIR}/src/core/ipv6/ip6.c
+ ${LWIP_DIR}/src/core/ipv6/ip6_addr.c
+ ${LWIP_DIR}/src/core/ipv6/ip6_frag.c
+ ${LWIP_DIR}/src/core/ipv6/mld6.c
+ ${LWIP_DIR}/src/core/ipv6/nd6.c
+)
+
+# APIFILES: The files which implement the sequential and socket APIs.
+set(lwipapi_SRCS
+ ${LWIP_DIR}/src/api/api_lib.c
+ ${LWIP_DIR}/src/api/api_msg.c
+ ${LWIP_DIR}/src/api/err.c
+ ${LWIP_DIR}/src/api/if_api.c
+ ${LWIP_DIR}/src/api/netbuf.c
+ ${LWIP_DIR}/src/api/netdb.c
+ ${LWIP_DIR}/src/api/netifapi.c
+ ${LWIP_DIR}/src/api/sockets.c
+ ${LWIP_DIR}/src/api/tcpip.c
+)
+
+# Files implementing various generic network interface functions
+set(lwipnetif_SRCS
+ ${LWIP_DIR}/src/netif/ethernet.c
+ ${LWIP_DIR}/src/netif/bridgeif.c
+ ${LWIP_DIR}/src/netif/bridgeif_fdb.c
+ ${LWIP_DIR}/src/netif/slipif.c
+)
+
+# 6LoWPAN
+set(lwipsixlowpan_SRCS
+ ${LWIP_DIR}/src/netif/lowpan6_common.c
+ ${LWIP_DIR}/src/netif/lowpan6.c
+ ${LWIP_DIR}/src/netif/lowpan6_ble.c
+ ${LWIP_DIR}/src/netif/zepif.c
+)
+
+# PPP
+set(lwipppp_SRCS
+ ${LWIP_DIR}/src/netif/ppp/auth.c
+ ${LWIP_DIR}/src/netif/ppp/ccp.c
+ ${LWIP_DIR}/src/netif/ppp/chap-md5.c
+ ${LWIP_DIR}/src/netif/ppp/chap_ms.c
+ ${LWIP_DIR}/src/netif/ppp/chap-new.c
+ ${LWIP_DIR}/src/netif/ppp/demand.c
+ ${LWIP_DIR}/src/netif/ppp/eap.c
+ ${LWIP_DIR}/src/netif/ppp/ecp.c
+ ${LWIP_DIR}/src/netif/ppp/eui64.c
+ ${LWIP_DIR}/src/netif/ppp/fsm.c
+ ${LWIP_DIR}/src/netif/ppp/ipcp.c
+ ${LWIP_DIR}/src/netif/ppp/ipv6cp.c
+ ${LWIP_DIR}/src/netif/ppp/lcp.c
+ ${LWIP_DIR}/src/netif/ppp/magic.c
+ ${LWIP_DIR}/src/netif/ppp/mppe.c
+ ${LWIP_DIR}/src/netif/ppp/multilink.c
+ ${LWIP_DIR}/src/netif/ppp/ppp.c
+ ${LWIP_DIR}/src/netif/ppp/pppapi.c
+ ${LWIP_DIR}/src/netif/ppp/pppcrypt.c
+ ${LWIP_DIR}/src/netif/ppp/pppoe.c
+ ${LWIP_DIR}/src/netif/ppp/pppol2tp.c
+ ${LWIP_DIR}/src/netif/ppp/pppos.c
+ ${LWIP_DIR}/src/netif/ppp/upap.c
+ ${LWIP_DIR}/src/netif/ppp/utils.c
+ ${LWIP_DIR}/src/netif/ppp/vj.c
+ ${LWIP_DIR}/src/netif/ppp/polarssl/arc4.c
+ ${LWIP_DIR}/src/netif/ppp/polarssl/des.c
+ ${LWIP_DIR}/src/netif/ppp/polarssl/md4.c
+ ${LWIP_DIR}/src/netif/ppp/polarssl/md5.c
+ ${LWIP_DIR}/src/netif/ppp/polarssl/sha1.c
+)
+
+# SNMPv3 agent
+set(lwipsnmp_SRCS
+ ${LWIP_DIR}/src/apps/snmp/snmp_asn1.c
+ ${LWIP_DIR}/src/apps/snmp/snmp_core.c
+ ${LWIP_DIR}/src/apps/snmp/snmp_mib2.c
+ ${LWIP_DIR}/src/apps/snmp/snmp_mib2_icmp.c
+ ${LWIP_DIR}/src/apps/snmp/snmp_mib2_interfaces.c
+ ${LWIP_DIR}/src/apps/snmp/snmp_mib2_ip.c
+ ${LWIP_DIR}/src/apps/snmp/snmp_mib2_snmp.c
+ ${LWIP_DIR}/src/apps/snmp/snmp_mib2_system.c
+ ${LWIP_DIR}/src/apps/snmp/snmp_mib2_tcp.c
+ ${LWIP_DIR}/src/apps/snmp/snmp_mib2_udp.c
+ ${LWIP_DIR}/src/apps/snmp/snmp_snmpv2_framework.c
+ ${LWIP_DIR}/src/apps/snmp/snmp_snmpv2_usm.c
+ ${LWIP_DIR}/src/apps/snmp/snmp_msg.c
+ ${LWIP_DIR}/src/apps/snmp/snmpv3.c
+ ${LWIP_DIR}/src/apps/snmp/snmp_netconn.c
+ ${LWIP_DIR}/src/apps/snmp/snmp_pbuf_stream.c
+ ${LWIP_DIR}/src/apps/snmp/snmp_raw.c
+ ${LWIP_DIR}/src/apps/snmp/snmp_scalar.c
+ ${LWIP_DIR}/src/apps/snmp/snmp_table.c
+ ${LWIP_DIR}/src/apps/snmp/snmp_threadsync.c
+ ${LWIP_DIR}/src/apps/snmp/snmp_traps.c
+)
+
+# HTTP server + client
+set(lwiphttp_SRCS
+ ${LWIP_DIR}/src/apps/http/altcp_proxyconnect.c
+ ${LWIP_DIR}/src/apps/http/fs.c
+ ${LWIP_DIR}/src/apps/http/http_client.c
+ ${LWIP_DIR}/src/apps/http/httpd.c
+)
+
+# MAKEFSDATA HTTP server host utility
+set(lwipmakefsdata_SRCS
+ ${LWIP_DIR}/src/apps/http/makefsdata/makefsdata.c
+)
+
+# IPERF server
+set(lwipiperf_SRCS
+ ${LWIP_DIR}/src/apps/lwiperf/lwiperf.c
+)
+
+# SMTP client
+set(lwipsmtp_SRCS
+ ${LWIP_DIR}/src/apps/smtp/smtp.c
+)
+
+# SNTP client
+set(lwipsntp_SRCS
+ ${LWIP_DIR}/src/apps/sntp/sntp.c
+)
+
+# MDNS responder
+set(lwipmdns_SRCS
+ ${LWIP_DIR}/src/apps/mdns/mdns.c
+)
+
+# NetBIOS name server
+set(lwipnetbios_SRCS
+ ${LWIP_DIR}/src/apps/netbiosns/netbiosns.c
+)
+
+# TFTP server files
+set(lwiptftp_SRCS
+ ${LWIP_DIR}/src/apps/tftp/tftp_server.c
+)
+
+# MQTT client files
+set(lwipmqtt_SRCS
+ ${LWIP_DIR}/src/apps/mqtt/mqtt.c
+)
+
+# ARM MBEDTLS related files of lwIP rep
+set(lwipmbedtls_SRCS
+ ${LWIP_DIR}/src/apps/altcp_tls/altcp_tls_mbedtls.c
+ ${LWIP_DIR}/src/apps/altcp_tls/altcp_tls_mbedtls_mem.c
+ ${LWIP_DIR}/src/apps/snmp/snmpv3_mbedtls.c
+)
+
+# All LWIP files without apps
+set(lwipnoapps_SRCS
+ ${lwipcore_SRCS}
+ ${lwipcore4_SRCS}
+ ${lwipcore6_SRCS}
+ ${lwipapi_SRCS}
+ ${lwipnetif_SRCS}
+ ${lwipsixlowpan_SRCS}
+ ${lwipppp_SRCS}
+)
+
+# LWIPAPPFILES: All LWIP APPs
+set(lwipallapps_SRCS
+ ${lwipsnmp_SRCS}
+ ${lwiphttp_SRCS}
+ ${lwipiperf_SRCS}
+ ${lwipsmtp_SRCS}
+ ${lwipsntp_SRCS}
+ ${lwipmdns_SRCS}
+ ${lwipnetbios_SRCS}
+ ${lwiptftp_SRCS}
+ ${lwipmqtt_SRCS}
+ ${lwipmbedtls_SRCS}
+)
+
+# Generate lwip/init.h (version info)
+configure_file(${LWIP_DIR}/src/include/lwip/init.h.cmake.in ${LWIP_DIR}/src/include/lwip/init.h)
+
+# Documentation
+set(DOXYGEN_DIR ${LWIP_DIR}/doc/doxygen)
+set(DOXYGEN_OUTPUT_DIR output)
+set(DOXYGEN_IN ${LWIP_DIR}/doc/doxygen/lwip.Doxyfile.cmake.in)
+set(DOXYGEN_OUT ${LWIP_DIR}/doc/doxygen/lwip.Doxyfile)
+configure_file(${DOXYGEN_IN} ${DOXYGEN_OUT})
+
+find_package(Doxygen)
+if (DOXYGEN_FOUND)
+ message("Doxygen build started")
+
+ add_custom_target(lwipdocs
+ COMMAND ${CMAKE_COMMAND} -E remove_directory ${DOXYGEN_DIR}/${DOXYGEN_OUTPUT_DIR}/html
+ COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT}
+ WORKING_DIRECTORY ${DOXYGEN_DIR}
+ COMMENT "Generating API documentation with Doxygen"
+ VERBATIM)
+else (DOXYGEN_FOUND)
+ message("Doxygen needs to be installed to generate the doxygen documentation")
+endif (DOXYGEN_FOUND)
+
+# lwIP libraries
+add_library(lwipcore EXCLUDE_FROM_ALL ${lwipnoapps_SRCS})
+target_compile_options(lwipcore PRIVATE ${LWIP_COMPILER_FLAGS})
+target_compile_definitions(lwipcore PRIVATE ${LWIP_DEFINITIONS} ${LWIP_MBEDTLS_DEFINITIONS})
+target_include_directories(lwipcore PRIVATE ${LWIP_INCLUDE_DIRS} ${LWIP_MBEDTLS_INCLUDE_DIRS})
+
+add_library(lwipallapps EXCLUDE_FROM_ALL ${lwipallapps_SRCS})
+target_compile_options(lwipallapps PRIVATE ${LWIP_COMPILER_FLAGS})
+target_compile_definitions(lwipallapps PRIVATE ${LWIP_DEFINITIONS} ${LWIP_MBEDTLS_DEFINITIONS})
+target_include_directories(lwipallapps PRIVATE ${LWIP_INCLUDE_DIRS} ${LWIP_MBEDTLS_INCLUDE_DIRS})
diff --git a/resources/ethernet/LwIP/Filelists.mk b/resources/ethernet/LwIP/Filelists.mk
new file mode 100644
index 00000000..12e1b40c
--- /dev/null
+++ b/resources/ethernet/LwIP/Filelists.mk
@@ -0,0 +1,205 @@
+#
+# Copyright (c) 2001, 2002 Swedish Institute of Computer Science.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# 3. The name of the author may not be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+# SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+# OF SUCH DAMAGE.
+#
+# This file is part of the lwIP TCP/IP stack.
+#
+# Author: Adam Dunkels
+#
+
+# COREFILES, CORE4FILES: The minimum set of files needed for lwIP.
+COREFILES=$(LWIPDIR)/core/init.c \
+ $(LWIPDIR)/core/def.c \
+ $(LWIPDIR)/core/dns.c \
+ $(LWIPDIR)/core/inet_chksum.c \
+ $(LWIPDIR)/core/ip.c \
+ $(LWIPDIR)/core/mem.c \
+ $(LWIPDIR)/core/memp.c \
+ $(LWIPDIR)/core/netif.c \
+ $(LWIPDIR)/core/pbuf.c \
+ $(LWIPDIR)/core/raw.c \
+ $(LWIPDIR)/core/stats.c \
+ $(LWIPDIR)/core/sys.c \
+ $(LWIPDIR)/core/altcp.c \
+ $(LWIPDIR)/core/altcp_alloc.c \
+ $(LWIPDIR)/core/altcp_tcp.c \
+ $(LWIPDIR)/core/tcp.c \
+ $(LWIPDIR)/core/tcp_in.c \
+ $(LWIPDIR)/core/tcp_out.c \
+ $(LWIPDIR)/core/timeouts.c \
+ $(LWIPDIR)/core/udp.c
+
+CORE4FILES=$(LWIPDIR)/core/ipv4/autoip.c \
+ $(LWIPDIR)/core/ipv4/dhcp.c \
+ $(LWIPDIR)/core/ipv4/etharp.c \
+ $(LWIPDIR)/core/ipv4/icmp.c \
+ $(LWIPDIR)/core/ipv4/igmp.c \
+ $(LWIPDIR)/core/ipv4/ip4_frag.c \
+ $(LWIPDIR)/core/ipv4/ip4.c \
+ $(LWIPDIR)/core/ipv4/ip4_addr.c
+
+CORE6FILES=$(LWIPDIR)/core/ipv6/dhcp6.c \
+ $(LWIPDIR)/core/ipv6/ethip6.c \
+ $(LWIPDIR)/core/ipv6/icmp6.c \
+ $(LWIPDIR)/core/ipv6/inet6.c \
+ $(LWIPDIR)/core/ipv6/ip6.c \
+ $(LWIPDIR)/core/ipv6/ip6_addr.c \
+ $(LWIPDIR)/core/ipv6/ip6_frag.c \
+ $(LWIPDIR)/core/ipv6/mld6.c \
+ $(LWIPDIR)/core/ipv6/nd6.c
+
+# APIFILES: The files which implement the sequential and socket APIs.
+APIFILES=$(LWIPDIR)/api/api_lib.c \
+ $(LWIPDIR)/api/api_msg.c \
+ $(LWIPDIR)/api/err.c \
+ $(LWIPDIR)/api/if_api.c \
+ $(LWIPDIR)/api/netbuf.c \
+ $(LWIPDIR)/api/netdb.c \
+ $(LWIPDIR)/api/netifapi.c \
+ $(LWIPDIR)/api/sockets.c \
+ $(LWIPDIR)/api/tcpip.c
+
+# NETIFFILES: Files implementing various generic network interface functions
+NETIFFILES=$(LWIPDIR)/netif/ethernet.c \
+ $(LWIPDIR)/netif/bridgeif.c \
+ $(LWIPDIR)/netif/bridgeif_fdb.c \
+ $(LWIPDIR)/netif/slipif.c
+
+# SIXLOWPAN: 6LoWPAN
+SIXLOWPAN=$(LWIPDIR)/netif/lowpan6_common.c \
+ $(LWIPDIR)/netif/lowpan6.c \
+ $(LWIPDIR)/netif/lowpan6_ble.c \
+ $(LWIPDIR)/netif/zepif.c
+
+# PPPFILES: PPP
+PPPFILES=$(LWIPDIR)/netif/ppp/auth.c \
+ $(LWIPDIR)/netif/ppp/ccp.c \
+ $(LWIPDIR)/netif/ppp/chap-md5.c \
+ $(LWIPDIR)/netif/ppp/chap_ms.c \
+ $(LWIPDIR)/netif/ppp/chap-new.c \
+ $(LWIPDIR)/netif/ppp/demand.c \
+ $(LWIPDIR)/netif/ppp/eap.c \
+ $(LWIPDIR)/netif/ppp/ecp.c \
+ $(LWIPDIR)/netif/ppp/eui64.c \
+ $(LWIPDIR)/netif/ppp/fsm.c \
+ $(LWIPDIR)/netif/ppp/ipcp.c \
+ $(LWIPDIR)/netif/ppp/ipv6cp.c \
+ $(LWIPDIR)/netif/ppp/lcp.c \
+ $(LWIPDIR)/netif/ppp/magic.c \
+ $(LWIPDIR)/netif/ppp/mppe.c \
+ $(LWIPDIR)/netif/ppp/multilink.c \
+ $(LWIPDIR)/netif/ppp/ppp.c \
+ $(LWIPDIR)/netif/ppp/pppapi.c \
+ $(LWIPDIR)/netif/ppp/pppcrypt.c \
+ $(LWIPDIR)/netif/ppp/pppoe.c \
+ $(LWIPDIR)/netif/ppp/pppol2tp.c \
+ $(LWIPDIR)/netif/ppp/pppos.c \
+ $(LWIPDIR)/netif/ppp/upap.c \
+ $(LWIPDIR)/netif/ppp/utils.c \
+ $(LWIPDIR)/netif/ppp/vj.c \
+ $(LWIPDIR)/netif/ppp/polarssl/arc4.c \
+ $(LWIPDIR)/netif/ppp/polarssl/des.c \
+ $(LWIPDIR)/netif/ppp/polarssl/md4.c \
+ $(LWIPDIR)/netif/ppp/polarssl/md5.c \
+ $(LWIPDIR)/netif/ppp/polarssl/sha1.c
+
+# LWIPNOAPPSFILES: All LWIP files without apps
+LWIPNOAPPSFILES=$(COREFILES) \
+ $(CORE4FILES) \
+ $(CORE6FILES) \
+ $(APIFILES) \
+ $(NETIFFILES) \
+ $(PPPFILES) \
+ $(SIXLOWPAN)
+
+# SNMPFILES: SNMPv2c agent
+SNMPFILES=$(LWIPDIR)/apps/snmp/snmp_asn1.c \
+ $(LWIPDIR)/apps/snmp/snmp_core.c \
+ $(LWIPDIR)/apps/snmp/snmp_mib2.c \
+ $(LWIPDIR)/apps/snmp/snmp_mib2_icmp.c \
+ $(LWIPDIR)/apps/snmp/snmp_mib2_interfaces.c \
+ $(LWIPDIR)/apps/snmp/snmp_mib2_ip.c \
+ $(LWIPDIR)/apps/snmp/snmp_mib2_snmp.c \
+ $(LWIPDIR)/apps/snmp/snmp_mib2_system.c \
+ $(LWIPDIR)/apps/snmp/snmp_mib2_tcp.c \
+ $(LWIPDIR)/apps/snmp/snmp_mib2_udp.c \
+ $(LWIPDIR)/apps/snmp/snmp_snmpv2_framework.c \
+ $(LWIPDIR)/apps/snmp/snmp_snmpv2_usm.c \
+ $(LWIPDIR)/apps/snmp/snmp_msg.c \
+ $(LWIPDIR)/apps/snmp/snmpv3.c \
+ $(LWIPDIR)/apps/snmp/snmp_netconn.c \
+ $(LWIPDIR)/apps/snmp/snmp_pbuf_stream.c \
+ $(LWIPDIR)/apps/snmp/snmp_raw.c \
+ $(LWIPDIR)/apps/snmp/snmp_scalar.c \
+ $(LWIPDIR)/apps/snmp/snmp_table.c \
+ $(LWIPDIR)/apps/snmp/snmp_threadsync.c \
+ $(LWIPDIR)/apps/snmp/snmp_traps.c
+
+# HTTPFILES: HTTP server + client
+HTTPFILES=$(LWIPDIR)/apps/http/altcp_proxyconnect.c \
+ $(LWIPDIR)/apps/http/fs.c \
+ $(LWIPDIR)/apps/http/http_client.c \
+ $(LWIPDIR)/apps/http/httpd.c
+
+# MAKEFSDATA: MAKEFSDATA HTTP server host utility
+MAKEFSDATAFILES=$(LWIPDIR)/apps/http/makefsdata/makefsdata.c
+
+# LWIPERFFILES: IPERF server
+LWIPERFFILES=$(LWIPDIR)/apps/lwiperf/lwiperf.c
+
+# SMTPFILES: SMTP client
+SMTPFILES=$(LWIPDIR)/apps/smtp/smtp.c
+
+# SNTPFILES: SNTP client
+SNTPFILES=$(LWIPDIR)/apps/sntp/sntp.c
+
+# MDNSFILES: MDNS responder
+MDNSFILES=$(LWIPDIR)/apps/mdns/mdns.c
+
+# NETBIOSNSFILES: NetBIOS name server
+NETBIOSNSFILES=$(LWIPDIR)/apps/netbiosns/netbiosns.c
+
+# TFTPFILES: TFTP server files
+TFTPFILES=$(LWIPDIR)/apps/tftp/tftp_server.c
+
+# MQTTFILES: MQTT client files
+MQTTFILES=$(LWIPDIR)/apps/mqtt/mqtt.c
+
+# MBEDTLS_FILES: MBEDTLS related files of lwIP rep
+MBEDTLS_FILES=$(LWIPDIR)/apps/altcp_tls/altcp_tls_mbedtls.c \
+ $(LWIPDIR)/apps/altcp_tls/altcp_tls_mbedtls_mem.c \
+ $(LWIPDIR)/apps/snmp/snmpv3_mbedtls.c
+
+# LWIPAPPFILES: All LWIP APPs
+LWIPAPPFILES=$(SNMPFILES) \
+ $(HTTPFILES) \
+ $(LWIPERFFILES) \
+ $(SMTPFILES) \
+ $(SNTPFILES) \
+ $(MDNSFILES) \
+ $(NETBIOSNSFILES) \
+ $(TFTPFILES) \
+ $(MQTTFILES) \
+ $(MBEDTLS_FILES)
diff --git a/resources/ethernet/LwIP/Makefile b/resources/ethernet/LwIP/Makefile
new file mode 100644
index 00000000..a5d3576a
--- /dev/null
+++ b/resources/ethernet/LwIP/Makefile
@@ -0,0 +1,6 @@
+SRC_DIR += api
+SRC_DIR += arch
+SRC_DIR += core
+SRC_DIR += netif
+
+include $(KERNEL_ROOT)/compiler.mk
diff --git a/resources/ethernet/LwIP/api/Makefile b/resources/ethernet/LwIP/api/Makefile
new file mode 100644
index 00000000..c2879b28
--- /dev/null
+++ b/resources/ethernet/LwIP/api/Makefile
@@ -0,0 +1,12 @@
+SRC_FILES += api_lib.c \
+ api_msg.c \
+ err.c \
+ if_api.c \
+ netbuf.c \
+ netdb.c \
+ netifapi.c \
+ sockets.c \
+ tcpip.c
+
+
+include $(KERNEL_ROOT)/compiler.mk
diff --git a/resources/ethernet/LwIP/api/api_lib.c b/resources/ethernet/LwIP/api/api_lib.c
new file mode 100644
index 00000000..9a30ec04
--- /dev/null
+++ b/resources/ethernet/LwIP/api/api_lib.c
@@ -0,0 +1,1367 @@
+/**
+ * @file
+ * Sequential API External module
+ *
+ * @defgroup netconn Netconn API
+ * @ingroup sequential_api
+ * Thread-safe, to be called from non-TCPIP threads only.
+ * TX/RX handling based on @ref netbuf (containing @ref pbuf)
+ * to avoid copying data around.
+ *
+ * @defgroup netconn_common Common functions
+ * @ingroup netconn
+ * For use with TCP and UDP
+ *
+ * @defgroup netconn_tcp TCP only
+ * @ingroup netconn
+ * TCP only functions
+ *
+ * @defgroup netconn_udp UDP only
+ * @ingroup netconn
+ * UDP only functions
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ */
+
+/* This is the part of the API that is linked with
+ the application */
+
+#include "lwip/opt.h"
+
+#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/api.h"
+#include "lwip/memp.h"
+
+#include "lwip/ip.h"
+#include "lwip/raw.h"
+#include "lwip/udp.h"
+#include "lwip/priv/api_msg.h"
+#include "lwip/priv/tcp_priv.h"
+#include "lwip/priv/tcpip_priv.h"
+
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+
+#include
+
+#define API_MSG_VAR_REF(name) API_VAR_REF(name)
+#define API_MSG_VAR_DECLARE(name) API_VAR_DECLARE(struct api_msg, name)
+#define API_MSG_VAR_ALLOC(name) API_VAR_ALLOC(struct api_msg, MEMP_API_MSG, name, ERR_MEM)
+#define API_MSG_VAR_ALLOC_RETURN_NULL(name) API_VAR_ALLOC(struct api_msg, MEMP_API_MSG, name, NULL)
+#define API_MSG_VAR_FREE(name) API_VAR_FREE(MEMP_API_MSG, name)
+
+#if TCP_LISTEN_BACKLOG
+/* need to allocate API message for accept so empty message pool does not result in event loss
+ * see bug #47512: MPU_COMPATIBLE may fail on empty pool */
+#define API_MSG_VAR_ALLOC_ACCEPT(msg) API_MSG_VAR_ALLOC(msg)
+#define API_MSG_VAR_FREE_ACCEPT(msg) API_MSG_VAR_FREE(msg)
+#else /* TCP_LISTEN_BACKLOG */
+#define API_MSG_VAR_ALLOC_ACCEPT(msg)
+#define API_MSG_VAR_FREE_ACCEPT(msg)
+#endif /* TCP_LISTEN_BACKLOG */
+
+#if LWIP_NETCONN_FULLDUPLEX
+#define NETCONN_RECVMBOX_WAITABLE(conn) (sys_mbox_valid(&(conn)->recvmbox) && (((conn)->flags & NETCONN_FLAG_MBOXINVALID) == 0))
+#define NETCONN_ACCEPTMBOX_WAITABLE(conn) (sys_mbox_valid(&(conn)->acceptmbox) && (((conn)->flags & (NETCONN_FLAG_MBOXCLOSED|NETCONN_FLAG_MBOXINVALID)) == 0))
+#define NETCONN_MBOX_WAITING_INC(conn) SYS_ARCH_INC(conn->mbox_threads_waiting, 1)
+#define NETCONN_MBOX_WAITING_DEC(conn) SYS_ARCH_DEC(conn->mbox_threads_waiting, 1)
+#else /* LWIP_NETCONN_FULLDUPLEX */
+#define NETCONN_RECVMBOX_WAITABLE(conn) sys_mbox_valid(&(conn)->recvmbox)
+#define NETCONN_ACCEPTMBOX_WAITABLE(conn) (sys_mbox_valid(&(conn)->acceptmbox) && (((conn)->flags & NETCONN_FLAG_MBOXCLOSED) == 0))
+#define NETCONN_MBOX_WAITING_INC(conn)
+#define NETCONN_MBOX_WAITING_DEC(conn)
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+
+static err_t netconn_close_shutdown(struct netconn *conn, u8_t how);
+
+/**
+ * Call the lower part of a netconn_* function
+ * This function is then running in the thread context
+ * of tcpip_thread and has exclusive access to lwIP core code.
+ *
+ * @param fn function to call
+ * @param apimsg a struct containing the function to call and its parameters
+ * @return ERR_OK if the function was called, another err_t if not
+ */
+static err_t
+netconn_apimsg(tcpip_callback_fn fn, struct api_msg *apimsg)
+{
+ err_t err;
+
+#ifdef LWIP_DEBUG
+ /* catch functions that don't set err */
+ apimsg->err = ERR_VAL;
+#endif /* LWIP_DEBUG */
+
+#if LWIP_NETCONN_SEM_PER_THREAD
+ apimsg->op_completed_sem = LWIP_NETCONN_THREAD_SEM_GET();
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+ err = tcpip_send_msg_wait_sem(fn, apimsg, LWIP_API_MSG_SEM(apimsg));
+ if (err == ERR_OK) {
+ return apimsg->err;
+ }
+ return err;
+}
+
+/**
+ * Create a new netconn (of a specific type) that has a callback function.
+ * The corresponding pcb is also created.
+ *
+ * @param t the type of 'connection' to create (@see enum netconn_type)
+ * @param proto the IP protocol for RAW IP pcbs
+ * @param callback a function to call on status changes (RX available, TX'ed)
+ * @return a newly allocated struct netconn or
+ * NULL on memory error
+ */
+struct netconn *
+netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, netconn_callback callback)
+{
+ struct netconn *conn;
+ API_MSG_VAR_DECLARE(msg);
+ API_MSG_VAR_ALLOC_RETURN_NULL(msg);
+
+ conn = netconn_alloc(t, callback);
+ if (conn != NULL) {
+ err_t err;
+
+ API_MSG_VAR_REF(msg).msg.n.proto = proto;
+ API_MSG_VAR_REF(msg).conn = conn;
+ err = netconn_apimsg(lwip_netconn_do_newconn, &API_MSG_VAR_REF(msg));
+ if (err != ERR_OK) {
+ LWIP_ASSERT("freeing conn without freeing pcb", conn->pcb.tcp == NULL);
+ LWIP_ASSERT("conn has no recvmbox", sys_mbox_valid(&conn->recvmbox));
+#if LWIP_TCP
+ LWIP_ASSERT("conn->acceptmbox shouldn't exist", !sys_mbox_valid(&conn->acceptmbox));
+#endif /* LWIP_TCP */
+#if !LWIP_NETCONN_SEM_PER_THREAD
+ LWIP_ASSERT("conn has no op_completed", sys_sem_valid(&conn->op_completed));
+ sys_sem_free(&conn->op_completed);
+#endif /* !LWIP_NETCONN_SEM_PER_THREAD */
+ sys_mbox_free(&conn->recvmbox);
+ memp_free(MEMP_NETCONN, conn);
+ API_MSG_VAR_FREE(msg);
+ return NULL;
+ }
+ }
+ API_MSG_VAR_FREE(msg);
+ return conn;
+}
+
+/**
+ * @ingroup netconn_common
+ * Close a netconn 'connection' and free all its resources but not the netconn itself.
+ * UDP and RAW connection are completely closed, TCP pcbs might still be in a waitstate
+ * after this returns.
+ *
+ * @param conn the netconn to delete
+ * @return ERR_OK if the connection was deleted
+ */
+err_t
+netconn_prepare_delete(struct netconn *conn)
+{
+ err_t err;
+ API_MSG_VAR_DECLARE(msg);
+
+ /* No ASSERT here because possible to get a (conn == NULL) if we got an accept error */
+ if (conn == NULL) {
+ return ERR_OK;
+ }
+
+ API_MSG_VAR_ALLOC(msg);
+ API_MSG_VAR_REF(msg).conn = conn;
+#if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
+ /* get the time we started, which is later compared to
+ sys_now() + conn->send_timeout */
+ API_MSG_VAR_REF(msg).msg.sd.time_started = sys_now();
+#else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
+#if LWIP_TCP
+ API_MSG_VAR_REF(msg).msg.sd.polls_left =
+ ((LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT + TCP_SLOW_INTERVAL - 1) / TCP_SLOW_INTERVAL) + 1;
+#endif /* LWIP_TCP */
+#endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
+ err = netconn_apimsg(lwip_netconn_do_delconn, &API_MSG_VAR_REF(msg));
+ API_MSG_VAR_FREE(msg);
+
+ if (err != ERR_OK) {
+ return err;
+ }
+ return ERR_OK;
+}
+
+/**
+ * @ingroup netconn_common
+ * Close a netconn 'connection' and free its resources.
+ * UDP and RAW connection are completely closed, TCP pcbs might still be in a waitstate
+ * after this returns.
+ *
+ * @param conn the netconn to delete
+ * @return ERR_OK if the connection was deleted
+ */
+err_t
+netconn_delete(struct netconn *conn)
+{
+ err_t err;
+
+ /* No ASSERT here because possible to get a (conn == NULL) if we got an accept error */
+ if (conn == NULL) {
+ return ERR_OK;
+ }
+
+#if LWIP_NETCONN_FULLDUPLEX
+ if (conn->flags & NETCONN_FLAG_MBOXINVALID) {
+ /* Already called netconn_prepare_delete() before */
+ err = ERR_OK;
+ } else
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+ {
+ err = netconn_prepare_delete(conn);
+ }
+ if (err == ERR_OK) {
+ netconn_free(conn);
+ }
+ return err;
+}
+
+/**
+ * Get the local or remote IP address and port of a netconn.
+ * For RAW netconns, this returns the protocol instead of a port!
+ *
+ * @param conn the netconn to query
+ * @param addr a pointer to which to save the IP address
+ * @param port a pointer to which to save the port (or protocol for RAW)
+ * @param local 1 to get the local IP address, 0 to get the remote one
+ * @return ERR_CONN for invalid connections
+ * ERR_OK if the information was retrieved
+ */
+err_t
+netconn_getaddr(struct netconn *conn, ip_addr_t *addr, u16_t *port, u8_t local)
+{
+ API_MSG_VAR_DECLARE(msg);
+ err_t err;
+
+ LWIP_ERROR("netconn_getaddr: invalid conn", (conn != NULL), return ERR_ARG;);
+ LWIP_ERROR("netconn_getaddr: invalid addr", (addr != NULL), return ERR_ARG;);
+ LWIP_ERROR("netconn_getaddr: invalid port", (port != NULL), return ERR_ARG;);
+
+ API_MSG_VAR_ALLOC(msg);
+ API_MSG_VAR_REF(msg).conn = conn;
+ API_MSG_VAR_REF(msg).msg.ad.local = local;
+#if LWIP_MPU_COMPATIBLE
+ err = netconn_apimsg(lwip_netconn_do_getaddr, &API_MSG_VAR_REF(msg));
+ *addr = msg->msg.ad.ipaddr;
+ *port = msg->msg.ad.port;
+#else /* LWIP_MPU_COMPATIBLE */
+ msg.msg.ad.ipaddr = addr;
+ msg.msg.ad.port = port;
+ err = netconn_apimsg(lwip_netconn_do_getaddr, &msg);
+#endif /* LWIP_MPU_COMPATIBLE */
+ API_MSG_VAR_FREE(msg);
+
+ return err;
+}
+
+/**
+ * @ingroup netconn_common
+ * Bind a netconn to a specific local IP address and port.
+ * Binding one netconn twice might not always be checked correctly!
+ *
+ * @param conn the netconn to bind
+ * @param addr the local IP address to bind the netconn to
+ * (use IP4_ADDR_ANY/IP6_ADDR_ANY to bind to all addresses)
+ * @param port the local port to bind the netconn to (not used for RAW)
+ * @return ERR_OK if bound, any other err_t on failure
+ */
+err_t
+netconn_bind(struct netconn *conn, const ip_addr_t *addr, u16_t port)
+{
+ API_MSG_VAR_DECLARE(msg);
+ err_t err;
+
+ LWIP_ERROR("netconn_bind: invalid conn", (conn != NULL), return ERR_ARG;);
+
+#if LWIP_IPV4
+ /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
+ if (addr == NULL) {
+ addr = IP4_ADDR_ANY;
+ }
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV4 && LWIP_IPV6
+ /* "Socket API like" dual-stack support: If IP to bind to is IP6_ADDR_ANY,
+ * and NETCONN_FLAG_IPV6_V6ONLY is 0, use IP_ANY_TYPE to bind
+ */
+ if ((netconn_get_ipv6only(conn) == 0) &&
+ ip_addr_cmp(addr, IP6_ADDR_ANY)) {
+ addr = IP_ANY_TYPE;
+ }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+ API_MSG_VAR_ALLOC(msg);
+ API_MSG_VAR_REF(msg).conn = conn;
+ API_MSG_VAR_REF(msg).msg.bc.ipaddr = API_MSG_VAR_REF(addr);
+ API_MSG_VAR_REF(msg).msg.bc.port = port;
+ err = netconn_apimsg(lwip_netconn_do_bind, &API_MSG_VAR_REF(msg));
+ API_MSG_VAR_FREE(msg);
+
+ return err;
+}
+
+/**
+ * @ingroup netconn_common
+ * Bind a netconn to a specific interface and port.
+ * Binding one netconn twice might not always be checked correctly!
+ *
+ * @param conn the netconn to bind
+ * @param if_idx the local interface index to bind the netconn to
+ * @return ERR_OK if bound, any other err_t on failure
+ */
+err_t
+netconn_bind_if(struct netconn *conn, u8_t if_idx)
+{
+ API_MSG_VAR_DECLARE(msg);
+ err_t err;
+
+ LWIP_ERROR("netconn_bind_if: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ API_MSG_VAR_ALLOC(msg);
+ API_MSG_VAR_REF(msg).conn = conn;
+ API_MSG_VAR_REF(msg).msg.bc.if_idx = if_idx;
+ err = netconn_apimsg(lwip_netconn_do_bind_if, &API_MSG_VAR_REF(msg));
+ API_MSG_VAR_FREE(msg);
+
+ return err;
+}
+
+/**
+ * @ingroup netconn_common
+ * Connect a netconn to a specific remote IP address and port.
+ *
+ * @param conn the netconn to connect
+ * @param addr the remote IP address to connect to
+ * @param port the remote port to connect to (no used for RAW)
+ * @return ERR_OK if connected, return value of tcp_/udp_/raw_connect otherwise
+ */
+err_t
+netconn_connect(struct netconn *conn, const ip_addr_t *addr, u16_t port)
+{
+ API_MSG_VAR_DECLARE(msg);
+ err_t err;
+
+ LWIP_ERROR("netconn_connect: invalid conn", (conn != NULL), return ERR_ARG;);
+
+#if LWIP_IPV4
+ /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
+ if (addr == NULL) {
+ addr = IP4_ADDR_ANY;
+ }
+#endif /* LWIP_IPV4 */
+
+ API_MSG_VAR_ALLOC(msg);
+ API_MSG_VAR_REF(msg).conn = conn;
+ API_MSG_VAR_REF(msg).msg.bc.ipaddr = API_MSG_VAR_REF(addr);
+ API_MSG_VAR_REF(msg).msg.bc.port = port;
+ err = netconn_apimsg(lwip_netconn_do_connect, &API_MSG_VAR_REF(msg));
+ API_MSG_VAR_FREE(msg);
+
+ return err;
+}
+
+/**
+ * @ingroup netconn_udp
+ * Disconnect a netconn from its current peer (only valid for UDP netconns).
+ *
+ * @param conn the netconn to disconnect
+ * @return See @ref err_t
+ */
+err_t
+netconn_disconnect(struct netconn *conn)
+{
+ API_MSG_VAR_DECLARE(msg);
+ err_t err;
+
+ LWIP_ERROR("netconn_disconnect: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ API_MSG_VAR_ALLOC(msg);
+ API_MSG_VAR_REF(msg).conn = conn;
+ err = netconn_apimsg(lwip_netconn_do_disconnect, &API_MSG_VAR_REF(msg));
+ API_MSG_VAR_FREE(msg);
+
+ return err;
+}
+
+/**
+ * @ingroup netconn_tcp
+ * Set a TCP netconn into listen mode
+ *
+ * @param conn the tcp netconn to set to listen mode
+ * @param backlog the listen backlog, only used if TCP_LISTEN_BACKLOG==1
+ * @return ERR_OK if the netconn was set to listen (UDP and RAW netconns
+ * don't return any error (yet?))
+ */
+err_t
+netconn_listen_with_backlog(struct netconn *conn, u8_t backlog)
+{
+#if LWIP_TCP
+ API_MSG_VAR_DECLARE(msg);
+ err_t err;
+
+ /* This does no harm. If TCP_LISTEN_BACKLOG is off, backlog is unused. */
+ LWIP_UNUSED_ARG(backlog);
+
+ LWIP_ERROR("netconn_listen: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ API_MSG_VAR_ALLOC(msg);
+ API_MSG_VAR_REF(msg).conn = conn;
+#if TCP_LISTEN_BACKLOG
+ API_MSG_VAR_REF(msg).msg.lb.backlog = backlog;
+#endif /* TCP_LISTEN_BACKLOG */
+ err = netconn_apimsg(lwip_netconn_do_listen, &API_MSG_VAR_REF(msg));
+ API_MSG_VAR_FREE(msg);
+
+ return err;
+#else /* LWIP_TCP */
+ LWIP_UNUSED_ARG(conn);
+ LWIP_UNUSED_ARG(backlog);
+ return ERR_ARG;
+#endif /* LWIP_TCP */
+}
+
+/**
+ * @ingroup netconn_tcp
+ * Accept a new connection on a TCP listening netconn.
+ *
+ * @param conn the TCP listen netconn
+ * @param new_conn pointer where the new connection is stored
+ * @return ERR_OK if a new connection has been received or an error
+ * code otherwise
+ */
+err_t
+netconn_accept(struct netconn *conn, struct netconn **new_conn)
+{
+#if LWIP_TCP
+ err_t err;
+ void *accept_ptr;
+ struct netconn *newconn;
+#if TCP_LISTEN_BACKLOG
+ API_MSG_VAR_DECLARE(msg);
+#endif /* TCP_LISTEN_BACKLOG */
+
+ LWIP_ERROR("netconn_accept: invalid pointer", (new_conn != NULL), return ERR_ARG;);
+ *new_conn = NULL;
+ LWIP_ERROR("netconn_accept: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ /* NOTE: Although the opengroup spec says a pending error shall be returned to
+ send/recv/getsockopt(SO_ERROR) only, we return it for listening
+ connections also, to handle embedded-system errors */
+ err = netconn_err(conn);
+ if (err != ERR_OK) {
+ /* return pending error */
+ return err;
+ }
+ if (!NETCONN_ACCEPTMBOX_WAITABLE(conn)) {
+ /* don't accept if closed: this might block the application task
+ waiting on acceptmbox forever! */
+ return ERR_CLSD;
+ }
+
+ API_MSG_VAR_ALLOC_ACCEPT(msg);
+
+ NETCONN_MBOX_WAITING_INC(conn);
+ if (netconn_is_nonblocking(conn)) {
+ if (sys_arch_mbox_tryfetch(&conn->acceptmbox, &accept_ptr) == SYS_ARCH_TIMEOUT) {
+ API_MSG_VAR_FREE_ACCEPT(msg);
+ NETCONN_MBOX_WAITING_DEC(conn);
+ return ERR_WOULDBLOCK;
+ }
+ } else {
+#if LWIP_SO_RCVTIMEO
+ if (sys_arch_mbox_fetch(&conn->acceptmbox, &accept_ptr, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
+ API_MSG_VAR_FREE_ACCEPT(msg);
+ NETCONN_MBOX_WAITING_DEC(conn);
+ return ERR_TIMEOUT;
+ }
+#else
+ sys_arch_mbox_fetch(&conn->acceptmbox, &accept_ptr, 0);
+#endif /* LWIP_SO_RCVTIMEO*/
+ }
+ NETCONN_MBOX_WAITING_DEC(conn);
+#if LWIP_NETCONN_FULLDUPLEX
+ if (conn->flags & NETCONN_FLAG_MBOXINVALID) {
+ if (lwip_netconn_is_deallocated_msg(accept_ptr)) {
+ /* the netconn has been closed from another thread */
+ API_MSG_VAR_FREE_ACCEPT(msg);
+ return ERR_CONN;
+ }
+ }
+#endif
+
+ /* Register event with callback */
+ API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);
+
+ if (lwip_netconn_is_err_msg(accept_ptr, &err)) {
+ /* a connection has been aborted: e.g. out of pcbs or out of netconns during accept */
+ API_MSG_VAR_FREE_ACCEPT(msg);
+ return err;
+ }
+ if (accept_ptr == NULL) {
+ /* connection has been aborted */
+ API_MSG_VAR_FREE_ACCEPT(msg);
+ return ERR_CLSD;
+ }
+ newconn = (struct netconn *)accept_ptr;
+#if TCP_LISTEN_BACKLOG
+ /* Let the stack know that we have accepted the connection. */
+ API_MSG_VAR_REF(msg).conn = newconn;
+ /* don't care for the return value of lwip_netconn_do_recv */
+ netconn_apimsg(lwip_netconn_do_accepted, &API_MSG_VAR_REF(msg));
+ API_MSG_VAR_FREE(msg);
+#endif /* TCP_LISTEN_BACKLOG */
+
+ *new_conn = newconn;
+ /* don't set conn->last_err: it's only ERR_OK, anyway */
+ return ERR_OK;
+#else /* LWIP_TCP */
+ LWIP_UNUSED_ARG(conn);
+ LWIP_UNUSED_ARG(new_conn);
+ return ERR_ARG;
+#endif /* LWIP_TCP */
+}
+
+/**
+ * @ingroup netconn_common
+ * Receive data: actual implementation that doesn't care whether pbuf or netbuf
+ * is received (this is internal, it's just here for describing common errors)
+ *
+ * @param conn the netconn from which to receive data
+ * @param new_buf pointer where a new pbuf/netbuf is stored when received data
+ * @param apiflags flags that control function behaviour. For now only:
+ * - NETCONN_DONTBLOCK: only read data that is available now, don't wait for more data
+ * @return ERR_OK if data has been received, an error code otherwise (timeout,
+ * memory error or another error)
+ * ERR_CONN if not connected
+ * ERR_CLSD if TCP connection has been closed
+ * ERR_WOULDBLOCK if the netconn is nonblocking but would block to wait for data
+ * ERR_TIMEOUT if the netconn has a receive timeout and no data was received
+ */
+static err_t
+netconn_recv_data(struct netconn *conn, void **new_buf, u8_t apiflags)
+{
+ void *buf = NULL;
+ u16_t len;
+
+ LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;);
+ *new_buf = NULL;
+ LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ if (!NETCONN_RECVMBOX_WAITABLE(conn)) {
+ err_t err = netconn_err(conn);
+ if (err != ERR_OK) {
+ /* return pending error */
+ return err;
+ }
+ return ERR_CONN;
+ }
+
+ NETCONN_MBOX_WAITING_INC(conn);
+ if (netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK) ||
+ (conn->flags & NETCONN_FLAG_MBOXCLOSED) || (conn->pending_err != ERR_OK)) {
+ if (sys_arch_mbox_tryfetch(&conn->recvmbox, &buf) == SYS_ARCH_TIMEOUT) {
+ err_t err;
+ NETCONN_MBOX_WAITING_DEC(conn);
+ err = netconn_err(conn);
+ if (err != ERR_OK) {
+ /* return pending error */
+ return err;
+ }
+ if (conn->flags & NETCONN_FLAG_MBOXCLOSED) {
+ return ERR_CONN;
+ }
+ return ERR_WOULDBLOCK;
+ }
+ } else {
+#if LWIP_SO_RCVTIMEO
+ if (sys_arch_mbox_fetch(&conn->recvmbox, &buf, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
+ NETCONN_MBOX_WAITING_DEC(conn);
+ return ERR_TIMEOUT;
+ }
+#else
+ sys_arch_mbox_fetch(&conn->recvmbox, &buf, 0);
+#endif /* LWIP_SO_RCVTIMEO*/
+ }
+ NETCONN_MBOX_WAITING_DEC(conn);
+#if LWIP_NETCONN_FULLDUPLEX
+ if (conn->flags & NETCONN_FLAG_MBOXINVALID) {
+ if (lwip_netconn_is_deallocated_msg(buf)) {
+ /* the netconn has been closed from another thread */
+ API_MSG_VAR_FREE_ACCEPT(msg);
+ return ERR_CONN;
+ }
+ }
+#endif
+
+#if LWIP_TCP
+#if (LWIP_UDP || LWIP_RAW)
+ if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
+#endif /* (LWIP_UDP || LWIP_RAW) */
+ {
+ err_t err;
+ /* Check if this is an error message or a pbuf */
+ if (lwip_netconn_is_err_msg(buf, &err)) {
+ /* new_buf has been zeroed above already */
+ if (err == ERR_CLSD) {
+ /* connection closed translates to ERR_OK with *new_buf == NULL */
+ return ERR_OK;
+ }
+ return err;
+ }
+ len = ((struct pbuf *)buf)->tot_len;
+ }
+#endif /* LWIP_TCP */
+#if LWIP_TCP && (LWIP_UDP || LWIP_RAW)
+ else
+#endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */
+#if (LWIP_UDP || LWIP_RAW)
+ {
+ LWIP_ASSERT("buf != NULL", buf != NULL);
+ len = netbuf_len((struct netbuf *)buf);
+ }
+#endif /* (LWIP_UDP || LWIP_RAW) */
+
+#if LWIP_SO_RCVBUF
+ SYS_ARCH_DEC(conn->recv_avail, len);
+#endif /* LWIP_SO_RCVBUF */
+ /* Register event with callback */
+ API_EVENT(conn, NETCONN_EVT_RCVMINUS, len);
+
+ LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_recv_data: received %p, len=%"U16_F"\n", buf, len));
+
+ *new_buf = buf;
+ /* don't set conn->last_err: it's only ERR_OK, anyway */
+ return ERR_OK;
+}
+
+#if LWIP_TCP
+static err_t
+netconn_tcp_recvd_msg(struct netconn *conn, size_t len, struct api_msg *msg)
+{
+ LWIP_ERROR("netconn_recv_tcp_pbuf: invalid conn", (conn != NULL) &&
+ NETCONNTYPE_GROUP(netconn_type(conn)) == NETCONN_TCP, return ERR_ARG;);
+
+ msg->conn = conn;
+ msg->msg.r.len = len;
+
+ return netconn_apimsg(lwip_netconn_do_recv, msg);
+}
+
+err_t
+netconn_tcp_recvd(struct netconn *conn, size_t len)
+{
+ err_t err;
+ API_MSG_VAR_DECLARE(msg);
+ LWIP_ERROR("netconn_recv_tcp_pbuf: invalid conn", (conn != NULL) &&
+ NETCONNTYPE_GROUP(netconn_type(conn)) == NETCONN_TCP, return ERR_ARG;);
+
+ API_MSG_VAR_ALLOC(msg);
+ err = netconn_tcp_recvd_msg(conn, len, &API_VAR_REF(msg));
+ API_MSG_VAR_FREE(msg);
+ return err;
+}
+
+static err_t
+netconn_recv_data_tcp(struct netconn *conn, struct pbuf **new_buf, u8_t apiflags)
+{
+ err_t err;
+ struct pbuf *buf;
+ API_MSG_VAR_DECLARE(msg);
+#if LWIP_MPU_COMPATIBLE
+ msg = NULL;
+#endif
+
+ if (!NETCONN_RECVMBOX_WAITABLE(conn)) {
+ /* This only happens when calling this function more than once *after* receiving FIN */
+ return ERR_CONN;
+ }
+ if (netconn_is_flag_set(conn, NETCONN_FIN_RX_PENDING)) {
+ netconn_clear_flags(conn, NETCONN_FIN_RX_PENDING);
+ goto handle_fin;
+ }
+
+ if (!(apiflags & NETCONN_NOAUTORCVD)) {
+ /* need to allocate API message here so empty message pool does not result in event loss
+ * see bug #47512: MPU_COMPATIBLE may fail on empty pool */
+ API_MSG_VAR_ALLOC(msg);
+ }
+
+ err = netconn_recv_data(conn, (void **)new_buf, apiflags);
+ if (err != ERR_OK) {
+ if (!(apiflags & NETCONN_NOAUTORCVD)) {
+ API_MSG_VAR_FREE(msg);
+ }
+ return err;
+ }
+ buf = *new_buf;
+ if (!(apiflags & NETCONN_NOAUTORCVD)) {
+ /* Let the stack know that we have taken the data. */
+ u16_t len = buf ? buf->tot_len : 1;
+ /* don't care for the return value of lwip_netconn_do_recv */
+ /* @todo: this should really be fixed, e.g. by retrying in poll on error */
+ netconn_tcp_recvd_msg(conn, len, &API_VAR_REF(msg));
+ API_MSG_VAR_FREE(msg);
+ }
+
+ /* If we are closed, we indicate that we no longer wish to use the socket */
+ if (buf == NULL) {
+ if (apiflags & NETCONN_NOFIN) {
+ /* received a FIN but the caller cannot handle it right now:
+ re-enqueue it and return "no data" */
+ netconn_set_flags(conn, NETCONN_FIN_RX_PENDING);
+ return ERR_WOULDBLOCK;
+ } else {
+handle_fin:
+ API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);
+ if (conn->pcb.ip == NULL) {
+ /* race condition: RST during recv */
+ err = netconn_err(conn);
+ if (err != ERR_OK) {
+ return err;
+ }
+ return ERR_RST;
+ }
+ /* RX side is closed, so deallocate the recvmbox */
+ netconn_close_shutdown(conn, NETCONN_SHUT_RD);
+ /* Don' store ERR_CLSD as conn->err since we are only half-closed */
+ return ERR_CLSD;
+ }
+ }
+ return err;
+}
+
+/**
+ * @ingroup netconn_tcp
+ * Receive data (in form of a pbuf) from a TCP netconn
+ *
+ * @param conn the netconn from which to receive data
+ * @param new_buf pointer where a new pbuf is stored when received data
+ * @return ERR_OK if data has been received, an error code otherwise (timeout,
+ * memory error or another error, @see netconn_recv_data)
+ * ERR_ARG if conn is not a TCP netconn
+ */
+err_t
+netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf)
+{
+ LWIP_ERROR("netconn_recv_tcp_pbuf: invalid conn", (conn != NULL) &&
+ NETCONNTYPE_GROUP(netconn_type(conn)) == NETCONN_TCP, return ERR_ARG;);
+
+ return netconn_recv_data_tcp(conn, new_buf, 0);
+}
+
+/**
+ * @ingroup netconn_tcp
+ * Receive data (in form of a pbuf) from a TCP netconn
+ *
+ * @param conn the netconn from which to receive data
+ * @param new_buf pointer where a new pbuf is stored when received data
+ * @param apiflags flags that control function behaviour. For now only:
+ * - NETCONN_DONTBLOCK: only read data that is available now, don't wait for more data
+ * @return ERR_OK if data has been received, an error code otherwise (timeout,
+ * memory error or another error, @see netconn_recv_data)
+ * ERR_ARG if conn is not a TCP netconn
+ */
+err_t
+netconn_recv_tcp_pbuf_flags(struct netconn *conn, struct pbuf **new_buf, u8_t apiflags)
+{
+ LWIP_ERROR("netconn_recv_tcp_pbuf: invalid conn", (conn != NULL) &&
+ NETCONNTYPE_GROUP(netconn_type(conn)) == NETCONN_TCP, return ERR_ARG;);
+
+ return netconn_recv_data_tcp(conn, new_buf, apiflags);
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Receive data (in form of a netbuf) from a UDP or RAW netconn
+ *
+ * @param conn the netconn from which to receive data
+ * @param new_buf pointer where a new netbuf is stored when received data
+ * @return ERR_OK if data has been received, an error code otherwise (timeout,
+ * memory error or another error)
+ * ERR_ARG if conn is not a UDP/RAW netconn
+ */
+err_t
+netconn_recv_udp_raw_netbuf(struct netconn *conn, struct netbuf **new_buf)
+{
+ LWIP_ERROR("netconn_recv_udp_raw_netbuf: invalid conn", (conn != NULL) &&
+ NETCONNTYPE_GROUP(netconn_type(conn)) != NETCONN_TCP, return ERR_ARG;);
+
+ return netconn_recv_data(conn, (void **)new_buf, 0);
+}
+
+/**
+ * Receive data (in form of a netbuf) from a UDP or RAW netconn
+ *
+ * @param conn the netconn from which to receive data
+ * @param new_buf pointer where a new netbuf is stored when received data
+ * @param apiflags flags that control function behaviour. For now only:
+ * - NETCONN_DONTBLOCK: only read data that is available now, don't wait for more data
+ * @return ERR_OK if data has been received, an error code otherwise (timeout,
+ * memory error or another error)
+ * ERR_ARG if conn is not a UDP/RAW netconn
+ */
+err_t
+netconn_recv_udp_raw_netbuf_flags(struct netconn *conn, struct netbuf **new_buf, u8_t apiflags)
+{
+ LWIP_ERROR("netconn_recv_udp_raw_netbuf: invalid conn", (conn != NULL) &&
+ NETCONNTYPE_GROUP(netconn_type(conn)) != NETCONN_TCP, return ERR_ARG;);
+
+ return netconn_recv_data(conn, (void **)new_buf, apiflags);
+}
+
+/**
+ * @ingroup netconn_common
+ * Receive data (in form of a netbuf containing a packet buffer) from a netconn
+ *
+ * @param conn the netconn from which to receive data
+ * @param new_buf pointer where a new netbuf is stored when received data
+ * @return ERR_OK if data has been received, an error code otherwise (timeout,
+ * memory error or another error)
+ */
+err_t
+netconn_recv(struct netconn *conn, struct netbuf **new_buf)
+{
+#if LWIP_TCP
+ struct netbuf *buf = NULL;
+ err_t err;
+#endif /* LWIP_TCP */
+
+ LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;);
+ *new_buf = NULL;
+ LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL), return ERR_ARG;);
+
+#if LWIP_TCP
+#if (LWIP_UDP || LWIP_RAW)
+ if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
+#endif /* (LWIP_UDP || LWIP_RAW) */
+ {
+ struct pbuf *p = NULL;
+ /* This is not a listening netconn, since recvmbox is set */
+
+ buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
+ if (buf == NULL) {
+ return ERR_MEM;
+ }
+
+ err = netconn_recv_data_tcp(conn, &p, 0);
+ if (err != ERR_OK) {
+ memp_free(MEMP_NETBUF, buf);
+ return err;
+ }
+ LWIP_ASSERT("p != NULL", p != NULL);
+
+ buf->p = p;
+ buf->ptr = p;
+ buf->port = 0;
+ ip_addr_set_zero(&buf->addr);
+ *new_buf = buf;
+ /* don't set conn->last_err: it's only ERR_OK, anyway */
+ return ERR_OK;
+ }
+#endif /* LWIP_TCP */
+#if LWIP_TCP && (LWIP_UDP || LWIP_RAW)
+ else
+#endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */
+ {
+#if (LWIP_UDP || LWIP_RAW)
+ return netconn_recv_data(conn, (void **)new_buf, 0);
+#endif /* (LWIP_UDP || LWIP_RAW) */
+ }
+}
+
+/**
+ * @ingroup netconn_udp
+ * Send data (in form of a netbuf) to a specific remote IP address and port.
+ * Only to be used for UDP and RAW netconns (not TCP).
+ *
+ * @param conn the netconn over which to send data
+ * @param buf a netbuf containing the data to send
+ * @param addr the remote IP address to which to send the data
+ * @param port the remote port to which to send the data
+ * @return ERR_OK if data was sent, any other err_t on error
+ */
+err_t
+netconn_sendto(struct netconn *conn, struct netbuf *buf, const ip_addr_t *addr, u16_t port)
+{
+ if (buf != NULL) {
+ ip_addr_set(&buf->addr, addr);
+ buf->port = port;
+ return netconn_send(conn, buf);
+ }
+ return ERR_VAL;
+}
+
+/**
+ * @ingroup netconn_udp
+ * Send data over a UDP or RAW netconn (that is already connected).
+ *
+ * @param conn the UDP or RAW netconn over which to send data
+ * @param buf a netbuf containing the data to send
+ * @return ERR_OK if data was sent, any other err_t on error
+ */
+err_t
+netconn_send(struct netconn *conn, struct netbuf *buf)
+{
+ API_MSG_VAR_DECLARE(msg);
+ err_t err;
+
+ LWIP_ERROR("netconn_send: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_send: sending %"U16_F" bytes\n", buf->p->tot_len));
+
+ API_MSG_VAR_ALLOC(msg);
+ API_MSG_VAR_REF(msg).conn = conn;
+ API_MSG_VAR_REF(msg).msg.b = buf;
+ err = netconn_apimsg(lwip_netconn_do_send, &API_MSG_VAR_REF(msg));
+ API_MSG_VAR_FREE(msg);
+
+ return err;
+}
+
+/**
+ * @ingroup netconn_tcp
+ * Send data over a TCP netconn.
+ *
+ * @param conn the TCP netconn over which to send data
+ * @param dataptr pointer to the application buffer that contains the data to send
+ * @param size size of the application data to send
+ * @param apiflags combination of following flags :
+ * - NETCONN_COPY: data will be copied into memory belonging to the stack
+ * - NETCONN_MORE: for TCP connection, PSH flag will be set on last segment sent
+ * - NETCONN_DONTBLOCK: only write the data if all data can be written at once
+ * @param bytes_written pointer to a location that receives the number of written bytes
+ * @return ERR_OK if data was sent, any other err_t on error
+ */
+err_t
+netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size,
+ u8_t apiflags, size_t *bytes_written)
+{
+ struct netvector vector;
+ vector.ptr = dataptr;
+ vector.len = size;
+ return netconn_write_vectors_partly(conn, &vector, 1, apiflags, bytes_written);
+}
+
+/**
+ * Send vectorized data atomically over a TCP netconn.
+ *
+ * @param conn the TCP netconn over which to send data
+ * @param vectors array of vectors containing data to send
+ * @param vectorcnt number of vectors in the array
+ * @param apiflags combination of following flags :
+ * - NETCONN_COPY: data will be copied into memory belonging to the stack
+ * - NETCONN_MORE: for TCP connection, PSH flag will be set on last segment sent
+ * - NETCONN_DONTBLOCK: only write the data if all data can be written at once
+ * @param bytes_written pointer to a location that receives the number of written bytes
+ * @return ERR_OK if data was sent, any other err_t on error
+ */
+err_t
+netconn_write_vectors_partly(struct netconn *conn, struct netvector *vectors, u16_t vectorcnt,
+ u8_t apiflags, size_t *bytes_written)
+{
+ API_MSG_VAR_DECLARE(msg);
+ err_t err;
+ u8_t dontblock;
+ size_t size;
+ int i;
+
+ LWIP_ERROR("netconn_write: invalid conn", (conn != NULL), return ERR_ARG;);
+ LWIP_ERROR("netconn_write: invalid conn->type", (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP), return ERR_VAL;);
+ dontblock = netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK);
+#if LWIP_SO_SNDTIMEO
+ if (conn->send_timeout != 0) {
+ dontblock = 1;
+ }
+#endif /* LWIP_SO_SNDTIMEO */
+ if (dontblock && !bytes_written) {
+ /* This implies netconn_write() cannot be used for non-blocking send, since
+ it has no way to return the number of bytes written. */
+ return ERR_VAL;
+ }
+
+ /* sum up the total size */
+ size = 0;
+ for (i = 0; i < vectorcnt; i++) {
+ size += vectors[i].len;
+ if (size < vectors[i].len) {
+ /* overflow */
+ return ERR_VAL;
+ }
+ }
+ if (size == 0) {
+ return ERR_OK;
+ } else if (size > SSIZE_MAX) {
+ ssize_t limited;
+ /* this is required by the socket layer (cannot send full size_t range) */
+ if (!bytes_written) {
+ return ERR_VAL;
+ }
+ /* limit the amount of data to send */
+ limited = SSIZE_MAX;
+ size = (size_t)limited;
+ }
+
+ API_MSG_VAR_ALLOC(msg);
+ /* non-blocking write sends as much */
+ API_MSG_VAR_REF(msg).conn = conn;
+ API_MSG_VAR_REF(msg).msg.w.vector = vectors;
+ API_MSG_VAR_REF(msg).msg.w.vector_cnt = vectorcnt;
+ API_MSG_VAR_REF(msg).msg.w.vector_off = 0;
+ API_MSG_VAR_REF(msg).msg.w.apiflags = apiflags;
+ API_MSG_VAR_REF(msg).msg.w.len = size;
+ API_MSG_VAR_REF(msg).msg.w.offset = 0;
+#if LWIP_SO_SNDTIMEO
+ if (conn->send_timeout != 0) {
+ /* get the time we started, which is later compared to
+ sys_now() + conn->send_timeout */
+ API_MSG_VAR_REF(msg).msg.w.time_started = sys_now();
+ } else {
+ API_MSG_VAR_REF(msg).msg.w.time_started = 0;
+ }
+#endif /* LWIP_SO_SNDTIMEO */
+
+ /* For locking the core: this _can_ be delayed on low memory/low send buffer,
+ but if it is, this is done inside api_msg.c:do_write(), so we can use the
+ non-blocking version here. */
+ err = netconn_apimsg(lwip_netconn_do_write, &API_MSG_VAR_REF(msg));
+ if (err == ERR_OK) {
+ if (bytes_written != NULL) {
+ *bytes_written = API_MSG_VAR_REF(msg).msg.w.offset;
+ }
+ /* for blocking, check all requested bytes were written, NOTE: send_timeout is
+ treated as dontblock (see dontblock assignment above) */
+ if (!dontblock) {
+ LWIP_ASSERT("do_write failed to write all bytes", API_MSG_VAR_REF(msg).msg.w.offset == size);
+ }
+ }
+ API_MSG_VAR_FREE(msg);
+
+ return err;
+}
+
+/**
+ * @ingroup netconn_tcp
+ * Close or shutdown a TCP netconn (doesn't delete it).
+ *
+ * @param conn the TCP netconn to close or shutdown
+ * @param how fully close or only shutdown one side?
+ * @return ERR_OK if the netconn was closed, any other err_t on error
+ */
+static err_t
+netconn_close_shutdown(struct netconn *conn, u8_t how)
+{
+ API_MSG_VAR_DECLARE(msg);
+ err_t err;
+ LWIP_UNUSED_ARG(how);
+
+ LWIP_ERROR("netconn_close: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ API_MSG_VAR_ALLOC(msg);
+ API_MSG_VAR_REF(msg).conn = conn;
+#if LWIP_TCP
+ /* shutting down both ends is the same as closing */
+ API_MSG_VAR_REF(msg).msg.sd.shut = how;
+#if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
+ /* get the time we started, which is later compared to
+ sys_now() + conn->send_timeout */
+ API_MSG_VAR_REF(msg).msg.sd.time_started = sys_now();
+#else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
+ API_MSG_VAR_REF(msg).msg.sd.polls_left =
+ ((LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT + TCP_SLOW_INTERVAL - 1) / TCP_SLOW_INTERVAL) + 1;
+#endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
+#endif /* LWIP_TCP */
+ err = netconn_apimsg(lwip_netconn_do_close, &API_MSG_VAR_REF(msg));
+ API_MSG_VAR_FREE(msg);
+
+ return err;
+}
+
+/**
+ * @ingroup netconn_tcp
+ * Close a TCP netconn (doesn't delete it).
+ *
+ * @param conn the TCP netconn to close
+ * @return ERR_OK if the netconn was closed, any other err_t on error
+ */
+err_t
+netconn_close(struct netconn *conn)
+{
+ /* shutting down both ends is the same as closing */
+ return netconn_close_shutdown(conn, NETCONN_SHUT_RDWR);
+}
+
+/**
+ * @ingroup netconn_common
+ * Get and reset pending error on a netconn
+ *
+ * @param conn the netconn to get the error from
+ * @return and pending error or ERR_OK if no error was pending
+ */
+err_t
+netconn_err(struct netconn *conn)
+{
+ err_t err;
+ SYS_ARCH_DECL_PROTECT(lev);
+ if (conn == NULL) {
+ return ERR_OK;
+ }
+ SYS_ARCH_PROTECT(lev);
+ err = conn->pending_err;
+ conn->pending_err = ERR_OK;
+ SYS_ARCH_UNPROTECT(lev);
+ return err;
+}
+
+/**
+ * @ingroup netconn_tcp
+ * Shut down one or both sides of a TCP netconn (doesn't delete it).
+ *
+ * @param conn the TCP netconn to shut down
+ * @param shut_rx shut down the RX side (no more read possible after this)
+ * @param shut_tx shut down the TX side (no more write possible after this)
+ * @return ERR_OK if the netconn was closed, any other err_t on error
+ */
+err_t
+netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx)
+{
+ return netconn_close_shutdown(conn, (u8_t)((shut_rx ? NETCONN_SHUT_RD : 0) | (shut_tx ? NETCONN_SHUT_WR : 0)));
+}
+
+#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
+/**
+ * @ingroup netconn_udp
+ * Join multicast groups for UDP netconns.
+ *
+ * @param conn the UDP netconn for which to change multicast addresses
+ * @param multiaddr IP address of the multicast group to join or leave
+ * @param netif_addr the IP address of the network interface on which to send
+ * the igmp message
+ * @param join_or_leave flag whether to send a join- or leave-message
+ * @return ERR_OK if the action was taken, any err_t on error
+ */
+err_t
+netconn_join_leave_group(struct netconn *conn,
+ const ip_addr_t *multiaddr,
+ const ip_addr_t *netif_addr,
+ enum netconn_igmp join_or_leave)
+{
+ API_MSG_VAR_DECLARE(msg);
+ err_t err;
+
+ LWIP_ERROR("netconn_join_leave_group: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ API_MSG_VAR_ALLOC(msg);
+
+#if LWIP_IPV4
+ /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
+ if (multiaddr == NULL) {
+ multiaddr = IP4_ADDR_ANY;
+ }
+ if (netif_addr == NULL) {
+ netif_addr = IP4_ADDR_ANY;
+ }
+#endif /* LWIP_IPV4 */
+
+ API_MSG_VAR_REF(msg).conn = conn;
+ API_MSG_VAR_REF(msg).msg.jl.multiaddr = API_MSG_VAR_REF(multiaddr);
+ API_MSG_VAR_REF(msg).msg.jl.netif_addr = API_MSG_VAR_REF(netif_addr);
+ API_MSG_VAR_REF(msg).msg.jl.join_or_leave = join_or_leave;
+ err = netconn_apimsg(lwip_netconn_do_join_leave_group, &API_MSG_VAR_REF(msg));
+ API_MSG_VAR_FREE(msg);
+
+ return err;
+}
+/**
+ * @ingroup netconn_udp
+ * Join multicast groups for UDP netconns.
+ *
+ * @param conn the UDP netconn for which to change multicast addresses
+ * @param multiaddr IP address of the multicast group to join or leave
+ * @param if_idx the index of the netif
+ * @param join_or_leave flag whether to send a join- or leave-message
+ * @return ERR_OK if the action was taken, any err_t on error
+ */
+err_t
+netconn_join_leave_group_netif(struct netconn *conn,
+ const ip_addr_t *multiaddr,
+ u8_t if_idx,
+ enum netconn_igmp join_or_leave)
+{
+ API_MSG_VAR_DECLARE(msg);
+ err_t err;
+
+ LWIP_ERROR("netconn_join_leave_group: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ API_MSG_VAR_ALLOC(msg);
+
+#if LWIP_IPV4
+ /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
+ if (multiaddr == NULL) {
+ multiaddr = IP4_ADDR_ANY;
+ }
+ if (if_idx == NETIF_NO_INDEX) {
+ return ERR_IF;
+ }
+#endif /* LWIP_IPV4 */
+
+ API_MSG_VAR_REF(msg).conn = conn;
+ API_MSG_VAR_REF(msg).msg.jl.multiaddr = API_MSG_VAR_REF(multiaddr);
+ API_MSG_VAR_REF(msg).msg.jl.if_idx = if_idx;
+ API_MSG_VAR_REF(msg).msg.jl.join_or_leave = join_or_leave;
+ err = netconn_apimsg(lwip_netconn_do_join_leave_group_netif, &API_MSG_VAR_REF(msg));
+ API_MSG_VAR_FREE(msg);
+
+ return err;
+}
+#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
+
+#if LWIP_DNS
+/**
+ * @ingroup netconn_common
+ * Execute a DNS query, only one IP address is returned
+ *
+ * @param name a string representation of the DNS host name to query
+ * @param addr a preallocated ip_addr_t where to store the resolved IP address
+ * @param dns_addrtype IP address type (IPv4 / IPv6)
+ * @return ERR_OK: resolving succeeded
+ * ERR_MEM: memory error, try again later
+ * ERR_ARG: dns client not initialized or invalid hostname
+ * ERR_VAL: dns server response was invalid
+ */
+#if LWIP_IPV4 && LWIP_IPV6
+err_t
+netconn_gethostbyname_addrtype(const char *name, ip_addr_t *addr, u8_t dns_addrtype)
+#else
+err_t
+netconn_gethostbyname(const char *name, ip_addr_t *addr)
+#endif
+{
+ API_VAR_DECLARE(struct dns_api_msg, msg);
+#if !LWIP_MPU_COMPATIBLE
+ sys_sem_t sem;
+#endif /* LWIP_MPU_COMPATIBLE */
+ err_t err;
+ err_t cberr;
+
+ LWIP_ERROR("netconn_gethostbyname: invalid name", (name != NULL), return ERR_ARG;);
+ LWIP_ERROR("netconn_gethostbyname: invalid addr", (addr != NULL), return ERR_ARG;);
+#if LWIP_MPU_COMPATIBLE
+ if (strlen(name) >= DNS_MAX_NAME_LENGTH) {
+ return ERR_ARG;
+ }
+#endif
+
+#ifdef LWIP_HOOK_NETCONN_EXTERNAL_RESOLVE
+#if LWIP_IPV4 && LWIP_IPV6
+ if (LWIP_HOOK_NETCONN_EXTERNAL_RESOLVE(name, addr, dns_addrtype, &err)) {
+#else
+ if (LWIP_HOOK_NETCONN_EXTERNAL_RESOLVE(name, addr, NETCONN_DNS_DEFAULT, &err)) {
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+ return err;
+ }
+#endif /* LWIP_HOOK_NETCONN_EXTERNAL_RESOLVE */
+
+ API_VAR_ALLOC(struct dns_api_msg, MEMP_DNS_API_MSG, msg, ERR_MEM);
+#if LWIP_MPU_COMPATIBLE
+ strncpy(API_VAR_REF(msg).name, name, DNS_MAX_NAME_LENGTH - 1);
+ API_VAR_REF(msg).name[DNS_MAX_NAME_LENGTH - 1] = 0;
+#else /* LWIP_MPU_COMPATIBLE */
+ msg.err = &err;
+ msg.sem = &sem;
+ API_VAR_REF(msg).addr = API_VAR_REF(addr);
+ API_VAR_REF(msg).name = name;
+#endif /* LWIP_MPU_COMPATIBLE */
+#if LWIP_IPV4 && LWIP_IPV6
+ API_VAR_REF(msg).dns_addrtype = dns_addrtype;
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+#if LWIP_NETCONN_SEM_PER_THREAD
+ API_VAR_REF(msg).sem = LWIP_NETCONN_THREAD_SEM_GET();
+#else /* LWIP_NETCONN_SEM_PER_THREAD*/
+ err = sys_sem_new(API_EXPR_REF(API_VAR_REF(msg).sem), 0);
+ if (err != ERR_OK) {
+ API_VAR_FREE(MEMP_DNS_API_MSG, msg);
+ return err;
+ }
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+ cberr = tcpip_send_msg_wait_sem(lwip_netconn_do_gethostbyname, &API_VAR_REF(msg), API_EXPR_REF(API_VAR_REF(msg).sem));
+#if !LWIP_NETCONN_SEM_PER_THREAD
+ sys_sem_free(API_EXPR_REF(API_VAR_REF(msg).sem));
+#endif /* !LWIP_NETCONN_SEM_PER_THREAD */
+ if (cberr != ERR_OK) {
+ API_VAR_FREE(MEMP_DNS_API_MSG, msg);
+ return cberr;
+ }
+
+#if LWIP_MPU_COMPATIBLE
+ *addr = msg->addr;
+ err = msg->err;
+#endif /* LWIP_MPU_COMPATIBLE */
+
+ API_VAR_FREE(MEMP_DNS_API_MSG, msg);
+ return err;
+}
+#endif /* LWIP_DNS*/
+
+#if LWIP_NETCONN_SEM_PER_THREAD
+void
+netconn_thread_init(void)
+{
+ sys_sem_t *sem = LWIP_NETCONN_THREAD_SEM_GET();
+ if ((sem == NULL) || !sys_sem_valid(sem)) {
+ /* call alloc only once */
+ LWIP_NETCONN_THREAD_SEM_ALLOC();
+ LWIP_ASSERT("LWIP_NETCONN_THREAD_SEM_ALLOC() failed", sys_sem_valid(LWIP_NETCONN_THREAD_SEM_GET()));
+ }
+}
+
+void
+netconn_thread_cleanup(void)
+{
+ sys_sem_t *sem = LWIP_NETCONN_THREAD_SEM_GET();
+ if ((sem != NULL) && sys_sem_valid(sem)) {
+ /* call free only once */
+ LWIP_NETCONN_THREAD_SEM_FREE();
+ }
+}
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+#endif /* LWIP_NETCONN */
diff --git a/resources/ethernet/LwIP/api/api_msg.c b/resources/ethernet/LwIP/api/api_msg.c
new file mode 100644
index 00000000..8952baaf
--- /dev/null
+++ b/resources/ethernet/LwIP/api/api_msg.c
@@ -0,0 +1,2173 @@
+/**
+ * @file
+ * Sequential API Internal module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/priv/api_msg.h"
+
+#include "lwip/ip.h"
+#include "lwip/ip_addr.h"
+#include "lwip/udp.h"
+#include "lwip/tcp.h"
+#include "lwip/raw.h"
+
+#include "lwip/memp.h"
+#include "lwip/igmp.h"
+#include "lwip/dns.h"
+#include "lwip/mld6.h"
+#include "lwip/priv/tcpip_priv.h"
+
+#include
+
+/* netconns are polled once per second (e.g. continue write on memory error) */
+#define NETCONN_TCP_POLL_INTERVAL 2
+
+#define SET_NONBLOCKING_CONNECT(conn, val) do { if (val) { \
+ netconn_set_flags(conn, NETCONN_FLAG_IN_NONBLOCKING_CONNECT); \
+} else { \
+ netconn_clear_flags(conn, NETCONN_FLAG_IN_NONBLOCKING_CONNECT); }} while(0)
+#define IN_NONBLOCKING_CONNECT(conn) netconn_is_flag_set(conn, NETCONN_FLAG_IN_NONBLOCKING_CONNECT)
+
+#if LWIP_NETCONN_FULLDUPLEX
+#define NETCONN_MBOX_VALID(conn, mbox) (sys_mbox_valid(mbox) && ((conn->flags & NETCONN_FLAG_MBOXINVALID) == 0))
+#else
+#define NETCONN_MBOX_VALID(conn, mbox) sys_mbox_valid(mbox)
+#endif
+
+/* forward declarations */
+#if LWIP_TCP
+#if LWIP_TCPIP_CORE_LOCKING
+#define WRITE_DELAYED , 1
+#define WRITE_DELAYED_PARAM , u8_t delayed
+#else /* LWIP_TCPIP_CORE_LOCKING */
+#define WRITE_DELAYED
+#define WRITE_DELAYED_PARAM
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+static err_t lwip_netconn_do_writemore(struct netconn *conn WRITE_DELAYED_PARAM);
+static err_t lwip_netconn_do_close_internal(struct netconn *conn WRITE_DELAYED_PARAM);
+#endif
+
+static void netconn_drain(struct netconn *conn);
+
+#if LWIP_TCPIP_CORE_LOCKING
+#define TCPIP_APIMSG_ACK(m)
+#else /* LWIP_TCPIP_CORE_LOCKING */
+#define TCPIP_APIMSG_ACK(m) do { sys_sem_signal(LWIP_API_MSG_SEM(m)); } while(0)
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+#if LWIP_NETCONN_FULLDUPLEX
+const u8_t netconn_deleted = 0;
+
+int
+lwip_netconn_is_deallocated_msg(void *msg)
+{
+ if (msg == &netconn_deleted) {
+ return 1;
+ }
+ return 0;
+}
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+
+#if LWIP_TCP
+const u8_t netconn_aborted = 0;
+const u8_t netconn_reset = 0;
+const u8_t netconn_closed = 0;
+
+/** Translate an error to a unique void* passed via an mbox */
+static void *
+lwip_netconn_err_to_msg(err_t err)
+{
+ switch (err) {
+ case ERR_ABRT:
+ return LWIP_CONST_CAST(void *, &netconn_aborted);
+ case ERR_RST:
+ return LWIP_CONST_CAST(void *, &netconn_reset);
+ case ERR_CLSD:
+ return LWIP_CONST_CAST(void *, &netconn_closed);
+ default:
+ LWIP_ASSERT("unhandled error", err == ERR_OK);
+ return NULL;
+ }
+}
+
+int
+lwip_netconn_is_err_msg(void *msg, err_t *err)
+{
+ LWIP_ASSERT("err != NULL", err != NULL);
+
+ if (msg == &netconn_aborted) {
+ *err = ERR_ABRT;
+ return 1;
+ } else if (msg == &netconn_reset) {
+ *err = ERR_RST;
+ return 1;
+ } else if (msg == &netconn_closed) {
+ *err = ERR_CLSD;
+ return 1;
+ }
+ return 0;
+}
+#endif /* LWIP_TCP */
+
+
+#if LWIP_RAW
+/**
+ * Receive callback function for RAW netconns.
+ * Doesn't 'eat' the packet, only copies it and sends it to
+ * conn->recvmbox
+ *
+ * @see raw.h (struct raw_pcb.recv) for parameters and return value
+ */
+static u8_t
+recv_raw(void *arg, struct raw_pcb *pcb, struct pbuf *p,
+ const ip_addr_t *addr)
+{
+ struct pbuf *q;
+ struct netbuf *buf;
+ struct netconn *conn;
+
+ LWIP_UNUSED_ARG(addr);
+ conn = (struct netconn *)arg;
+
+ if ((conn != NULL) && NETCONN_MBOX_VALID(conn, &conn->recvmbox)) {
+#if LWIP_SO_RCVBUF
+ int recv_avail;
+ SYS_ARCH_GET(conn->recv_avail, recv_avail);
+ if ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize) {
+ return 0;
+ }
+#endif /* LWIP_SO_RCVBUF */
+ /* copy the whole packet into new pbufs */
+ q = pbuf_clone(PBUF_RAW, PBUF_RAM, p);
+ if (q != NULL) {
+ u16_t len;
+ buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
+ if (buf == NULL) {
+ pbuf_free(q);
+ return 0;
+ }
+
+ buf->p = q;
+ buf->ptr = q;
+ ip_addr_copy(buf->addr, *ip_current_src_addr());
+ buf->port = pcb->protocol;
+
+ len = q->tot_len;
+ if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) {
+ netbuf_delete(buf);
+ return 0;
+ } else {
+#if LWIP_SO_RCVBUF
+ SYS_ARCH_INC(conn->recv_avail, len);
+#endif /* LWIP_SO_RCVBUF */
+ /* Register event with callback */
+ API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
+ }
+ }
+ }
+
+ return 0; /* do not eat the packet */
+}
+#endif /* LWIP_RAW*/
+
+#if LWIP_UDP
+/**
+ * Receive callback function for UDP netconns.
+ * Posts the packet to conn->recvmbox or deletes it on memory error.
+ *
+ * @see udp.h (struct udp_pcb.recv) for parameters
+ */
+static void
+recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p,
+ const ip_addr_t *addr, u16_t port)
+{
+ struct netbuf *buf;
+ struct netconn *conn;
+ u16_t len;
+#if LWIP_SO_RCVBUF
+ int recv_avail;
+#endif /* LWIP_SO_RCVBUF */
+
+ LWIP_UNUSED_ARG(pcb); /* only used for asserts... */
+ LWIP_ASSERT("recv_udp must have a pcb argument", pcb != NULL);
+ LWIP_ASSERT("recv_udp must have an argument", arg != NULL);
+ conn = (struct netconn *)arg;
+
+ if (conn == NULL) {
+ pbuf_free(p);
+ return;
+ }
+
+ LWIP_ASSERT("recv_udp: recv for wrong pcb!", conn->pcb.udp == pcb);
+
+#if LWIP_SO_RCVBUF
+ SYS_ARCH_GET(conn->recv_avail, recv_avail);
+ if (!NETCONN_MBOX_VALID(conn, &conn->recvmbox) ||
+ ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize)) {
+#else /* LWIP_SO_RCVBUF */
+ if (!NETCONN_MBOX_VALID(conn, &conn->recvmbox)) {
+#endif /* LWIP_SO_RCVBUF */
+ pbuf_free(p);
+ return;
+ }
+
+ buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
+ if (buf == NULL) {
+ pbuf_free(p);
+ return;
+ } else {
+ buf->p = p;
+ buf->ptr = p;
+ ip_addr_set(&buf->addr, addr);
+ buf->port = port;
+#if LWIP_NETBUF_RECVINFO
+ if (conn->flags & NETCONN_FLAG_PKTINFO) {
+ /* get the UDP header - always in the first pbuf, ensured by udp_input */
+ const struct udp_hdr *udphdr = (const struct udp_hdr *)ip_next_header_ptr();
+ buf->flags = NETBUF_FLAG_DESTADDR;
+ ip_addr_set(&buf->toaddr, ip_current_dest_addr());
+ buf->toport_chksum = udphdr->dest;
+ }
+#endif /* LWIP_NETBUF_RECVINFO */
+ }
+
+ len = p->tot_len;
+ if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) {
+ netbuf_delete(buf);
+ return;
+ } else {
+#if LWIP_SO_RCVBUF
+ SYS_ARCH_INC(conn->recv_avail, len);
+#endif /* LWIP_SO_RCVBUF */
+ /* Register event with callback */
+ API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
+ }
+}
+#endif /* LWIP_UDP */
+
+#if LWIP_TCP
+/**
+ * Receive callback function for TCP netconns.
+ * Posts the packet to conn->recvmbox, but doesn't delete it on errors.
+ *
+ * @see tcp.h (struct tcp_pcb.recv) for parameters and return value
+ */
+static err_t
+recv_tcp(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
+{
+ struct netconn *conn;
+ u16_t len;
+ void *msg;
+
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_ASSERT("recv_tcp must have a pcb argument", pcb != NULL);
+ LWIP_ASSERT("recv_tcp must have an argument", arg != NULL);
+ LWIP_ASSERT("err != ERR_OK unhandled", err == ERR_OK);
+ LWIP_UNUSED_ARG(err); /* for LWIP_NOASSERT */
+ conn = (struct netconn *)arg;
+
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+ LWIP_ASSERT("recv_tcp: recv for wrong pcb!", conn->pcb.tcp == pcb);
+
+ if (!NETCONN_MBOX_VALID(conn, &conn->recvmbox)) {
+ /* recvmbox already deleted */
+ if (p != NULL) {
+ tcp_recved(pcb, p->tot_len);
+ pbuf_free(p);
+ }
+ return ERR_OK;
+ }
+ /* Unlike for UDP or RAW pcbs, don't check for available space
+ using recv_avail since that could break the connection
+ (data is already ACKed) */
+
+ if (p != NULL) {
+ msg = p;
+ len = p->tot_len;
+ } else {
+ msg = LWIP_CONST_CAST(void *, &netconn_closed);
+ len = 0;
+ }
+
+ if (sys_mbox_trypost(&conn->recvmbox, msg) != ERR_OK) {
+ /* don't deallocate p: it is presented to us later again from tcp_fasttmr! */
+ return ERR_MEM;
+ } else {
+#if LWIP_SO_RCVBUF
+ SYS_ARCH_INC(conn->recv_avail, len);
+#endif /* LWIP_SO_RCVBUF */
+ /* Register event with callback */
+ API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
+ }
+
+ return ERR_OK;
+}
+
+/**
+ * Poll callback function for TCP netconns.
+ * Wakes up an application thread that waits for a connection to close
+ * or data to be sent. The application thread then takes the
+ * appropriate action to go on.
+ *
+ * Signals the conn->sem.
+ * netconn_close waits for conn->sem if closing failed.
+ *
+ * @see tcp.h (struct tcp_pcb.poll) for parameters and return value
+ */
+static err_t
+poll_tcp(void *arg, struct tcp_pcb *pcb)
+{
+ struct netconn *conn = (struct netconn *)arg;
+
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_ASSERT("conn != NULL", (conn != NULL));
+
+ if (conn->state == NETCONN_WRITE) {
+ lwip_netconn_do_writemore(conn WRITE_DELAYED);
+ } else if (conn->state == NETCONN_CLOSE) {
+#if !LWIP_SO_SNDTIMEO && !LWIP_SO_LINGER
+ if (conn->current_msg && conn->current_msg->msg.sd.polls_left) {
+ conn->current_msg->msg.sd.polls_left--;
+ }
+#endif /* !LWIP_SO_SNDTIMEO && !LWIP_SO_LINGER */
+ lwip_netconn_do_close_internal(conn WRITE_DELAYED);
+ }
+ /* @todo: implement connect timeout here? */
+
+ /* Did a nonblocking write fail before? Then check available write-space. */
+ if (conn->flags & NETCONN_FLAG_CHECK_WRITESPACE) {
+ /* If the queued byte- or pbuf-count drops below the configured low-water limit,
+ let select mark this pcb as writable again. */
+ if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) &&
+ (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) {
+ netconn_clear_flags(conn, NETCONN_FLAG_CHECK_WRITESPACE);
+ API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
+ }
+ }
+
+ return ERR_OK;
+}
+
+/**
+ * Sent callback function for TCP netconns.
+ * Signals the conn->sem and calls API_EVENT.
+ * netconn_write waits for conn->sem if send buffer is low.
+ *
+ * @see tcp.h (struct tcp_pcb.sent) for parameters and return value
+ */
+static err_t
+sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len)
+{
+ struct netconn *conn = (struct netconn *)arg;
+
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_ASSERT("conn != NULL", (conn != NULL));
+
+ if (conn) {
+ if (conn->state == NETCONN_WRITE) {
+ lwip_netconn_do_writemore(conn WRITE_DELAYED);
+ } else if (conn->state == NETCONN_CLOSE) {
+ lwip_netconn_do_close_internal(conn WRITE_DELAYED);
+ }
+
+ /* If the queued byte- or pbuf-count drops below the configured low-water limit,
+ let select mark this pcb as writable again. */
+ if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) &&
+ (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) {
+ netconn_clear_flags(conn, NETCONN_FLAG_CHECK_WRITESPACE);
+ API_EVENT(conn, NETCONN_EVT_SENDPLUS, len);
+ }
+ }
+
+ return ERR_OK;
+}
+
+/**
+ * Error callback function for TCP netconns.
+ * Signals conn->sem, posts to all conn mboxes and calls API_EVENT.
+ * The application thread has then to decide what to do.
+ *
+ * @see tcp.h (struct tcp_pcb.err) for parameters
+ */
+static void
+err_tcp(void *arg, err_t err)
+{
+ struct netconn *conn;
+ enum netconn_state old_state;
+ void *mbox_msg;
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ conn = (struct netconn *)arg;
+ LWIP_ASSERT("conn != NULL", (conn != NULL));
+
+ SYS_ARCH_PROTECT(lev);
+
+ /* when err is called, the pcb is deallocated, so delete the reference */
+ conn->pcb.tcp = NULL;
+ /* store pending error */
+ conn->pending_err = err;
+ /* prevent application threads from blocking on 'recvmbox'/'acceptmbox' */
+ conn->flags |= NETCONN_FLAG_MBOXCLOSED;
+
+ /* reset conn->state now before waking up other threads */
+ old_state = conn->state;
+ conn->state = NETCONN_NONE;
+
+ SYS_ARCH_UNPROTECT(lev);
+
+ /* Notify the user layer about a connection error. Used to signal select. */
+ API_EVENT(conn, NETCONN_EVT_ERROR, 0);
+ /* Try to release selects pending on 'read' or 'write', too.
+ They will get an error if they actually try to read or write. */
+ API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+ API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
+
+ mbox_msg = lwip_netconn_err_to_msg(err);
+ /* pass error message to recvmbox to wake up pending recv */
+ if (NETCONN_MBOX_VALID(conn, &conn->recvmbox)) {
+ /* use trypost to prevent deadlock */
+ sys_mbox_trypost(&conn->recvmbox, mbox_msg);
+ }
+ /* pass error message to acceptmbox to wake up pending accept */
+ if (NETCONN_MBOX_VALID(conn, &conn->acceptmbox)) {
+ /* use trypost to preven deadlock */
+ sys_mbox_trypost(&conn->acceptmbox, mbox_msg);
+ }
+
+ if ((old_state == NETCONN_WRITE) || (old_state == NETCONN_CLOSE) ||
+ (old_state == NETCONN_CONNECT)) {
+ /* calling lwip_netconn_do_writemore/lwip_netconn_do_close_internal is not necessary
+ since the pcb has already been deleted! */
+ int was_nonblocking_connect = IN_NONBLOCKING_CONNECT(conn);
+ SET_NONBLOCKING_CONNECT(conn, 0);
+
+ if (!was_nonblocking_connect) {
+ sys_sem_t *op_completed_sem;
+ /* set error return code */
+ LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
+ if (old_state == NETCONN_CLOSE) {
+ /* let close succeed: the connection is closed after all... */
+ conn->current_msg->err = ERR_OK;
+ } else {
+ /* Write and connect fail */
+ conn->current_msg->err = err;
+ }
+ op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
+ LWIP_ASSERT("inavlid op_completed_sem", sys_sem_valid(op_completed_sem));
+ conn->current_msg = NULL;
+ /* wake up the waiting task */
+ sys_sem_signal(op_completed_sem);
+ } else {
+ /* @todo: test what happens for error on nonblocking connect */
+ }
+ } else {
+ LWIP_ASSERT("conn->current_msg == NULL", conn->current_msg == NULL);
+ }
+}
+
+/**
+ * Setup a tcp_pcb with the correct callback function pointers
+ * and their arguments.
+ *
+ * @param conn the TCP netconn to setup
+ */
+static void
+setup_tcp(struct netconn *conn)
+{
+ struct tcp_pcb *pcb;
+
+ pcb = conn->pcb.tcp;
+ tcp_arg(pcb, conn);
+ tcp_recv(pcb, recv_tcp);
+ tcp_sent(pcb, sent_tcp);
+ tcp_poll(pcb, poll_tcp, NETCONN_TCP_POLL_INTERVAL);
+ tcp_err(pcb, err_tcp);
+}
+
+/**
+ * Accept callback function for TCP netconns.
+ * Allocates a new netconn and posts that to conn->acceptmbox.
+ *
+ * @see tcp.h (struct tcp_pcb_listen.accept) for parameters and return value
+ */
+static err_t
+accept_function(void *arg, struct tcp_pcb *newpcb, err_t err)
+{
+ struct netconn *newconn;
+ struct netconn *conn = (struct netconn *)arg;
+
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+ if (!NETCONN_MBOX_VALID(conn, &conn->acceptmbox)) {
+ LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: acceptmbox already deleted\n"));
+ return ERR_VAL;
+ }
+
+ if (newpcb == NULL) {
+ /* out-of-pcbs during connect: pass on this error to the application */
+ if (sys_mbox_trypost(&conn->acceptmbox, lwip_netconn_err_to_msg(ERR_ABRT)) == ERR_OK) {
+ /* Register event with callback */
+ API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+ }
+ return ERR_VAL;
+ }
+ LWIP_ASSERT("expect newpcb == NULL or err == ERR_OK", err == ERR_OK);
+ LWIP_UNUSED_ARG(err); /* for LWIP_NOASSERT */
+
+ LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: newpcb->state: %s\n", tcp_debug_state_str(newpcb->state)));
+
+ /* We have to set the callback here even though
+ * the new socket is unknown. newconn->socket is marked as -1. */
+ newconn = netconn_alloc(conn->type, conn->callback);
+ if (newconn == NULL) {
+ /* outof netconns: pass on this error to the application */
+ if (sys_mbox_trypost(&conn->acceptmbox, lwip_netconn_err_to_msg(ERR_ABRT)) == ERR_OK) {
+ /* Register event with callback */
+ API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+ }
+ return ERR_MEM;
+ }
+ newconn->pcb.tcp = newpcb;
+ setup_tcp(newconn);
+
+ /* handle backlog counter */
+ tcp_backlog_delayed(newpcb);
+
+ if (sys_mbox_trypost(&conn->acceptmbox, newconn) != ERR_OK) {
+ /* When returning != ERR_OK, the pcb is aborted in tcp_process(),
+ so do nothing here! */
+ /* remove all references to this netconn from the pcb */
+ struct tcp_pcb *pcb = newconn->pcb.tcp;
+ tcp_arg(pcb, NULL);
+ tcp_recv(pcb, NULL);
+ tcp_sent(pcb, NULL);
+ tcp_poll(pcb, NULL, 0);
+ tcp_err(pcb, NULL);
+ /* remove reference from to the pcb from this netconn */
+ newconn->pcb.tcp = NULL;
+ /* no need to drain since we know the recvmbox is empty. */
+ sys_mbox_free(&newconn->recvmbox);
+ sys_mbox_set_invalid(&newconn->recvmbox);
+ netconn_free(newconn);
+ return ERR_MEM;
+ } else {
+ /* Register event with callback */
+ API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+ }
+
+ return ERR_OK;
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Create a new pcb of a specific type.
+ * Called from lwip_netconn_do_newconn().
+ *
+ * @param msg the api_msg describing the connection type
+ */
+static void
+pcb_new(struct api_msg *msg)
+{
+ enum lwip_ip_addr_type iptype = IPADDR_TYPE_V4;
+
+ LWIP_ASSERT("pcb_new: pcb already allocated", msg->conn->pcb.tcp == NULL);
+
+#if LWIP_IPV6 && LWIP_IPV4
+ /* IPv6: Dual-stack by default, unless netconn_set_ipv6only() is called */
+ if (NETCONNTYPE_ISIPV6(netconn_type(msg->conn))) {
+ iptype = IPADDR_TYPE_ANY;
+ }
+#endif
+
+ /* Allocate a PCB for this connection */
+ switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ msg->conn->pcb.raw = raw_new_ip_type(iptype, msg->msg.n.proto);
+ if (msg->conn->pcb.raw != NULL) {
+#if LWIP_IPV6
+ /* ICMPv6 packets should always have checksum calculated by the stack as per RFC 3542 chapter 3.1 */
+ if (NETCONNTYPE_ISIPV6(msg->conn->type) && msg->conn->pcb.raw->protocol == IP6_NEXTH_ICMP6) {
+ msg->conn->pcb.raw->chksum_reqd = 1;
+ msg->conn->pcb.raw->chksum_offset = 2;
+ }
+#endif /* LWIP_IPV6 */
+ raw_recv(msg->conn->pcb.raw, recv_raw, msg->conn);
+ }
+ break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ case NETCONN_UDP:
+ msg->conn->pcb.udp = udp_new_ip_type(iptype);
+ if (msg->conn->pcb.udp != NULL) {
+#if LWIP_UDPLITE
+ if (NETCONNTYPE_ISUDPLITE(msg->conn->type)) {
+ udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_UDPLITE);
+ }
+#endif /* LWIP_UDPLITE */
+ if (NETCONNTYPE_ISUDPNOCHKSUM(msg->conn->type)) {
+ udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_NOCHKSUM);
+ }
+ udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn);
+ }
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case NETCONN_TCP:
+ msg->conn->pcb.tcp = tcp_new_ip_type(iptype);
+ if (msg->conn->pcb.tcp != NULL) {
+ setup_tcp(msg->conn);
+ }
+ break;
+#endif /* LWIP_TCP */
+ default:
+ /* Unsupported netconn type, e.g. protocol disabled */
+ msg->err = ERR_VAL;
+ return;
+ }
+ if (msg->conn->pcb.ip == NULL) {
+ msg->err = ERR_MEM;
+ }
+}
+
+/**
+ * Create a new pcb of a specific type inside a netconn.
+ * Called from netconn_new_with_proto_and_callback.
+ *
+ * @param m the api_msg describing the connection type
+ */
+void
+lwip_netconn_do_newconn(void *m)
+{
+ struct api_msg *msg = (struct api_msg *)m;
+
+ msg->err = ERR_OK;
+ if (msg->conn->pcb.tcp == NULL) {
+ pcb_new(msg);
+ }
+ /* Else? This "new" connection already has a PCB allocated. */
+ /* Is this an error condition? Should it be deleted? */
+ /* We currently just are happy and return. */
+
+ TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * Create a new netconn (of a specific type) that has a callback function.
+ * The corresponding pcb is NOT created!
+ *
+ * @param t the type of 'connection' to create (@see enum netconn_type)
+ * @param callback a function to call on status changes (RX available, TX'ed)
+ * @return a newly allocated struct netconn or
+ * NULL on memory error
+ */
+struct netconn *
+netconn_alloc(enum netconn_type t, netconn_callback callback)
+{
+ struct netconn *conn;
+ int size;
+ u8_t init_flags = 0;
+
+ conn = (struct netconn *)memp_malloc(MEMP_NETCONN);
+ if (conn == NULL) {
+ return NULL;
+ }
+
+ conn->pending_err = ERR_OK;
+ conn->type = t;
+ conn->pcb.tcp = NULL;
+
+ /* If all sizes are the same, every compiler should optimize this switch to nothing */
+ switch (NETCONNTYPE_GROUP(t)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ size = DEFAULT_RAW_RECVMBOX_SIZE;
+ break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ case NETCONN_UDP:
+ size = DEFAULT_UDP_RECVMBOX_SIZE;
+#if LWIP_NETBUF_RECVINFO
+ init_flags |= NETCONN_FLAG_PKTINFO;
+#endif /* LWIP_NETBUF_RECVINFO */
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case NETCONN_TCP:
+ size = DEFAULT_TCP_RECVMBOX_SIZE;
+ break;
+#endif /* LWIP_TCP */
+ default:
+ LWIP_ASSERT("netconn_alloc: undefined netconn_type", 0);
+ goto free_and_return;
+ }
+
+ if (sys_mbox_new(&conn->recvmbox, size) != ERR_OK) {
+ goto free_and_return;
+ }
+#if !LWIP_NETCONN_SEM_PER_THREAD
+ if (sys_sem_new(&conn->op_completed, 0) != ERR_OK) {
+ sys_mbox_free(&conn->recvmbox);
+ goto free_and_return;
+ }
+#endif
+
+#if LWIP_TCP
+ sys_mbox_set_invalid(&conn->acceptmbox);
+#endif
+ conn->state = NETCONN_NONE;
+#if LWIP_SOCKET
+ /* initialize socket to -1 since 0 is a valid socket */
+ conn->socket = -1;
+#endif /* LWIP_SOCKET */
+ conn->callback = callback;
+#if LWIP_TCP
+ conn->current_msg = NULL;
+#endif /* LWIP_TCP */
+#if LWIP_SO_SNDTIMEO
+ conn->send_timeout = 0;
+#endif /* LWIP_SO_SNDTIMEO */
+#if LWIP_SO_RCVTIMEO
+ conn->recv_timeout = 0;
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+ conn->recv_bufsize = RECV_BUFSIZE_DEFAULT;
+ conn->recv_avail = 0;
+#endif /* LWIP_SO_RCVBUF */
+#if LWIP_SO_LINGER
+ conn->linger = -1;
+#endif /* LWIP_SO_LINGER */
+ conn->flags = init_flags;
+ return conn;
+free_and_return:
+ memp_free(MEMP_NETCONN, conn);
+ return NULL;
+}
+
+/**
+ * Delete a netconn and all its resources.
+ * The pcb is NOT freed (since we might not be in the right thread context do this).
+ *
+ * @param conn the netconn to free
+ */
+void
+netconn_free(struct netconn *conn)
+{
+ LWIP_ASSERT("PCB must be deallocated outside this function", conn->pcb.tcp == NULL);
+
+#if LWIP_NETCONN_FULLDUPLEX
+ /* in fullduplex, netconn is drained here */
+ netconn_drain(conn);
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+
+ LWIP_ASSERT("recvmbox must be deallocated before calling this function",
+ !sys_mbox_valid(&conn->recvmbox));
+#if LWIP_TCP
+ LWIP_ASSERT("acceptmbox must be deallocated before calling this function",
+ !sys_mbox_valid(&conn->acceptmbox));
+#endif /* LWIP_TCP */
+
+#if !LWIP_NETCONN_SEM_PER_THREAD
+ sys_sem_free(&conn->op_completed);
+ sys_sem_set_invalid(&conn->op_completed);
+#endif
+
+ memp_free(MEMP_NETCONN, conn);
+}
+
+/**
+ * Delete rcvmbox and acceptmbox of a netconn and free the left-over data in
+ * these mboxes
+ *
+ * @param conn the netconn to free
+ * @bytes_drained bytes drained from recvmbox
+ * @accepts_drained pending connections drained from acceptmbox
+ */
+static void
+netconn_drain(struct netconn *conn)
+{
+ void *mem;
+
+ /* This runs when mbox and netconn are marked as closed,
+ so we don't need to lock against rx packets */
+#if LWIP_NETCONN_FULLDUPLEX
+ LWIP_ASSERT("netconn marked closed", conn->flags & NETCONN_FLAG_MBOXINVALID);
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+
+ /* Delete and drain the recvmbox. */
+ if (sys_mbox_valid(&conn->recvmbox)) {
+ while (sys_mbox_tryfetch(&conn->recvmbox, &mem) != SYS_MBOX_EMPTY) {
+#if LWIP_NETCONN_FULLDUPLEX
+ if (!lwip_netconn_is_deallocated_msg(mem))
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+ {
+#if LWIP_TCP
+ if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) {
+ err_t err;
+ if (!lwip_netconn_is_err_msg(mem, &err)) {
+ pbuf_free((struct pbuf *)mem);
+ }
+ } else
+#endif /* LWIP_TCP */
+ {
+ netbuf_delete((struct netbuf *)mem);
+ }
+ }
+ }
+ sys_mbox_free(&conn->recvmbox);
+ sys_mbox_set_invalid(&conn->recvmbox);
+ }
+
+ /* Delete and drain the acceptmbox. */
+#if LWIP_TCP
+ if (sys_mbox_valid(&conn->acceptmbox)) {
+ while (sys_mbox_tryfetch(&conn->acceptmbox, &mem) != SYS_MBOX_EMPTY) {
+#if LWIP_NETCONN_FULLDUPLEX
+ if (!lwip_netconn_is_deallocated_msg(mem))
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+ {
+ err_t err;
+ if (!lwip_netconn_is_err_msg(mem, &err)) {
+ struct netconn *newconn = (struct netconn *)mem;
+ /* Only tcp pcbs have an acceptmbox, so no need to check conn->type */
+ /* pcb might be set to NULL already by err_tcp() */
+ /* drain recvmbox */
+ netconn_drain(newconn);
+ if (newconn->pcb.tcp != NULL) {
+ tcp_abort(newconn->pcb.tcp);
+ newconn->pcb.tcp = NULL;
+ }
+ netconn_free(newconn);
+ }
+ }
+ }
+ sys_mbox_free(&conn->acceptmbox);
+ sys_mbox_set_invalid(&conn->acceptmbox);
+ }
+#endif /* LWIP_TCP */
+}
+
+#if LWIP_NETCONN_FULLDUPLEX
+static void
+netconn_mark_mbox_invalid(struct netconn *conn)
+{
+ int i, num_waiting;
+ void *msg = LWIP_CONST_CAST(void *, &netconn_deleted);
+
+ /* Prevent new calls/threads from reading from the mbox */
+ conn->flags |= NETCONN_FLAG_MBOXINVALID;
+
+ SYS_ARCH_LOCKED(num_waiting = conn->mbox_threads_waiting);
+ for (i = 0; i < num_waiting; i++) {
+ if (sys_mbox_valid_val(conn->recvmbox)) {
+ sys_mbox_trypost(&conn->recvmbox, msg);
+ } else {
+ sys_mbox_trypost(&conn->acceptmbox, msg);
+ }
+ }
+}
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+
+#if LWIP_TCP
+/**
+ * Internal helper function to close a TCP netconn: since this sometimes
+ * doesn't work at the first attempt, this function is called from multiple
+ * places.
+ *
+ * @param conn the TCP netconn to close
+ */
+static err_t
+lwip_netconn_do_close_internal(struct netconn *conn WRITE_DELAYED_PARAM)
+{
+ err_t err;
+ u8_t shut, shut_rx, shut_tx, shut_close;
+ u8_t close_finished = 0;
+ struct tcp_pcb *tpcb;
+#if LWIP_SO_LINGER
+ u8_t linger_wait_required = 0;
+#endif /* LWIP_SO_LINGER */
+
+ LWIP_ASSERT("invalid conn", (conn != NULL));
+ LWIP_ASSERT("this is for tcp netconns only", (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP));
+ LWIP_ASSERT("conn must be in state NETCONN_CLOSE", (conn->state == NETCONN_CLOSE));
+ LWIP_ASSERT("pcb already closed", (conn->pcb.tcp != NULL));
+ LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
+
+ tpcb = conn->pcb.tcp;
+ shut = conn->current_msg->msg.sd.shut;
+ shut_rx = shut & NETCONN_SHUT_RD;
+ shut_tx = shut & NETCONN_SHUT_WR;
+ /* shutting down both ends is the same as closing
+ (also if RD or WR side was shut down before already) */
+ if (shut == NETCONN_SHUT_RDWR) {
+ shut_close = 1;
+ } else if (shut_rx &&
+ ((tpcb->state == FIN_WAIT_1) ||
+ (tpcb->state == FIN_WAIT_2) ||
+ (tpcb->state == CLOSING))) {
+ shut_close = 1;
+ } else if (shut_tx && ((tpcb->flags & TF_RXCLOSED) != 0)) {
+ shut_close = 1;
+ } else {
+ shut_close = 0;
+ }
+
+ /* Set back some callback pointers */
+ if (shut_close) {
+ tcp_arg(tpcb, NULL);
+ }
+ if (tpcb->state == LISTEN) {
+ tcp_accept(tpcb, NULL);
+ } else {
+ /* some callbacks have to be reset if tcp_close is not successful */
+ if (shut_rx) {
+ tcp_recv(tpcb, NULL);
+ tcp_accept(tpcb, NULL);
+ }
+ if (shut_tx) {
+ tcp_sent(tpcb, NULL);
+ }
+ if (shut_close) {
+ tcp_poll(tpcb, NULL, 0);
+ tcp_err(tpcb, NULL);
+ }
+ }
+ /* Try to close the connection */
+ if (shut_close) {
+#if LWIP_SO_LINGER
+ /* check linger possibilites before calling tcp_close */
+ err = ERR_OK;
+ /* linger enabled/required at all? (i.e. is there untransmitted data left?) */
+ if ((conn->linger >= 0) && (conn->pcb.tcp->unsent || conn->pcb.tcp->unacked)) {
+ if ((conn->linger == 0)) {
+ /* data left but linger prevents waiting */
+ tcp_abort(tpcb);
+ tpcb = NULL;
+ } else if (conn->linger > 0) {
+ /* data left and linger says we should wait */
+ if (netconn_is_nonblocking(conn)) {
+ /* data left on a nonblocking netconn -> cannot linger */
+ err = ERR_WOULDBLOCK;
+ } else if ((s32_t)(sys_now() - conn->current_msg->msg.sd.time_started) >=
+ (conn->linger * 1000)) {
+ /* data left but linger timeout has expired (this happens on further
+ calls to this function through poll_tcp */
+ tcp_abort(tpcb);
+ tpcb = NULL;
+ } else {
+ /* data left -> need to wait for ACK after successful close */
+ linger_wait_required = 1;
+ }
+ }
+ }
+ if ((err == ERR_OK) && (tpcb != NULL))
+#endif /* LWIP_SO_LINGER */
+ {
+ err = tcp_close(tpcb);
+ }
+ } else {
+ err = tcp_shutdown(tpcb, shut_rx, shut_tx);
+ }
+ if (err == ERR_OK) {
+ close_finished = 1;
+#if LWIP_SO_LINGER
+ if (linger_wait_required) {
+ /* wait for ACK of all unsent/unacked data by just getting called again */
+ close_finished = 0;
+ err = ERR_INPROGRESS;
+ }
+#endif /* LWIP_SO_LINGER */
+ } else {
+ if (err == ERR_MEM) {
+ /* Closing failed because of memory shortage, try again later. Even for
+ nonblocking netconns, we have to wait since no standard socket application
+ is prepared for close failing because of resource shortage.
+ Check the timeout: this is kind of an lwip addition to the standard sockets:
+ we wait for some time when failing to allocate a segment for the FIN */
+#if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
+ s32_t close_timeout = LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT;
+#if LWIP_SO_SNDTIMEO
+ if (conn->send_timeout > 0) {
+ close_timeout = conn->send_timeout;
+ }
+#endif /* LWIP_SO_SNDTIMEO */
+#if LWIP_SO_LINGER
+ if (conn->linger >= 0) {
+ /* use linger timeout (seconds) */
+ close_timeout = conn->linger * 1000U;
+ }
+#endif
+ if ((s32_t)(sys_now() - conn->current_msg->msg.sd.time_started) >= close_timeout) {
+#else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
+ if (conn->current_msg->msg.sd.polls_left == 0) {
+#endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
+ close_finished = 1;
+ if (shut_close) {
+ /* in this case, we want to RST the connection */
+ tcp_abort(tpcb);
+ err = ERR_OK;
+ }
+ }
+ } else {
+ /* Closing failed for a non-memory error: give up */
+ close_finished = 1;
+ }
+ }
+ if (close_finished) {
+ /* Closing done (succeeded, non-memory error, nonblocking error or timeout) */
+ sys_sem_t *op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
+ conn->current_msg->err = err;
+ conn->current_msg = NULL;
+ conn->state = NETCONN_NONE;
+ if (err == ERR_OK) {
+ if (shut_close) {
+ /* Set back some callback pointers as conn is going away */
+ conn->pcb.tcp = NULL;
+ /* Trigger select() in socket layer. Make sure everybody notices activity
+ on the connection, error first! */
+ API_EVENT(conn, NETCONN_EVT_ERROR, 0);
+ }
+ if (shut_rx) {
+ API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+ }
+ if (shut_tx) {
+ API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
+ }
+ }
+#if LWIP_TCPIP_CORE_LOCKING
+ if (delayed)
+#endif
+ {
+ /* wake up the application task */
+ sys_sem_signal(op_completed_sem);
+ }
+ return ERR_OK;
+ }
+ if (!close_finished) {
+ /* Closing failed and we want to wait: restore some of the callbacks */
+ /* Closing of listen pcb will never fail! */
+ LWIP_ASSERT("Closing a listen pcb may not fail!", (tpcb->state != LISTEN));
+ if (shut_tx) {
+ tcp_sent(tpcb, sent_tcp);
+ }
+ /* when waiting for close, set up poll interval to 500ms */
+ tcp_poll(tpcb, poll_tcp, 1);
+ tcp_err(tpcb, err_tcp);
+ tcp_arg(tpcb, conn);
+ /* don't restore recv callback: we don't want to receive any more data */
+ }
+ /* If closing didn't succeed, we get called again either
+ from poll_tcp or from sent_tcp */
+ LWIP_ASSERT("err != ERR_OK", err != ERR_OK);
+ return err;
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Delete the pcb inside a netconn.
+ * Called from netconn_delete.
+ *
+ * @param m the api_msg pointing to the connection
+ */
+void
+lwip_netconn_do_delconn(void *m)
+{
+ struct api_msg *msg = (struct api_msg *)m;
+
+ enum netconn_state state = msg->conn->state;
+ LWIP_ASSERT("netconn state error", /* this only happens for TCP netconns */
+ (state == NETCONN_NONE) || (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP));
+#if LWIP_NETCONN_FULLDUPLEX
+ /* In full duplex mode, blocking write/connect is aborted with ERR_CLSD */
+ if (state != NETCONN_NONE) {
+ if ((state == NETCONN_WRITE) ||
+ ((state == NETCONN_CONNECT) && !IN_NONBLOCKING_CONNECT(msg->conn))) {
+ /* close requested, abort running write/connect */
+ sys_sem_t *op_completed_sem;
+ LWIP_ASSERT("msg->conn->current_msg != NULL", msg->conn->current_msg != NULL);
+ op_completed_sem = LWIP_API_MSG_SEM(msg->conn->current_msg);
+ msg->conn->current_msg->err = ERR_CLSD;
+ msg->conn->current_msg = NULL;
+ msg->conn->state = NETCONN_NONE;
+ sys_sem_signal(op_completed_sem);
+ }
+ }
+#else /* LWIP_NETCONN_FULLDUPLEX */
+ if (((state != NETCONN_NONE) &&
+ (state != NETCONN_LISTEN) &&
+ (state != NETCONN_CONNECT)) ||
+ ((state == NETCONN_CONNECT) && !IN_NONBLOCKING_CONNECT(msg->conn))) {
+ /* This means either a blocking write or blocking connect is running
+ (nonblocking write returns and sets state to NONE) */
+ msg->err = ERR_INPROGRESS;
+ } else
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+ {
+ LWIP_ASSERT("blocking connect in progress",
+ (state != NETCONN_CONNECT) || IN_NONBLOCKING_CONNECT(msg->conn));
+ msg->err = ERR_OK;
+#if LWIP_NETCONN_FULLDUPLEX
+ /* Mark mboxes invalid */
+ netconn_mark_mbox_invalid(msg->conn);
+#else /* LWIP_NETCONN_FULLDUPLEX */
+ netconn_drain(msg->conn);
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+
+ if (msg->conn->pcb.tcp != NULL) {
+
+ switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ raw_remove(msg->conn->pcb.raw);
+ break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ case NETCONN_UDP:
+ msg->conn->pcb.udp->recv_arg = NULL;
+ udp_remove(msg->conn->pcb.udp);
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case NETCONN_TCP:
+ LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL);
+ msg->conn->state = NETCONN_CLOSE;
+ msg->msg.sd.shut = NETCONN_SHUT_RDWR;
+ msg->conn->current_msg = msg;
+#if LWIP_TCPIP_CORE_LOCKING
+ if (lwip_netconn_do_close_internal(msg->conn, 0) != ERR_OK) {
+ LWIP_ASSERT("state!", msg->conn->state == NETCONN_CLOSE);
+ UNLOCK_TCPIP_CORE();
+ sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
+ LOCK_TCPIP_CORE();
+ LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE);
+ }
+#else /* LWIP_TCPIP_CORE_LOCKING */
+ lwip_netconn_do_close_internal(msg->conn);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+ /* API_EVENT is called inside lwip_netconn_do_close_internal, before releasing
+ the application thread, so we can return at this point! */
+ return;
+#endif /* LWIP_TCP */
+ default:
+ break;
+ }
+ msg->conn->pcb.tcp = NULL;
+ }
+ /* tcp netconns don't come here! */
+
+ /* @todo: this lets select make the socket readable and writable,
+ which is wrong! errfd instead? */
+ API_EVENT(msg->conn, NETCONN_EVT_RCVPLUS, 0);
+ API_EVENT(msg->conn, NETCONN_EVT_SENDPLUS, 0);
+ }
+ if (sys_sem_valid(LWIP_API_MSG_SEM(msg))) {
+ TCPIP_APIMSG_ACK(msg);
+ }
+}
+
+/**
+ * Bind a pcb contained in a netconn
+ * Called from netconn_bind.
+ *
+ * @param m the api_msg pointing to the connection and containing
+ * the IP address and port to bind to
+ */
+void
+lwip_netconn_do_bind(void *m)
+{
+ struct api_msg *msg = (struct api_msg *)m;
+ err_t err;
+
+ if (msg->conn->pcb.tcp != NULL) {
+ switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ err = raw_bind(msg->conn->pcb.raw, API_EXPR_REF(msg->msg.bc.ipaddr));
+ break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ case NETCONN_UDP:
+ err = udp_bind(msg->conn->pcb.udp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port);
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case NETCONN_TCP:
+ err = tcp_bind(msg->conn->pcb.tcp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port);
+ break;
+#endif /* LWIP_TCP */
+ default:
+ err = ERR_VAL;
+ break;
+ }
+ } else {
+ err = ERR_VAL;
+ }
+ msg->err = err;
+ TCPIP_APIMSG_ACK(msg);
+}
+/**
+ * Bind a pcb contained in a netconn to an interface
+ * Called from netconn_bind_if.
+ *
+ * @param m the api_msg pointing to the connection and containing
+ * the IP address and port to bind to
+ */
+void
+lwip_netconn_do_bind_if(void *m)
+{
+ struct netif *netif;
+ struct api_msg *msg = (struct api_msg *)m;
+ err_t err;
+
+ netif = netif_get_by_index(msg->msg.bc.if_idx);
+
+ if ((netif != NULL) && (msg->conn->pcb.tcp != NULL)) {
+ err = ERR_OK;
+ switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ raw_bind_netif(msg->conn->pcb.raw, netif);
+ break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ case NETCONN_UDP:
+ udp_bind_netif(msg->conn->pcb.udp, netif);
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case NETCONN_TCP:
+ tcp_bind_netif(msg->conn->pcb.tcp, netif);
+ break;
+#endif /* LWIP_TCP */
+ default:
+ err = ERR_VAL;
+ break;
+ }
+ } else {
+ err = ERR_VAL;
+ }
+ msg->err = err;
+ TCPIP_APIMSG_ACK(msg);
+}
+
+#if LWIP_TCP
+/**
+ * TCP callback function if a connection (opened by tcp_connect/lwip_netconn_do_connect) has
+ * been established (or reset by the remote host).
+ *
+ * @see tcp.h (struct tcp_pcb.connected) for parameters and return values
+ */
+static err_t
+lwip_netconn_do_connected(void *arg, struct tcp_pcb *pcb, err_t err)
+{
+ struct netconn *conn;
+ int was_blocking;
+ sys_sem_t *op_completed_sem = NULL;
+
+ LWIP_UNUSED_ARG(pcb);
+
+ conn = (struct netconn *)arg;
+
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+
+ LWIP_ASSERT("conn->state == NETCONN_CONNECT", conn->state == NETCONN_CONNECT);
+ LWIP_ASSERT("(conn->current_msg != NULL) || conn->in_non_blocking_connect",
+ (conn->current_msg != NULL) || IN_NONBLOCKING_CONNECT(conn));
+
+ if (conn->current_msg != NULL) {
+ conn->current_msg->err = err;
+ op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
+ }
+ if ((NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) && (err == ERR_OK)) {
+ setup_tcp(conn);
+ }
+ was_blocking = !IN_NONBLOCKING_CONNECT(conn);
+ SET_NONBLOCKING_CONNECT(conn, 0);
+ LWIP_ASSERT("blocking connect state error",
+ (was_blocking && op_completed_sem != NULL) ||
+ (!was_blocking && op_completed_sem == NULL));
+ conn->current_msg = NULL;
+ conn->state = NETCONN_NONE;
+ API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
+
+ if (was_blocking) {
+ sys_sem_signal(op_completed_sem);
+ }
+ return ERR_OK;
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Connect a pcb contained inside a netconn
+ * Called from netconn_connect.
+ *
+ * @param m the api_msg pointing to the connection and containing
+ * the IP address and port to connect to
+ */
+void
+lwip_netconn_do_connect(void *m)
+{
+ struct api_msg *msg = (struct api_msg *)m;
+ err_t err;
+
+ if (msg->conn->pcb.tcp == NULL) {
+ /* This may happen when calling netconn_connect() a second time */
+ err = ERR_CLSD;
+ } else {
+ switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ err = raw_connect(msg->conn->pcb.raw, API_EXPR_REF(msg->msg.bc.ipaddr));
+ break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ case NETCONN_UDP:
+ err = udp_connect(msg->conn->pcb.udp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port);
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case NETCONN_TCP:
+ /* Prevent connect while doing any other action. */
+ if (msg->conn->state == NETCONN_CONNECT) {
+ err = ERR_ALREADY;
+ } else if (msg->conn->state != NETCONN_NONE) {
+ err = ERR_ISCONN;
+ } else {
+ setup_tcp(msg->conn);
+ err = tcp_connect(msg->conn->pcb.tcp, API_EXPR_REF(msg->msg.bc.ipaddr),
+ msg->msg.bc.port, lwip_netconn_do_connected);
+ if (err == ERR_OK) {
+ u8_t non_blocking = netconn_is_nonblocking(msg->conn);
+ msg->conn->state = NETCONN_CONNECT;
+ SET_NONBLOCKING_CONNECT(msg->conn, non_blocking);
+ if (non_blocking) {
+ err = ERR_INPROGRESS;
+ } else {
+ msg->conn->current_msg = msg;
+ /* sys_sem_signal() is called from lwip_netconn_do_connected (or err_tcp()),
+ when the connection is established! */
+#if LWIP_TCPIP_CORE_LOCKING
+ LWIP_ASSERT("state!", msg->conn->state == NETCONN_CONNECT);
+ UNLOCK_TCPIP_CORE();
+ sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
+ LOCK_TCPIP_CORE();
+ LWIP_ASSERT("state!", msg->conn->state != NETCONN_CONNECT);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+ return;
+ }
+ }
+ }
+ break;
+#endif /* LWIP_TCP */
+ default:
+ LWIP_ERROR("Invalid netconn type", 0, do {
+ err = ERR_VAL;
+ } while (0));
+ break;
+ }
+ }
+ msg->err = err;
+ /* For all other protocols, netconn_connect() calls netconn_apimsg(),
+ so use TCPIP_APIMSG_ACK() here. */
+ TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * Disconnect a pcb contained inside a netconn
+ * Only used for UDP netconns.
+ * Called from netconn_disconnect.
+ *
+ * @param m the api_msg pointing to the connection to disconnect
+ */
+void
+lwip_netconn_do_disconnect(void *m)
+{
+ struct api_msg *msg = (struct api_msg *)m;
+
+#if LWIP_UDP
+ if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
+ udp_disconnect(msg->conn->pcb.udp);
+ msg->err = ERR_OK;
+ } else
+#endif /* LWIP_UDP */
+ {
+ msg->err = ERR_VAL;
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+
+#if LWIP_TCP
+/**
+ * Set a TCP pcb contained in a netconn into listen mode
+ * Called from netconn_listen.
+ *
+ * @param m the api_msg pointing to the connection
+ */
+void
+lwip_netconn_do_listen(void *m)
+{
+ struct api_msg *msg = (struct api_msg *)m;
+ err_t err;
+
+ if (msg->conn->pcb.tcp != NULL) {
+ if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
+ if (msg->conn->state == NETCONN_NONE) {
+ struct tcp_pcb *lpcb;
+ if (msg->conn->pcb.tcp->state != CLOSED) {
+ /* connection is not closed, cannot listen */
+ err = ERR_VAL;
+ } else {
+ u8_t backlog;
+#if TCP_LISTEN_BACKLOG
+ backlog = msg->msg.lb.backlog;
+#else /* TCP_LISTEN_BACKLOG */
+ backlog = TCP_DEFAULT_LISTEN_BACKLOG;
+#endif /* TCP_LISTEN_BACKLOG */
+#if LWIP_IPV4 && LWIP_IPV6
+ /* "Socket API like" dual-stack support: If IP to listen to is IP6_ADDR_ANY,
+ * and NETCONN_FLAG_IPV6_V6ONLY is NOT set, use IP_ANY_TYPE to listen
+ */
+ if (ip_addr_cmp(&msg->conn->pcb.ip->local_ip, IP6_ADDR_ANY) &&
+ (netconn_get_ipv6only(msg->conn) == 0)) {
+ /* change PCB type to IPADDR_TYPE_ANY */
+ IP_SET_TYPE_VAL(msg->conn->pcb.tcp->local_ip, IPADDR_TYPE_ANY);
+ IP_SET_TYPE_VAL(msg->conn->pcb.tcp->remote_ip, IPADDR_TYPE_ANY);
+ }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+ lpcb = tcp_listen_with_backlog_and_err(msg->conn->pcb.tcp, backlog, &err);
+
+ if (lpcb == NULL) {
+ /* in this case, the old pcb is still allocated */
+ } else {
+ /* delete the recvmbox and allocate the acceptmbox */
+ if (sys_mbox_valid(&msg->conn->recvmbox)) {
+ /** @todo: should we drain the recvmbox here? */
+ sys_mbox_free(&msg->conn->recvmbox);
+ sys_mbox_set_invalid(&msg->conn->recvmbox);
+ }
+ err = ERR_OK;
+ if (!sys_mbox_valid(&msg->conn->acceptmbox)) {
+ err = sys_mbox_new(&msg->conn->acceptmbox, DEFAULT_ACCEPTMBOX_SIZE);
+ }
+ if (err == ERR_OK) {
+ msg->conn->state = NETCONN_LISTEN;
+ msg->conn->pcb.tcp = lpcb;
+ tcp_arg(msg->conn->pcb.tcp, msg->conn);
+ tcp_accept(msg->conn->pcb.tcp, accept_function);
+ } else {
+ /* since the old pcb is already deallocated, free lpcb now */
+ tcp_close(lpcb);
+ msg->conn->pcb.tcp = NULL;
+ }
+ }
+ }
+ } else if (msg->conn->state == NETCONN_LISTEN) {
+ /* already listening, allow updating of the backlog */
+ err = ERR_OK;
+ tcp_backlog_set(msg->conn->pcb.tcp, msg->msg.lb.backlog);
+ } else {
+ err = ERR_CONN;
+ }
+ } else {
+ err = ERR_ARG;
+ }
+ } else {
+ err = ERR_CONN;
+ }
+ msg->err = err;
+ TCPIP_APIMSG_ACK(msg);
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Send some data on a RAW or UDP pcb contained in a netconn
+ * Called from netconn_send
+ *
+ * @param m the api_msg pointing to the connection
+ */
+void
+lwip_netconn_do_send(void *m)
+{
+ struct api_msg *msg = (struct api_msg *)m;
+
+ err_t err = netconn_err(msg->conn);
+ if (err == ERR_OK) {
+ if (msg->conn->pcb.tcp != NULL) {
+ switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ if (ip_addr_isany(&msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) {
+ err = raw_send(msg->conn->pcb.raw, msg->msg.b->p);
+ } else {
+ err = raw_sendto(msg->conn->pcb.raw, msg->msg.b->p, &msg->msg.b->addr);
+ }
+ break;
+#endif
+#if LWIP_UDP
+ case NETCONN_UDP:
+#if LWIP_CHECKSUM_ON_COPY
+ if (ip_addr_isany(&msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) {
+ err = udp_send_chksum(msg->conn->pcb.udp, msg->msg.b->p,
+ msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum);
+ } else {
+ err = udp_sendto_chksum(msg->conn->pcb.udp, msg->msg.b->p,
+ &msg->msg.b->addr, msg->msg.b->port,
+ msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum);
+ }
+#else /* LWIP_CHECKSUM_ON_COPY */
+ if (ip_addr_isany_val(msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) {
+ err = udp_send(msg->conn->pcb.udp, msg->msg.b->p);
+ } else {
+ err = udp_sendto(msg->conn->pcb.udp, msg->msg.b->p, &msg->msg.b->addr, msg->msg.b->port);
+ }
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ break;
+#endif /* LWIP_UDP */
+ default:
+ err = ERR_CONN;
+ break;
+ }
+ } else {
+ err = ERR_CONN;
+ }
+ }
+ msg->err = err;
+ TCPIP_APIMSG_ACK(msg);
+}
+
+#if LWIP_TCP
+/**
+ * Indicate data has been received from a TCP pcb contained in a netconn
+ * Called from netconn_recv
+ *
+ * @param m the api_msg pointing to the connection
+ */
+void
+lwip_netconn_do_recv(void *m)
+{
+ struct api_msg *msg = (struct api_msg *)m;
+
+ msg->err = ERR_OK;
+ if (msg->conn->pcb.tcp != NULL) {
+ if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
+ size_t remaining = msg->msg.r.len;
+ do {
+ u16_t recved = (u16_t)((remaining > 0xffff) ? 0xffff : remaining);
+ tcp_recved(msg->conn->pcb.tcp, recved);
+ remaining -= recved;
+ } while (remaining != 0);
+ }
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+
+#if TCP_LISTEN_BACKLOG
+/** Indicate that a TCP pcb has been accepted
+ * Called from netconn_accept
+ *
+ * @param m the api_msg pointing to the connection
+ */
+void
+lwip_netconn_do_accepted(void *m)
+{
+ struct api_msg *msg = (struct api_msg *)m;
+
+ msg->err = ERR_OK;
+ if (msg->conn->pcb.tcp != NULL) {
+ if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
+ tcp_backlog_accepted(msg->conn->pcb.tcp);
+ }
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+#endif /* TCP_LISTEN_BACKLOG */
+
+/**
+ * See if more data needs to be written from a previous call to netconn_write.
+ * Called initially from lwip_netconn_do_write. If the first call can't send all data
+ * (because of low memory or empty send-buffer), this function is called again
+ * from sent_tcp() or poll_tcp() to send more data. If all data is sent, the
+ * blocking application thread (waiting in netconn_write) is released.
+ *
+ * @param conn netconn (that is currently in state NETCONN_WRITE) to process
+ * @return ERR_OK
+ * ERR_MEM if LWIP_TCPIP_CORE_LOCKING=1 and sending hasn't yet finished
+ */
+static err_t
+lwip_netconn_do_writemore(struct netconn *conn WRITE_DELAYED_PARAM)
+{
+ err_t err;
+ const void *dataptr;
+ u16_t len, available;
+ u8_t write_finished = 0;
+ size_t diff;
+ u8_t dontblock;
+ u8_t apiflags;
+ u8_t write_more;
+
+ LWIP_ASSERT("conn != NULL", conn != NULL);
+ LWIP_ASSERT("conn->state == NETCONN_WRITE", (conn->state == NETCONN_WRITE));
+ LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
+ LWIP_ASSERT("conn->pcb.tcp != NULL", conn->pcb.tcp != NULL);
+ LWIP_ASSERT("conn->current_msg->msg.w.offset < conn->current_msg->msg.w.len",
+ conn->current_msg->msg.w.offset < conn->current_msg->msg.w.len);
+ LWIP_ASSERT("conn->current_msg->msg.w.vector_cnt > 0", conn->current_msg->msg.w.vector_cnt > 0);
+
+ apiflags = conn->current_msg->msg.w.apiflags;
+ dontblock = netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK);
+
+#if LWIP_SO_SNDTIMEO
+ if ((conn->send_timeout != 0) &&
+ ((s32_t)(sys_now() - conn->current_msg->msg.w.time_started) >= conn->send_timeout)) {
+ write_finished = 1;
+ if (conn->current_msg->msg.w.offset == 0) {
+ /* nothing has been written */
+ err = ERR_WOULDBLOCK;
+ } else {
+ /* partial write */
+ err = ERR_OK;
+ }
+ } else
+#endif /* LWIP_SO_SNDTIMEO */
+ {
+ do {
+ dataptr = (const u8_t *)conn->current_msg->msg.w.vector->ptr + conn->current_msg->msg.w.vector_off;
+ diff = conn->current_msg->msg.w.vector->len - conn->current_msg->msg.w.vector_off;
+ if (diff > 0xffffUL) { /* max_u16_t */
+ len = 0xffff;
+ apiflags |= TCP_WRITE_FLAG_MORE;
+ } else {
+ len = (u16_t)diff;
+ }
+ available = tcp_sndbuf(conn->pcb.tcp);
+ if (available < len) {
+ /* don't try to write more than sendbuf */
+ len = available;
+ if (dontblock) {
+ if (!len) {
+ /* set error according to partial write or not */
+ err = (conn->current_msg->msg.w.offset == 0) ? ERR_WOULDBLOCK : ERR_OK;
+ goto err_mem;
+ }
+ } else {
+ apiflags |= TCP_WRITE_FLAG_MORE;
+ }
+ }
+ LWIP_ASSERT("lwip_netconn_do_writemore: invalid length!",
+ ((conn->current_msg->msg.w.vector_off + len) <= conn->current_msg->msg.w.vector->len));
+ /* we should loop around for more sending in the following cases:
+ 1) We couldn't finish the current vector because of 16-bit size limitations.
+ tcp_write() and tcp_sndbuf() both are limited to 16-bit sizes
+ 2) We are sending the remainder of the current vector and have more */
+ if ((len == 0xffff && diff > 0xffffUL) ||
+ (len == (u16_t)diff && conn->current_msg->msg.w.vector_cnt > 1)) {
+ write_more = 1;
+ apiflags |= TCP_WRITE_FLAG_MORE;
+ } else {
+ write_more = 0;
+ }
+ err = tcp_write(conn->pcb.tcp, dataptr, len, apiflags);
+ if (err == ERR_OK) {
+ conn->current_msg->msg.w.offset += len;
+ conn->current_msg->msg.w.vector_off += len;
+ /* check if current vector is finished */
+ if (conn->current_msg->msg.w.vector_off == conn->current_msg->msg.w.vector->len) {
+ conn->current_msg->msg.w.vector_cnt--;
+ /* if we have additional vectors, move on to them */
+ if (conn->current_msg->msg.w.vector_cnt > 0) {
+ conn->current_msg->msg.w.vector++;
+ conn->current_msg->msg.w.vector_off = 0;
+ }
+ }
+ }
+ } while (write_more && err == ERR_OK);
+ /* if OK or memory error, check available space */
+ if ((err == ERR_OK) || (err == ERR_MEM)) {
+err_mem:
+ if (dontblock && (conn->current_msg->msg.w.offset < conn->current_msg->msg.w.len)) {
+ /* non-blocking write did not write everything: mark the pcb non-writable
+ and let poll_tcp check writable space to mark the pcb writable again */
+ API_EVENT(conn, NETCONN_EVT_SENDMINUS, 0);
+ conn->flags |= NETCONN_FLAG_CHECK_WRITESPACE;
+ } else if ((tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT) ||
+ (tcp_sndqueuelen(conn->pcb.tcp) >= TCP_SNDQUEUELOWAT)) {
+ /* The queued byte- or pbuf-count exceeds the configured low-water limit,
+ let select mark this pcb as non-writable. */
+ API_EVENT(conn, NETCONN_EVT_SENDMINUS, 0);
+ }
+ }
+
+ if (err == ERR_OK) {
+ err_t out_err;
+ if ((conn->current_msg->msg.w.offset == conn->current_msg->msg.w.len) || dontblock) {
+ /* return sent length (caller reads length from msg.w.offset) */
+ write_finished = 1;
+ }
+ out_err = tcp_output(conn->pcb.tcp);
+ if (out_err == ERR_RTE) {
+ /* If tcp_output fails because no route is found,
+ don't try writing any more but return the error
+ to the application thread. */
+ err = out_err;
+ write_finished = 1;
+ }
+ } else if (err == ERR_MEM) {
+ /* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called.
+ For blocking sockets, we do NOT return to the application
+ thread, since ERR_MEM is only a temporary error! Non-blocking
+ will remain non-writable until sent_tcp/poll_tcp is called */
+
+ /* tcp_write returned ERR_MEM, try tcp_output anyway */
+ err_t out_err = tcp_output(conn->pcb.tcp);
+ if (out_err == ERR_RTE) {
+ /* If tcp_output fails because no route is found,
+ don't try writing any more but return the error
+ to the application thread. */
+ err = out_err;
+ write_finished = 1;
+ } else if (dontblock) {
+ /* non-blocking write is done on ERR_MEM, set error according
+ to partial write or not */
+ err = (conn->current_msg->msg.w.offset == 0) ? ERR_WOULDBLOCK : ERR_OK;
+ write_finished = 1;
+ }
+ } else {
+ /* On errors != ERR_MEM, we don't try writing any more but return
+ the error to the application thread. */
+ write_finished = 1;
+ }
+ }
+ if (write_finished) {
+ /* everything was written: set back connection state
+ and back to application task */
+ sys_sem_t *op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
+ conn->current_msg->err = err;
+ conn->current_msg = NULL;
+ conn->state = NETCONN_NONE;
+#if LWIP_TCPIP_CORE_LOCKING
+ if (delayed)
+#endif
+ {
+ sys_sem_signal(op_completed_sem);
+ }
+ }
+#if LWIP_TCPIP_CORE_LOCKING
+ else {
+ return ERR_MEM;
+ }
+#endif
+ return ERR_OK;
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Send some data on a TCP pcb contained in a netconn
+ * Called from netconn_write
+ *
+ * @param m the api_msg pointing to the connection
+ */
+void
+lwip_netconn_do_write(void *m)
+{
+ struct api_msg *msg = (struct api_msg *)m;
+
+ err_t err = netconn_err(msg->conn);
+ if (err == ERR_OK) {
+ if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
+#if LWIP_TCP
+ if (msg->conn->state != NETCONN_NONE) {
+ /* netconn is connecting, closing or in blocking write */
+ err = ERR_INPROGRESS;
+ } else if (msg->conn->pcb.tcp != NULL) {
+ msg->conn->state = NETCONN_WRITE;
+ /* set all the variables used by lwip_netconn_do_writemore */
+ LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL);
+ LWIP_ASSERT("msg->msg.w.len != 0", msg->msg.w.len != 0);
+ msg->conn->current_msg = msg;
+#if LWIP_TCPIP_CORE_LOCKING
+ if (lwip_netconn_do_writemore(msg->conn, 0) != ERR_OK) {
+ LWIP_ASSERT("state!", msg->conn->state == NETCONN_WRITE);
+ UNLOCK_TCPIP_CORE();
+ sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
+ LOCK_TCPIP_CORE();
+ LWIP_ASSERT("state!", msg->conn->state != NETCONN_WRITE);
+ }
+#else /* LWIP_TCPIP_CORE_LOCKING */
+ lwip_netconn_do_writemore(msg->conn);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+ /* for both cases: if lwip_netconn_do_writemore was called, don't ACK the APIMSG
+ since lwip_netconn_do_writemore ACKs it! */
+ return;
+ } else {
+ err = ERR_CONN;
+ }
+#else /* LWIP_TCP */
+ err = ERR_VAL;
+#endif /* LWIP_TCP */
+#if (LWIP_UDP || LWIP_RAW)
+ } else {
+ err = ERR_VAL;
+#endif /* (LWIP_UDP || LWIP_RAW) */
+ }
+ }
+ msg->err = err;
+ TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * Return a connection's local or remote address
+ * Called from netconn_getaddr
+ *
+ * @param m the api_msg pointing to the connection
+ */
+void
+lwip_netconn_do_getaddr(void *m)
+{
+ struct api_msg *msg = (struct api_msg *)m;
+
+ if (msg->conn->pcb.ip != NULL) {
+ if (msg->msg.ad.local) {
+ ip_addr_copy(API_EXPR_DEREF(msg->msg.ad.ipaddr),
+ msg->conn->pcb.ip->local_ip);
+ } else {
+ ip_addr_copy(API_EXPR_DEREF(msg->msg.ad.ipaddr),
+ msg->conn->pcb.ip->remote_ip);
+ }
+
+ msg->err = ERR_OK;
+ switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ if (msg->msg.ad.local) {
+ API_EXPR_DEREF(msg->msg.ad.port) = msg->conn->pcb.raw->protocol;
+ } else {
+ /* return an error as connecting is only a helper for upper layers */
+ msg->err = ERR_CONN;
+ }
+ break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ case NETCONN_UDP:
+ if (msg->msg.ad.local) {
+ API_EXPR_DEREF(msg->msg.ad.port) = msg->conn->pcb.udp->local_port;
+ } else {
+ if ((msg->conn->pcb.udp->flags & UDP_FLAGS_CONNECTED) == 0) {
+ msg->err = ERR_CONN;
+ } else {
+ API_EXPR_DEREF(msg->msg.ad.port) = msg->conn->pcb.udp->remote_port;
+ }
+ }
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case NETCONN_TCP:
+ if ((msg->msg.ad.local == 0) &&
+ ((msg->conn->pcb.tcp->state == CLOSED) || (msg->conn->pcb.tcp->state == LISTEN))) {
+ /* pcb is not connected and remote name is requested */
+ msg->err = ERR_CONN;
+ } else {
+ API_EXPR_DEREF(msg->msg.ad.port) = (msg->msg.ad.local ? msg->conn->pcb.tcp->local_port : msg->conn->pcb.tcp->remote_port);
+ }
+ break;
+#endif /* LWIP_TCP */
+ default:
+ LWIP_ASSERT("invalid netconn_type", 0);
+ break;
+ }
+ } else {
+ msg->err = ERR_CONN;
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * Close or half-shutdown a TCP pcb contained in a netconn
+ * Called from netconn_close
+ * In contrast to closing sockets, the netconn is not deallocated.
+ *
+ * @param m the api_msg pointing to the connection
+ */
+void
+lwip_netconn_do_close(void *m)
+{
+ struct api_msg *msg = (struct api_msg *)m;
+
+#if LWIP_TCP
+ enum netconn_state state = msg->conn->state;
+ /* First check if this is a TCP netconn and if it is in a correct state
+ (LISTEN doesn't support half shutdown) */
+ if ((msg->conn->pcb.tcp != NULL) &&
+ (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) &&
+ ((msg->msg.sd.shut == NETCONN_SHUT_RDWR) || (state != NETCONN_LISTEN))) {
+ /* Check if we are in a connected state */
+ if (state == NETCONN_CONNECT) {
+ /* TCP connect in progress: cannot shutdown */
+ msg->err = ERR_CONN;
+ } else if (state == NETCONN_WRITE) {
+#if LWIP_NETCONN_FULLDUPLEX
+ if (msg->msg.sd.shut & NETCONN_SHUT_WR) {
+ /* close requested, abort running write */
+ sys_sem_t *write_completed_sem;
+ LWIP_ASSERT("msg->conn->current_msg != NULL", msg->conn->current_msg != NULL);
+ write_completed_sem = LWIP_API_MSG_SEM(msg->conn->current_msg);
+ msg->conn->current_msg->err = ERR_CLSD;
+ msg->conn->current_msg = NULL;
+ msg->conn->state = NETCONN_NONE;
+ state = NETCONN_NONE;
+ sys_sem_signal(write_completed_sem);
+ } else {
+ LWIP_ASSERT("msg->msg.sd.shut == NETCONN_SHUT_RD", msg->msg.sd.shut == NETCONN_SHUT_RD);
+ /* In this case, let the write continue and do not interfere with
+ conn->current_msg or conn->state! */
+ msg->err = tcp_shutdown(msg->conn->pcb.tcp, 1, 0);
+ }
+ }
+ if (state == NETCONN_NONE) {
+#else /* LWIP_NETCONN_FULLDUPLEX */
+ msg->err = ERR_INPROGRESS;
+ } else {
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+ if (msg->msg.sd.shut & NETCONN_SHUT_RD) {
+#if LWIP_NETCONN_FULLDUPLEX
+ /* Mark mboxes invalid */
+ netconn_mark_mbox_invalid(msg->conn);
+#else /* LWIP_NETCONN_FULLDUPLEX */
+ netconn_drain(msg->conn);
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+ }
+ LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL);
+ msg->conn->state = NETCONN_CLOSE;
+ msg->conn->current_msg = msg;
+#if LWIP_TCPIP_CORE_LOCKING
+ if (lwip_netconn_do_close_internal(msg->conn, 0) != ERR_OK) {
+ LWIP_ASSERT("state!", msg->conn->state == NETCONN_CLOSE);
+ UNLOCK_TCPIP_CORE();
+ sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
+ LOCK_TCPIP_CORE();
+ LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE);
+ }
+#else /* LWIP_TCPIP_CORE_LOCKING */
+ lwip_netconn_do_close_internal(msg->conn);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+ /* for tcp netconns, lwip_netconn_do_close_internal ACKs the message */
+ return;
+ }
+ } else
+#endif /* LWIP_TCP */
+ {
+ msg->err = ERR_CONN;
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+
+#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
+/**
+ * Join multicast groups for UDP netconns.
+ * Called from netconn_join_leave_group
+ *
+ * @param m the api_msg pointing to the connection
+ */
+void
+lwip_netconn_do_join_leave_group(void *m)
+{
+ struct api_msg *msg = (struct api_msg *)m;
+
+ msg->err = ERR_CONN;
+ if (msg->conn->pcb.tcp != NULL) {
+ if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
+#if LWIP_UDP
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+ if (NETCONNTYPE_ISIPV6(msg->conn->type)) {
+ if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
+ msg->err = mld6_joingroup(ip_2_ip6(API_EXPR_REF(msg->msg.jl.netif_addr)),
+ ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
+ } else {
+ msg->err = mld6_leavegroup(ip_2_ip6(API_EXPR_REF(msg->msg.jl.netif_addr)),
+ ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
+ }
+ } else
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+ {
+#if LWIP_IGMP
+ if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
+ msg->err = igmp_joingroup(ip_2_ip4(API_EXPR_REF(msg->msg.jl.netif_addr)),
+ ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr)));
+ } else {
+ msg->err = igmp_leavegroup(ip_2_ip4(API_EXPR_REF(msg->msg.jl.netif_addr)),
+ ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr)));
+ }
+#endif /* LWIP_IGMP */
+ }
+#endif /* LWIP_UDP */
+#if (LWIP_TCP || LWIP_RAW)
+ } else {
+ msg->err = ERR_VAL;
+#endif /* (LWIP_TCP || LWIP_RAW) */
+ }
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+/**
+ * Join multicast groups for UDP netconns.
+ * Called from netconn_join_leave_group_netif
+ *
+ * @param m the api_msg pointing to the connection
+ */
+void
+lwip_netconn_do_join_leave_group_netif(void *m)
+{
+ struct api_msg *msg = (struct api_msg *)m;
+ struct netif *netif;
+
+ netif = netif_get_by_index(msg->msg.jl.if_idx);
+ if (netif == NULL) {
+ msg->err = ERR_IF;
+ goto done;
+ }
+
+ msg->err = ERR_CONN;
+ if (msg->conn->pcb.tcp != NULL) {
+ if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
+#if LWIP_UDP
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+ if (NETCONNTYPE_ISIPV6(msg->conn->type)) {
+ if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
+ msg->err = mld6_joingroup_netif(netif,
+ ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
+ } else {
+ msg->err = mld6_leavegroup_netif(netif,
+ ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
+ }
+ } else
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+ {
+#if LWIP_IGMP
+ if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
+ msg->err = igmp_joingroup_netif(netif,
+ ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr)));
+ } else {
+ msg->err = igmp_leavegroup_netif(netif,
+ ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr)));
+ }
+#endif /* LWIP_IGMP */
+ }
+#endif /* LWIP_UDP */
+#if (LWIP_TCP || LWIP_RAW)
+ } else {
+ msg->err = ERR_VAL;
+#endif /* (LWIP_TCP || LWIP_RAW) */
+ }
+ }
+
+done:
+ TCPIP_APIMSG_ACK(msg);
+}
+#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
+
+#if LWIP_DNS
+/**
+ * Callback function that is called when DNS name is resolved
+ * (or on timeout). A waiting application thread is waked up by
+ * signaling the semaphore.
+ */
+static void
+lwip_netconn_do_dns_found(const char *name, const ip_addr_t *ipaddr, void *arg)
+{
+ struct dns_api_msg *msg = (struct dns_api_msg *)arg;
+
+ /* we trust the internal implementation to be correct :-) */
+ LWIP_UNUSED_ARG(name);
+
+ if (ipaddr == NULL) {
+ /* timeout or memory error */
+ API_EXPR_DEREF(msg->err) = ERR_VAL;
+ } else {
+ /* address was resolved */
+ API_EXPR_DEREF(msg->err) = ERR_OK;
+ API_EXPR_DEREF(msg->addr) = *ipaddr;
+ }
+ /* wake up the application task waiting in netconn_gethostbyname */
+ sys_sem_signal(API_EXPR_REF_SEM(msg->sem));
+}
+
+/**
+ * Execute a DNS query
+ * Called from netconn_gethostbyname
+ *
+ * @param arg the dns_api_msg pointing to the query
+ */
+void
+lwip_netconn_do_gethostbyname(void *arg)
+{
+ struct dns_api_msg *msg = (struct dns_api_msg *)arg;
+ u8_t addrtype =
+#if LWIP_IPV4 && LWIP_IPV6
+ msg->dns_addrtype;
+#else
+ LWIP_DNS_ADDRTYPE_DEFAULT;
+#endif
+
+ API_EXPR_DEREF(msg->err) = dns_gethostbyname_addrtype(msg->name,
+ API_EXPR_REF(msg->addr), lwip_netconn_do_dns_found, msg, addrtype);
+#if LWIP_TCPIP_CORE_LOCKING
+ /* For core locking, only block if we need to wait for answer/timeout */
+ if (API_EXPR_DEREF(msg->err) == ERR_INPROGRESS) {
+ UNLOCK_TCPIP_CORE();
+ sys_sem_wait(API_EXPR_REF_SEM(msg->sem));
+ LOCK_TCPIP_CORE();
+ LWIP_ASSERT("do_gethostbyname still in progress!!", API_EXPR_DEREF(msg->err) != ERR_INPROGRESS);
+ }
+#else /* LWIP_TCPIP_CORE_LOCKING */
+ if (API_EXPR_DEREF(msg->err) != ERR_INPROGRESS) {
+ /* on error or immediate success, wake up the application
+ * task waiting in netconn_gethostbyname */
+ sys_sem_signal(API_EXPR_REF_SEM(msg->sem));
+ }
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+}
+#endif /* LWIP_DNS */
+
+#endif /* LWIP_NETCONN */
diff --git a/resources/ethernet/LwIP/api/err.c b/resources/ethernet/LwIP/api/err.c
new file mode 100644
index 00000000..17e28646
--- /dev/null
+++ b/resources/ethernet/LwIP/api/err.c
@@ -0,0 +1,127 @@
+/**
+ * @file
+ * Error Management module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ */
+
+#include "lwip/err.h"
+#include "lwip/def.h"
+#include "lwip/sys.h"
+
+#include "lwip/errno.h"
+
+
+#if !NO_SYS
+
+//tmp
+#define EHOSTUNREACH 113 /* No route to host */
+#define EINPROGRESS 115 /* Operation now in progress */
+#define EADDRINUSE 98 /* Address already in use */
+#define EALREADY 114 /* Operation already in progress */
+#define EISCONN 106 /* Transport endpoint is already connected */
+#define ENOTCONN 107 /* Transport endpoint is not connected */
+#define ECONNABORTED 103 /* Software caused connection abort */
+
+
+/** Table to quickly map an lwIP error (err_t) to a socket error
+ * by using -err as an index */
+static const int err_to_errno_table[] = {
+ 0, /* ERR_OK 0 No error, everything OK. */
+ ENOMEM, /* ERR_MEM -1 Out of memory error. */
+ ENOBUFS, /* ERR_BUF -2 Buffer error. */
+ EWOULDBLOCK, /* ERR_TIMEOUT -3 Timeout */
+ EHOSTUNREACH, /* ERR_RTE -4 Routing problem. */
+ EINPROGRESS, /* ERR_INPROGRESS -5 Operation in progress */
+ EINVAL, /* ERR_VAL -6 Illegal value. */
+ EWOULDBLOCK, /* ERR_WOULDBLOCK -7 Operation would block. */
+ EADDRINUSE, /* ERR_USE -8 Address in use. */
+ EALREADY, /* ERR_ALREADY -9 Already connecting. */
+ EISCONN, /* ERR_ISCONN -10 Conn already established.*/
+ ENOTCONN, /* ERR_CONN -11 Not connected. */
+ -1, /* ERR_IF -12 Low-level netif error */
+ ECONNABORTED, /* ERR_ABRT -13 Connection aborted. */
+ ECONNRESET, /* ERR_RST -14 Connection reset. */
+ ENOTCONN, /* ERR_CLSD -15 Connection closed. */
+ EIO /* ERR_ARG -16 Illegal argument. */
+};
+
+int
+err_to_errno(err_t err)
+{
+ if ((err > 0) || (-err >= (err_t)LWIP_ARRAYSIZE(err_to_errno_table))) {
+ return EIO;
+ }
+ return err_to_errno_table[-err];
+}
+#endif /* !NO_SYS */
+
+#ifdef LWIP_DEBUG
+
+static const char *err_strerr[] = {
+ "Ok.", /* ERR_OK 0 */
+ "Out of memory error.", /* ERR_MEM -1 */
+ "Buffer error.", /* ERR_BUF -2 */
+ "Timeout.", /* ERR_TIMEOUT -3 */
+ "Routing problem.", /* ERR_RTE -4 */
+ "Operation in progress.", /* ERR_INPROGRESS -5 */
+ "Illegal value.", /* ERR_VAL -6 */
+ "Operation would block.", /* ERR_WOULDBLOCK -7 */
+ "Address in use.", /* ERR_USE -8 */
+ "Already connecting.", /* ERR_ALREADY -9 */
+ "Already connected.", /* ERR_ISCONN -10 */
+ "Not connected.", /* ERR_CONN -11 */
+ "Low-level netif error.", /* ERR_IF -12 */
+ "Connection aborted.", /* ERR_ABRT -13 */
+ "Connection reset.", /* ERR_RST -14 */
+ "Connection closed.", /* ERR_CLSD -15 */
+ "Illegal argument." /* ERR_ARG -16 */
+};
+
+/**
+ * Convert an lwip internal error to a string representation.
+ *
+ * @param err an lwip internal err_t
+ * @return a string representation for err
+ */
+const char *
+lwip_strerr(err_t err)
+{
+ if ((err > 0) || (-err >= (err_t)LWIP_ARRAYSIZE(err_strerr))) {
+ return "Unknown error.";
+ }
+ return err_strerr[-err];
+}
+
+#endif /* LWIP_DEBUG */
diff --git a/resources/ethernet/LwIP/api/if_api.c b/resources/ethernet/LwIP/api/if_api.c
new file mode 100644
index 00000000..2eda9f0b
--- /dev/null
+++ b/resources/ethernet/LwIP/api/if_api.c
@@ -0,0 +1,102 @@
+/**
+ * @file
+ * Interface Identification APIs from:
+ * RFC 3493: Basic Socket Interface Extensions for IPv6
+ * Section 4: Interface Identification
+ *
+ * @defgroup if_api Interface Identification API
+ * @ingroup socket
+ */
+
+/*
+ * Copyright (c) 2017 Joel Cunningham, Garmin International, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Joel Cunningham
+ *
+ */
+#include "lwip/opt.h"
+
+#if LWIP_SOCKET
+
+#include "lwip/errno.h"
+#include "lwip/if_api.h"
+#include "lwip/netifapi.h"
+#include "lwip/priv/sockets_priv.h"
+
+/**
+ * @ingroup if_api
+ * Maps an interface index to its corresponding name.
+ * @param ifindex interface index
+ * @param ifname shall point to a buffer of at least {IF_NAMESIZE} bytes
+ * @return If ifindex is an interface index, then the function shall return the
+ * value supplied in ifname, which points to a buffer now containing the interface name.
+ * Otherwise, the function shall return a NULL pointer.
+ */
+char *
+lwip_if_indextoname(unsigned int ifindex, char *ifname)
+{
+#if LWIP_NETIF_API
+ if (ifindex <= 0xff) {
+ err_t err = netifapi_netif_index_to_name((u8_t)ifindex, ifname);
+ if (!err && ifname[0] != '\0') {
+ return ifname;
+ }
+ }
+#else /* LWIP_NETIF_API */
+ LWIP_UNUSED_ARG(ifindex);
+ LWIP_UNUSED_ARG(ifname);
+#endif /* LWIP_NETIF_API */
+ set_errno(ENXIO);
+ return NULL;
+}
+
+/**
+ * @ingroup if_api
+ * Returs the interface index corresponding to name ifname.
+ * @param ifname Interface name
+ * @return The corresponding index if ifname is the name of an interface;
+ * otherwise, zero.
+ */
+unsigned int
+lwip_if_nametoindex(const char *ifname)
+{
+#if LWIP_NETIF_API
+ err_t err;
+ u8_t idx;
+
+ err = netifapi_netif_name_to_index(ifname, &idx);
+ if (!err) {
+ return idx;
+ }
+#else /* LWIP_NETIF_API */
+ LWIP_UNUSED_ARG(ifname);
+#endif /* LWIP_NETIF_API */
+ return 0; /* invalid index */
+}
+
+#endif /* LWIP_SOCKET */
diff --git a/resources/ethernet/LwIP/api/netbuf.c b/resources/ethernet/LwIP/api/netbuf.c
new file mode 100644
index 00000000..b4644404
--- /dev/null
+++ b/resources/ethernet/LwIP/api/netbuf.c
@@ -0,0 +1,250 @@
+/**
+ * @file
+ * Network buffer management
+ *
+ * @defgroup netbuf Network buffers
+ * @ingroup netconn
+ * Network buffer descriptor for @ref netconn. Based on @ref pbuf internally
+ * to avoid copying data around.\n
+ * Buffers must not be shared accross multiple threads, all functions except
+ * netbuf_new() and netbuf_delete() are not thread-safe.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/netbuf.h"
+#include "lwip/memp.h"
+
+#include
+
+/**
+ * @ingroup netbuf
+ * Create (allocate) and initialize a new netbuf.
+ * The netbuf doesn't yet contain a packet buffer!
+ *
+ * @return a pointer to a new netbuf
+ * NULL on lack of memory
+ */
+struct
+netbuf *netbuf_new(void)
+{
+ struct netbuf *buf;
+
+ buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
+ if (buf != NULL) {
+ memset(buf, 0, sizeof(struct netbuf));
+ }
+ return buf;
+}
+
+/**
+ * @ingroup netbuf
+ * Deallocate a netbuf allocated by netbuf_new().
+ *
+ * @param buf pointer to a netbuf allocated by netbuf_new()
+ */
+void
+netbuf_delete(struct netbuf *buf)
+{
+ if (buf != NULL) {
+ if (buf->p != NULL) {
+ pbuf_free(buf->p);
+ buf->p = buf->ptr = NULL;
+ }
+ memp_free(MEMP_NETBUF, buf);
+ }
+}
+
+/**
+ * @ingroup netbuf
+ * Allocate memory for a packet buffer for a given netbuf.
+ *
+ * @param buf the netbuf for which to allocate a packet buffer
+ * @param size the size of the packet buffer to allocate
+ * @return pointer to the allocated memory
+ * NULL if no memory could be allocated
+ */
+void *
+netbuf_alloc(struct netbuf *buf, u16_t size)
+{
+ LWIP_ERROR("netbuf_alloc: invalid buf", (buf != NULL), return NULL;);
+
+ /* Deallocate any previously allocated memory. */
+ if (buf->p != NULL) {
+ pbuf_free(buf->p);
+ }
+ buf->p = pbuf_alloc(PBUF_TRANSPORT, size, PBUF_RAM);
+ if (buf->p == NULL) {
+ return NULL;
+ }
+ LWIP_ASSERT("check that first pbuf can hold size",
+ (buf->p->len >= size));
+ buf->ptr = buf->p;
+ return buf->p->payload;
+}
+
+/**
+ * @ingroup netbuf
+ * Free the packet buffer included in a netbuf
+ *
+ * @param buf pointer to the netbuf which contains the packet buffer to free
+ */
+void
+netbuf_free(struct netbuf *buf)
+{
+ LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return;);
+ if (buf->p != NULL) {
+ pbuf_free(buf->p);
+ }
+ buf->p = buf->ptr = NULL;
+#if LWIP_CHECKSUM_ON_COPY
+ buf->flags = 0;
+ buf->toport_chksum = 0;
+#endif /* LWIP_CHECKSUM_ON_COPY */
+}
+
+/**
+ * @ingroup netbuf
+ * Let a netbuf reference existing (non-volatile) data.
+ *
+ * @param buf netbuf which should reference the data
+ * @param dataptr pointer to the data to reference
+ * @param size size of the data
+ * @return ERR_OK if data is referenced
+ * ERR_MEM if data couldn't be referenced due to lack of memory
+ */
+err_t
+netbuf_ref(struct netbuf *buf, const void *dataptr, u16_t size)
+{
+ LWIP_ERROR("netbuf_ref: invalid buf", (buf != NULL), return ERR_ARG;);
+ if (buf->p != NULL) {
+ pbuf_free(buf->p);
+ }
+ buf->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF);
+ if (buf->p == NULL) {
+ buf->ptr = NULL;
+ return ERR_MEM;
+ }
+ ((struct pbuf_rom *)buf->p)->payload = dataptr;
+ buf->p->len = buf->p->tot_len = size;
+ buf->ptr = buf->p;
+ return ERR_OK;
+}
+
+/**
+ * @ingroup netbuf
+ * Chain one netbuf to another (@see pbuf_chain)
+ *
+ * @param head the first netbuf
+ * @param tail netbuf to chain after head, freed by this function, may not be reference after returning
+ */
+void
+netbuf_chain(struct netbuf *head, struct netbuf *tail)
+{
+ LWIP_ERROR("netbuf_chain: invalid head", (head != NULL), return;);
+ LWIP_ERROR("netbuf_chain: invalid tail", (tail != NULL), return;);
+ pbuf_cat(head->p, tail->p);
+ head->ptr = head->p;
+ memp_free(MEMP_NETBUF, tail);
+}
+
+/**
+ * @ingroup netbuf
+ * Get the data pointer and length of the data inside a netbuf.
+ *
+ * @param buf netbuf to get the data from
+ * @param dataptr pointer to a void pointer where to store the data pointer
+ * @param len pointer to an u16_t where the length of the data is stored
+ * @return ERR_OK if the information was retrieved,
+ * ERR_BUF on error.
+ */
+err_t
+netbuf_data(struct netbuf *buf, void **dataptr, u16_t *len)
+{
+ LWIP_ERROR("netbuf_data: invalid buf", (buf != NULL), return ERR_ARG;);
+ LWIP_ERROR("netbuf_data: invalid dataptr", (dataptr != NULL), return ERR_ARG;);
+ LWIP_ERROR("netbuf_data: invalid len", (len != NULL), return ERR_ARG;);
+
+ if (buf->ptr == NULL) {
+ return ERR_BUF;
+ }
+ *dataptr = buf->ptr->payload;
+ *len = buf->ptr->len;
+ return ERR_OK;
+}
+
+/**
+ * @ingroup netbuf
+ * Move the current data pointer of a packet buffer contained in a netbuf
+ * to the next part.
+ * The packet buffer itself is not modified.
+ *
+ * @param buf the netbuf to modify
+ * @return -1 if there is no next part
+ * 1 if moved to the next part but now there is no next part
+ * 0 if moved to the next part and there are still more parts
+ */
+s8_t
+netbuf_next(struct netbuf *buf)
+{
+ LWIP_ERROR("netbuf_next: invalid buf", (buf != NULL), return -1;);
+ if (buf->ptr->next == NULL) {
+ return -1;
+ }
+ buf->ptr = buf->ptr->next;
+ if (buf->ptr->next == NULL) {
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * @ingroup netbuf
+ * Move the current data pointer of a packet buffer contained in a netbuf
+ * to the beginning of the packet.
+ * The packet buffer itself is not modified.
+ *
+ * @param buf the netbuf to modify
+ */
+void
+netbuf_first(struct netbuf *buf)
+{
+ LWIP_ERROR("netbuf_first: invalid buf", (buf != NULL), return;);
+ buf->ptr = buf->p;
+}
+
+#endif /* LWIP_NETCONN */
diff --git a/resources/ethernet/LwIP/api/netdb.c b/resources/ethernet/LwIP/api/netdb.c
new file mode 100644
index 00000000..9ffc75a2
--- /dev/null
+++ b/resources/ethernet/LwIP/api/netdb.c
@@ -0,0 +1,414 @@
+/**
+ * @file
+ * API functions for name resolving
+ *
+ * @defgroup netdbapi NETDB API
+ * @ingroup socket
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ *
+ */
+
+#include "lwip/netdb.h"
+
+#if LWIP_DNS && LWIP_SOCKET
+
+#include "lwip/err.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/ip_addr.h"
+#include "lwip/api.h"
+#include "lwip/dns.h"
+
+#include /* memset */
+#include /* atoi */
+
+/** helper struct for gethostbyname_r to access the char* buffer */
+struct gethostbyname_r_helper {
+ ip_addr_t *addr_list[2];
+ ip_addr_t addr;
+ char *aliases;
+};
+
+/** h_errno is exported in netdb.h for access by applications. */
+#if LWIP_DNS_API_DECLARE_H_ERRNO
+int h_errno;
+#endif /* LWIP_DNS_API_DECLARE_H_ERRNO */
+
+/** define "hostent" variables storage: 0 if we use a static (but unprotected)
+ * set of variables for lwip_gethostbyname, 1 if we use a local storage */
+#ifndef LWIP_DNS_API_HOSTENT_STORAGE
+#define LWIP_DNS_API_HOSTENT_STORAGE 0
+#endif
+
+/** define "hostent" variables storage */
+#if LWIP_DNS_API_HOSTENT_STORAGE
+#define HOSTENT_STORAGE
+#else
+#define HOSTENT_STORAGE static
+#endif /* LWIP_DNS_API_STATIC_HOSTENT */
+
+/**
+ * Returns an entry containing addresses of address family AF_INET
+ * for the host with name name.
+ * Due to dns_gethostbyname limitations, only one address is returned.
+ *
+ * @param name the hostname to resolve
+ * @return an entry containing addresses of address family AF_INET
+ * for the host with name name
+ */
+struct hostent *
+lwip_gethostbyname(const char *name)
+{
+ err_t err;
+ ip_addr_t addr;
+
+ /* buffer variables for lwip_gethostbyname() */
+ HOSTENT_STORAGE struct hostent s_hostent;
+ HOSTENT_STORAGE char *s_aliases;
+ HOSTENT_STORAGE ip_addr_t s_hostent_addr;
+ HOSTENT_STORAGE ip_addr_t *s_phostent_addr[2];
+ HOSTENT_STORAGE char s_hostname[DNS_MAX_NAME_LENGTH + 1];
+
+ /* query host IP address */
+ err = netconn_gethostbyname(name, &addr);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err));
+ h_errno = HOST_NOT_FOUND;
+ return NULL;
+ }
+
+ /* fill hostent */
+ s_hostent_addr = addr;
+ s_phostent_addr[0] = &s_hostent_addr;
+ s_phostent_addr[1] = NULL;
+ strncpy(s_hostname, name, DNS_MAX_NAME_LENGTH);
+ s_hostname[DNS_MAX_NAME_LENGTH] = 0;
+ s_hostent.h_name = s_hostname;
+ s_aliases = NULL;
+ s_hostent.h_aliases = &s_aliases;
+ s_hostent.h_addrtype = AF_INET;
+ s_hostent.h_length = sizeof(ip_addr_t);
+ s_hostent.h_addr_list = (char **)&s_phostent_addr;
+
+#if DNS_DEBUG
+ /* dump hostent */
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_name == %s\n", s_hostent.h_name));
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases == %p\n", (void *)s_hostent.h_aliases));
+ /* h_aliases are always empty */
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addrtype == %d\n", s_hostent.h_addrtype));
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_length == %d\n", s_hostent.h_length));
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list == %p\n", (void *)s_hostent.h_addr_list));
+ if (s_hostent.h_addr_list != NULL) {
+ u8_t idx;
+ for (idx = 0; s_hostent.h_addr_list[idx]; idx++) {
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i] == %p\n", idx, s_hostent.h_addr_list[idx]));
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i]-> == %s\n", idx, ipaddr_ntoa((ip_addr_t *)s_hostent.h_addr_list[idx])));
+ }
+ }
+#endif /* DNS_DEBUG */
+
+#if LWIP_DNS_API_HOSTENT_STORAGE
+ /* this function should return the "per-thread" hostent after copy from s_hostent */
+ return sys_thread_hostent(&s_hostent);
+#else
+ return &s_hostent;
+#endif /* LWIP_DNS_API_HOSTENT_STORAGE */
+}
+
+/**
+ * Thread-safe variant of lwip_gethostbyname: instead of using a static
+ * buffer, this function takes buffer and errno pointers as arguments
+ * and uses these for the result.
+ *
+ * @param name the hostname to resolve
+ * @param ret pre-allocated struct where to store the result
+ * @param buf pre-allocated buffer where to store additional data
+ * @param buflen the size of buf
+ * @param result pointer to a hostent pointer that is set to ret on success
+ * and set to zero on error
+ * @param h_errnop pointer to an int where to store errors (instead of modifying
+ * the global h_errno)
+ * @return 0 on success, non-zero on error, additional error information
+ * is stored in *h_errnop instead of h_errno to be thread-safe
+ */
+int
+lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf,
+ size_t buflen, struct hostent **result, int *h_errnop)
+{
+ err_t err;
+ struct gethostbyname_r_helper *h;
+ char *hostname;
+ size_t namelen;
+ int lh_errno;
+
+ if (h_errnop == NULL) {
+ /* ensure h_errnop is never NULL */
+ h_errnop = &lh_errno;
+ }
+
+ if (result == NULL) {
+ /* not all arguments given */
+ *h_errnop = EINVAL;
+ return -1;
+ }
+ /* first thing to do: set *result to nothing */
+ *result = NULL;
+ if ((name == NULL) || (ret == NULL) || (buf == NULL)) {
+ /* not all arguments given */
+ *h_errnop = EINVAL;
+ return -1;
+ }
+
+ namelen = strlen(name);
+ if (buflen < (sizeof(struct gethostbyname_r_helper) + LWIP_MEM_ALIGN_BUFFER(namelen + 1))) {
+ /* buf can't hold the data needed + a copy of name */
+ *h_errnop = ERANGE;
+ return -1;
+ }
+
+ h = (struct gethostbyname_r_helper *)LWIP_MEM_ALIGN(buf);
+ hostname = ((char *)h) + sizeof(struct gethostbyname_r_helper);
+
+ /* query host IP address */
+ err = netconn_gethostbyname(name, &h->addr);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err));
+ *h_errnop = HOST_NOT_FOUND;
+ return -1;
+ }
+
+ /* copy the hostname into buf */
+ MEMCPY(hostname, name, namelen);
+ hostname[namelen] = 0;
+
+ /* fill hostent */
+ h->addr_list[0] = &h->addr;
+ h->addr_list[1] = NULL;
+ h->aliases = NULL;
+ ret->h_name = hostname;
+ ret->h_aliases = &h->aliases;
+ ret->h_addrtype = AF_INET;
+ ret->h_length = sizeof(ip_addr_t);
+ ret->h_addr_list = (char **)&h->addr_list;
+
+ /* set result != NULL */
+ *result = ret;
+
+ /* return success */
+ return 0;
+}
+
+/**
+ * Frees one or more addrinfo structures returned by getaddrinfo(), along with
+ * any additional storage associated with those structures. If the ai_next field
+ * of the structure is not null, the entire list of structures is freed.
+ *
+ * @param ai struct addrinfo to free
+ */
+void
+lwip_freeaddrinfo(struct addrinfo *ai)
+{
+ struct addrinfo *next;
+
+ while (ai != NULL) {
+ next = ai->ai_next;
+ memp_free(MEMP_NETDB, ai);
+ ai = next;
+ }
+}
+
+/**
+ * Translates the name of a service location (for example, a host name) and/or
+ * a service name and returns a set of socket addresses and associated
+ * information to be used in creating a socket with which to address the
+ * specified service.
+ * Memory for the result is allocated internally and must be freed by calling
+ * lwip_freeaddrinfo()!
+ *
+ * Due to a limitation in dns_gethostbyname, only the first address of a
+ * host is returned.
+ * Also, service names are not supported (only port numbers)!
+ *
+ * @param nodename descriptive name or address string of the host
+ * (may be NULL -> local address)
+ * @param servname port number as string of NULL
+ * @param hints structure containing input values that set socktype and protocol
+ * @param res pointer to a pointer where to store the result (set to NULL on failure)
+ * @return 0 on success, non-zero on failure
+ *
+ * @todo: implement AI_V4MAPPED, AI_ADDRCONFIG
+ */
+int
+lwip_getaddrinfo(const char *nodename, const char *servname,
+ const struct addrinfo *hints, struct addrinfo **res)
+{
+ err_t err;
+ ip_addr_t addr;
+ struct addrinfo *ai;
+ struct sockaddr_storage *sa = NULL;
+ int port_nr = 0;
+ size_t total_size;
+ size_t namelen = 0;
+ int ai_family;
+
+ if (res == NULL) {
+ return EAI_FAIL;
+ }
+ *res = NULL;
+ if ((nodename == NULL) && (servname == NULL)) {
+ return EAI_NONAME;
+ }
+
+ if (hints != NULL) {
+ ai_family = hints->ai_family;
+ if ((ai_family != AF_UNSPEC)
+#if LWIP_IPV4
+ && (ai_family != AF_INET)
+#endif /* LWIP_IPV4 */
+#if LWIP_IPV6
+ && (ai_family != AF_INET6)
+#endif /* LWIP_IPV6 */
+ ) {
+ return EAI_FAMILY;
+ }
+ } else {
+ ai_family = AF_UNSPEC;
+ }
+
+ if (servname != NULL) {
+ /* service name specified: convert to port number
+ * @todo?: currently, only ASCII integers (port numbers) are supported (AI_NUMERICSERV)! */
+ port_nr = atoi(servname);
+ if ((port_nr <= 0) || (port_nr > 0xffff)) {
+ return EAI_SERVICE;
+ }
+ }
+
+ if (nodename != NULL) {
+ /* service location specified, try to resolve */
+ if ((hints != NULL) && (hints->ai_flags & AI_NUMERICHOST)) {
+ /* no DNS lookup, just parse for an address string */
+ if (!ipaddr_aton(nodename, &addr)) {
+ return EAI_NONAME;
+ }
+#if LWIP_IPV4 && LWIP_IPV6
+ if ((IP_IS_V6_VAL(addr) && ai_family == AF_INET) ||
+ (IP_IS_V4_VAL(addr) && ai_family == AF_INET6)) {
+ return EAI_NONAME;
+ }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+ } else {
+#if LWIP_IPV4 && LWIP_IPV6
+ /* AF_UNSPEC: prefer IPv4 */
+ u8_t type = NETCONN_DNS_IPV4_IPV6;
+ if (ai_family == AF_INET) {
+ type = NETCONN_DNS_IPV4;
+ } else if (ai_family == AF_INET6) {
+ type = NETCONN_DNS_IPV6;
+ }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+ err = netconn_gethostbyname_addrtype(nodename, &addr, type);
+ if (err != ERR_OK) {
+ return EAI_FAIL;
+ }
+ }
+ } else {
+ /* service location specified, use loopback address */
+ if ((hints != NULL) && (hints->ai_flags & AI_PASSIVE)) {
+ ip_addr_set_any_val(ai_family == AF_INET6, addr);
+ } else {
+ ip_addr_set_loopback_val(ai_family == AF_INET6, addr);
+ }
+ }
+
+ total_size = sizeof(struct addrinfo) + sizeof(struct sockaddr_storage);
+ if (nodename != NULL) {
+ namelen = strlen(nodename);
+ if (namelen > DNS_MAX_NAME_LENGTH) {
+ /* invalid name length */
+ return EAI_FAIL;
+ }
+ LWIP_ASSERT("namelen is too long", total_size + namelen + 1 > total_size);
+ total_size += namelen + 1;
+ }
+ /* If this fails, please report to lwip-devel! :-) */
+ LWIP_ASSERT("total_size <= NETDB_ELEM_SIZE: please report this!",
+ total_size <= NETDB_ELEM_SIZE);
+ ai = (struct addrinfo *)memp_malloc(MEMP_NETDB);
+ if (ai == NULL) {
+ return EAI_MEMORY;
+ }
+ memset(ai, 0, total_size);
+ /* cast through void* to get rid of alignment warnings */
+ sa = (struct sockaddr_storage *)(void *)((u8_t *)ai + sizeof(struct addrinfo));
+ if (IP_IS_V6_VAL(addr)) {
+#if LWIP_IPV6
+ struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa;
+ /* set up sockaddr */
+ inet6_addr_from_ip6addr(&sa6->sin6_addr, ip_2_ip6(&addr));
+ sa6->sin6_family = AF_INET6;
+ sa6->sin6_len = sizeof(struct sockaddr_in6);
+ sa6->sin6_port = lwip_htons((u16_t)port_nr);
+ sa6->sin6_scope_id = ip6_addr_zone(ip_2_ip6(&addr));
+ ai->ai_family = AF_INET6;
+#endif /* LWIP_IPV6 */
+ } else {
+#if LWIP_IPV4
+ struct sockaddr_in *sa4 = (struct sockaddr_in *)sa;
+ /* set up sockaddr */
+ inet_addr_from_ip4addr(&sa4->sin_addr, ip_2_ip4(&addr));
+ sa4->sin_family = AF_INET;
+ sa4->sin_len = sizeof(struct sockaddr_in);
+ sa4->sin_port = lwip_htons((u16_t)port_nr);
+ ai->ai_family = AF_INET;
+#endif /* LWIP_IPV4 */
+ }
+
+ /* set up addrinfo */
+ if (hints != NULL) {
+ /* copy socktype & protocol from hints if specified */
+ ai->ai_socktype = hints->ai_socktype;
+ ai->ai_protocol = hints->ai_protocol;
+ }
+ if (nodename != NULL) {
+ /* copy nodename to canonname if specified */
+ ai->ai_canonname = ((char *)ai + sizeof(struct addrinfo) + sizeof(struct sockaddr_storage));
+ MEMCPY(ai->ai_canonname, nodename, namelen);
+ ai->ai_canonname[namelen] = 0;
+ }
+ ai->ai_addrlen = sizeof(struct sockaddr_storage);
+ ai->ai_addr = (struct sockaddr *)sa;
+
+ *res = ai;
+
+ return 0;
+}
+
+#endif /* LWIP_DNS && LWIP_SOCKET */
diff --git a/resources/ethernet/LwIP/api/netifapi.c b/resources/ethernet/LwIP/api/netifapi.c
new file mode 100644
index 00000000..7c2011f7
--- /dev/null
+++ b/resources/ethernet/LwIP/api/netifapi.c
@@ -0,0 +1,380 @@
+/**
+ * @file
+ * Network Interface Sequential API module
+ *
+ * @defgroup netifapi NETIF API
+ * @ingroup sequential_api
+ * Thread-safe functions to be called from non-TCPIP threads
+ *
+ * @defgroup netifapi_netif NETIF related
+ * @ingroup netifapi
+ * To be called from non-TCPIP threads
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_NETIF_API /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/etharp.h"
+#include "lwip/netifapi.h"
+#include "lwip/memp.h"
+#include "lwip/priv/tcpip_priv.h"
+
+#include /* strncpy */
+
+#define NETIFAPI_VAR_REF(name) API_VAR_REF(name)
+#define NETIFAPI_VAR_DECLARE(name) API_VAR_DECLARE(struct netifapi_msg, name)
+#define NETIFAPI_VAR_ALLOC(name) API_VAR_ALLOC(struct netifapi_msg, MEMP_NETIFAPI_MSG, name, ERR_MEM)
+#define NETIFAPI_VAR_FREE(name) API_VAR_FREE(MEMP_NETIFAPI_MSG, name)
+
+/**
+ * Call netif_add() inside the tcpip_thread context.
+ */
+static err_t
+netifapi_do_netif_add(struct tcpip_api_call_data *m)
+{
+ /* cast through void* to silence alignment warnings.
+ * We know it works because the structs have been instantiated as struct netifapi_msg */
+ struct netifapi_msg *msg = (struct netifapi_msg *)(void *)m;
+
+ if (!netif_add( msg->netif,
+#if LWIP_IPV4
+ API_EXPR_REF(msg->msg.add.ipaddr),
+ API_EXPR_REF(msg->msg.add.netmask),
+ API_EXPR_REF(msg->msg.add.gw),
+#endif /* LWIP_IPV4 */
+ msg->msg.add.state,
+ msg->msg.add.init,
+ msg->msg.add.input)) {
+ return ERR_IF;
+ } else {
+ return ERR_OK;
+ }
+}
+
+#if LWIP_IPV4
+/**
+ * Call netif_set_addr() inside the tcpip_thread context.
+ */
+static err_t
+netifapi_do_netif_set_addr(struct tcpip_api_call_data *m)
+{
+ /* cast through void* to silence alignment warnings.
+ * We know it works because the structs have been instantiated as struct netifapi_msg */
+ struct netifapi_msg *msg = (struct netifapi_msg *)(void *)m;
+
+ netif_set_addr( msg->netif,
+ API_EXPR_REF(msg->msg.add.ipaddr),
+ API_EXPR_REF(msg->msg.add.netmask),
+ API_EXPR_REF(msg->msg.add.gw));
+ return ERR_OK;
+}
+#endif /* LWIP_IPV4 */
+
+/**
+* Call netif_name_to_index() inside the tcpip_thread context.
+*/
+static err_t
+netifapi_do_name_to_index(struct tcpip_api_call_data *m)
+{
+ /* cast through void* to silence alignment warnings.
+ * We know it works because the structs have been instantiated as struct netifapi_msg */
+ struct netifapi_msg *msg = (struct netifapi_msg *)(void *)m;
+
+ msg->msg.ifs.index = netif_name_to_index(msg->msg.ifs.name);
+ return ERR_OK;
+}
+
+/**
+* Call netif_index_to_name() inside the tcpip_thread context.
+*/
+static err_t
+netifapi_do_index_to_name(struct tcpip_api_call_data *m)
+{
+ /* cast through void* to silence alignment warnings.
+ * We know it works because the structs have been instantiated as struct netifapi_msg */
+ struct netifapi_msg *msg = (struct netifapi_msg *)(void *)m;
+
+ if (!netif_index_to_name(msg->msg.ifs.index, msg->msg.ifs.name)) {
+ /* return failure via empty name */
+ msg->msg.ifs.name[0] = '\0';
+ }
+ return ERR_OK;
+}
+
+/**
+ * Call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) inside the
+ * tcpip_thread context.
+ */
+static err_t
+netifapi_do_netif_common(struct tcpip_api_call_data *m)
+{
+ /* cast through void* to silence alignment warnings.
+ * We know it works because the structs have been instantiated as struct netifapi_msg */
+ struct netifapi_msg *msg = (struct netifapi_msg *)(void *)m;
+
+ if (msg->msg.common.errtfunc != NULL) {
+ return msg->msg.common.errtfunc(msg->netif);
+ } else {
+ msg->msg.common.voidfunc(msg->netif);
+ return ERR_OK;
+ }
+}
+
+#if LWIP_ARP && LWIP_IPV4
+/**
+ * @ingroup netifapi_arp
+ * Add or update an entry in the ARP cache.
+ * For an update, ipaddr is used to find the cache entry.
+ *
+ * @param ipaddr IPv4 address of cache entry
+ * @param ethaddr hardware address mapped to ipaddr
+ * @param type type of ARP cache entry
+ * @return ERR_OK: entry added/updated, else error from err_t
+ */
+err_t
+netifapi_arp_add(const ip4_addr_t *ipaddr, struct eth_addr *ethaddr, enum netifapi_arp_entry type)
+{
+ err_t err;
+
+ /* We only support permanent entries currently */
+ LWIP_UNUSED_ARG(type);
+
+#if ETHARP_SUPPORT_STATIC_ENTRIES && LWIP_TCPIP_CORE_LOCKING
+ LOCK_TCPIP_CORE();
+ err = etharp_add_static_entry(ipaddr, ethaddr);
+ UNLOCK_TCPIP_CORE();
+#else
+ /* @todo add new vars to struct netifapi_msg and create a 'do' func */
+ LWIP_UNUSED_ARG(ipaddr);
+ LWIP_UNUSED_ARG(ethaddr);
+ err = ERR_VAL;
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES && LWIP_TCPIP_CORE_LOCKING */
+
+ return err;
+}
+
+/**
+ * @ingroup netifapi_arp
+ * Remove an entry in the ARP cache identified by ipaddr
+ *
+ * @param ipaddr IPv4 address of cache entry
+ * @param type type of ARP cache entry
+ * @return ERR_OK: entry removed, else error from err_t
+ */
+err_t
+netifapi_arp_remove(const ip4_addr_t *ipaddr, enum netifapi_arp_entry type)
+{
+ err_t err;
+
+ /* We only support permanent entries currently */
+ LWIP_UNUSED_ARG(type);
+
+#if ETHARP_SUPPORT_STATIC_ENTRIES && LWIP_TCPIP_CORE_LOCKING
+ LOCK_TCPIP_CORE();
+ err = etharp_remove_static_entry(ipaddr);
+ UNLOCK_TCPIP_CORE();
+#else
+ /* @todo add new vars to struct netifapi_msg and create a 'do' func */
+ LWIP_UNUSED_ARG(ipaddr);
+ err = ERR_VAL;
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES && LWIP_TCPIP_CORE_LOCKING */
+
+ return err;
+}
+#endif /* LWIP_ARP && LWIP_IPV4 */
+
+/**
+ * @ingroup netifapi_netif
+ * Call netif_add() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ *
+ * @note for params @see netif_add()
+ */
+err_t
+netifapi_netif_add(struct netif *netif,
+#if LWIP_IPV4
+ const ip4_addr_t *ipaddr, const ip4_addr_t *netmask, const ip4_addr_t *gw,
+#endif /* LWIP_IPV4 */
+ void *state, netif_init_fn init, netif_input_fn input)
+{
+ err_t err;
+ NETIFAPI_VAR_DECLARE(msg);
+ NETIFAPI_VAR_ALLOC(msg);
+
+#if LWIP_IPV4
+ if (ipaddr == NULL) {
+ ipaddr = IP4_ADDR_ANY4;
+ }
+ if (netmask == NULL) {
+ netmask = IP4_ADDR_ANY4;
+ }
+ if (gw == NULL) {
+ gw = IP4_ADDR_ANY4;
+ }
+#endif /* LWIP_IPV4 */
+
+ NETIFAPI_VAR_REF(msg).netif = netif;
+#if LWIP_IPV4
+ NETIFAPI_VAR_REF(msg).msg.add.ipaddr = NETIFAPI_VAR_REF(ipaddr);
+ NETIFAPI_VAR_REF(msg).msg.add.netmask = NETIFAPI_VAR_REF(netmask);
+ NETIFAPI_VAR_REF(msg).msg.add.gw = NETIFAPI_VAR_REF(gw);
+#endif /* LWIP_IPV4 */
+ NETIFAPI_VAR_REF(msg).msg.add.state = state;
+ NETIFAPI_VAR_REF(msg).msg.add.init = init;
+ NETIFAPI_VAR_REF(msg).msg.add.input = input;
+ err = tcpip_api_call(netifapi_do_netif_add, &API_VAR_REF(msg).call);
+ NETIFAPI_VAR_FREE(msg);
+ return err;
+}
+
+#if LWIP_IPV4
+/**
+ * @ingroup netifapi_netif
+ * Call netif_set_addr() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ *
+ * @note for params @see netif_set_addr()
+ */
+err_t
+netifapi_netif_set_addr(struct netif *netif,
+ const ip4_addr_t *ipaddr,
+ const ip4_addr_t *netmask,
+ const ip4_addr_t *gw)
+{
+ err_t err;
+ NETIFAPI_VAR_DECLARE(msg);
+ NETIFAPI_VAR_ALLOC(msg);
+
+ if (ipaddr == NULL) {
+ ipaddr = IP4_ADDR_ANY4;
+ }
+ if (netmask == NULL) {
+ netmask = IP4_ADDR_ANY4;
+ }
+ if (gw == NULL) {
+ gw = IP4_ADDR_ANY4;
+ }
+
+ NETIFAPI_VAR_REF(msg).netif = netif;
+ NETIFAPI_VAR_REF(msg).msg.add.ipaddr = NETIFAPI_VAR_REF(ipaddr);
+ NETIFAPI_VAR_REF(msg).msg.add.netmask = NETIFAPI_VAR_REF(netmask);
+ NETIFAPI_VAR_REF(msg).msg.add.gw = NETIFAPI_VAR_REF(gw);
+ err = tcpip_api_call(netifapi_do_netif_set_addr, &API_VAR_REF(msg).call);
+ NETIFAPI_VAR_FREE(msg);
+ return err;
+}
+#endif /* LWIP_IPV4 */
+
+/**
+ * call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) in a thread-safe
+ * way by running that function inside the tcpip_thread context.
+ *
+ * @note use only for functions where there is only "netif" parameter.
+ */
+err_t
+netifapi_netif_common(struct netif *netif, netifapi_void_fn voidfunc,
+ netifapi_errt_fn errtfunc)
+{
+ err_t err;
+ NETIFAPI_VAR_DECLARE(msg);
+ NETIFAPI_VAR_ALLOC(msg);
+
+ NETIFAPI_VAR_REF(msg).netif = netif;
+ NETIFAPI_VAR_REF(msg).msg.common.voidfunc = voidfunc;
+ NETIFAPI_VAR_REF(msg).msg.common.errtfunc = errtfunc;
+ err = tcpip_api_call(netifapi_do_netif_common, &API_VAR_REF(msg).call);
+ NETIFAPI_VAR_FREE(msg);
+ return err;
+}
+
+/**
+* @ingroup netifapi_netif
+* Call netif_name_to_index() in a thread-safe way by running that function inside the
+* tcpip_thread context.
+*
+* @param name the interface name of the netif
+* @param idx output index of the found netif
+*/
+err_t
+netifapi_netif_name_to_index(const char *name, u8_t *idx)
+{
+ err_t err;
+ NETIFAPI_VAR_DECLARE(msg);
+ NETIFAPI_VAR_ALLOC(msg);
+
+ *idx = 0;
+
+#if LWIP_MPU_COMPATIBLE
+ strncpy(NETIFAPI_VAR_REF(msg).msg.ifs.name, name, NETIF_NAMESIZE - 1);
+ NETIFAPI_VAR_REF(msg).msg.ifs.name[NETIF_NAMESIZE - 1] = '\0';
+#else
+ NETIFAPI_VAR_REF(msg).msg.ifs.name = LWIP_CONST_CAST(char *, name);
+#endif /* LWIP_MPU_COMPATIBLE */
+ err = tcpip_api_call(netifapi_do_name_to_index, &API_VAR_REF(msg).call);
+ if (!err) {
+ *idx = NETIFAPI_VAR_REF(msg).msg.ifs.index;
+ }
+ NETIFAPI_VAR_FREE(msg);
+ return err;
+}
+
+/**
+* @ingroup netifapi_netif
+* Call netif_index_to_name() in a thread-safe way by running that function inside the
+* tcpip_thread context.
+*
+* @param idx the interface index of the netif
+* @param name output name of the found netif, empty '\0' string if netif not found.
+* name should be of at least NETIF_NAMESIZE bytes
+*/
+err_t
+netifapi_netif_index_to_name(u8_t idx, char *name)
+{
+ err_t err;
+ NETIFAPI_VAR_DECLARE(msg);
+ NETIFAPI_VAR_ALLOC(msg);
+
+ NETIFAPI_VAR_REF(msg).msg.ifs.index = idx;
+#if !LWIP_MPU_COMPATIBLE
+ NETIFAPI_VAR_REF(msg).msg.ifs.name = name;
+#endif /* LWIP_MPU_COMPATIBLE */
+ err = tcpip_api_call(netifapi_do_index_to_name, &API_VAR_REF(msg).call);
+#if LWIP_MPU_COMPATIBLE
+ if (!err) {
+ strncpy(name, NETIFAPI_VAR_REF(msg).msg.ifs.name, NETIF_NAMESIZE - 1);
+ name[NETIF_NAMESIZE - 1] = '\0';
+ }
+#endif /* LWIP_MPU_COMPATIBLE */
+ NETIFAPI_VAR_FREE(msg);
+ return err;
+}
+
+#endif /* LWIP_NETIF_API */
diff --git a/resources/ethernet/LwIP/api/sockets.c b/resources/ethernet/LwIP/api/sockets.c
new file mode 100644
index 00000000..21dbcbfb
--- /dev/null
+++ b/resources/ethernet/LwIP/api/sockets.c
@@ -0,0 +1,4168 @@
+/**
+ * @file
+ * Sockets BSD-Like API module
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ * Improved by Marc Boucher and David Haas
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/sockets.h"
+#include "lwip/priv/sockets_priv.h"
+#include "lwip/api.h"
+#include "lwip/igmp.h"
+#include "lwip/inet.h"
+#include "lwip/tcp.h"
+#include "lwip/raw.h"
+#include "lwip/udp.h"
+#include "lwip/memp.h"
+#include "lwip/pbuf.h"
+#include "lwip/netif.h"
+#include "lwip/priv/tcpip_priv.h"
+#include "lwip/mld6.h"
+#if LWIP_CHECKSUM_ON_COPY
+#include "lwip/inet_chksum.h"
+#endif
+
+#if LWIP_COMPAT_SOCKETS == 2 && LWIP_POSIX_SOCKETS_IO_NAMES
+#include
+#endif
+
+#include
+
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+
+/* If the netconn API is not required publicly, then we include the necessary
+ files here to get the implementation */
+#if !LWIP_NETCONN
+#undef LWIP_NETCONN
+#define LWIP_NETCONN 1
+#include "api_msg.c"
+#include "api_lib.c"
+#include "netbuf.c"
+#undef LWIP_NETCONN
+#define LWIP_NETCONN 0
+#endif
+
+#define API_SELECT_CB_VAR_REF(name) API_VAR_REF(name)
+#define API_SELECT_CB_VAR_DECLARE(name) API_VAR_DECLARE(struct lwip_select_cb, name)
+#define API_SELECT_CB_VAR_ALLOC(name, retblock) API_VAR_ALLOC_EXT(struct lwip_select_cb, MEMP_SELECT_CB, name, retblock)
+#define API_SELECT_CB_VAR_FREE(name) API_VAR_FREE(MEMP_SELECT_CB, name)
+
+#if LWIP_IPV4
+#define IP4ADDR_PORT_TO_SOCKADDR(sin, ipaddr, port) do { \
+ (sin)->sin_len = sizeof(struct sockaddr_in); \
+ (sin)->sin_family = AF_INET; \
+ (sin)->sin_port = lwip_htons((port)); \
+ inet_addr_from_ip4addr(&(sin)->sin_addr, ipaddr); \
+ memset((sin)->sin_zero, 0, SIN_ZERO_LEN); }while(0)
+#define SOCKADDR4_TO_IP4ADDR_PORT(sin, ipaddr, port) do { \
+ inet_addr_to_ip4addr(ip_2_ip4(ipaddr), &((sin)->sin_addr)); \
+ (port) = lwip_ntohs((sin)->sin_port); }while(0)
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV6
+#define IP6ADDR_PORT_TO_SOCKADDR(sin6, ipaddr, port) do { \
+ (sin6)->sin6_len = sizeof(struct sockaddr_in6); \
+ (sin6)->sin6_family = AF_INET6; \
+ (sin6)->sin6_port = lwip_htons((port)); \
+ (sin6)->sin6_flowinfo = 0; \
+ inet6_addr_from_ip6addr(&(sin6)->sin6_addr, ipaddr); \
+ (sin6)->sin6_scope_id = ip6_addr_zone(ipaddr); }while(0)
+#define SOCKADDR6_TO_IP6ADDR_PORT(sin6, ipaddr, port) do { \
+ inet6_addr_to_ip6addr(ip_2_ip6(ipaddr), &((sin6)->sin6_addr)); \
+ if (ip6_addr_has_scope(ip_2_ip6(ipaddr), IP6_UNKNOWN)) { \
+ ip6_addr_set_zone(ip_2_ip6(ipaddr), (u8_t)((sin6)->sin6_scope_id)); \
+ } \
+ (port) = lwip_ntohs((sin6)->sin6_port); }while(0)
+#endif /* LWIP_IPV6 */
+
+#if LWIP_IPV4 && LWIP_IPV6
+static void sockaddr_to_ipaddr_port(const struct sockaddr *sockaddr, ip_addr_t *ipaddr, u16_t *port);
+
+#define IS_SOCK_ADDR_LEN_VALID(namelen) (((namelen) == sizeof(struct sockaddr_in)) || \
+ ((namelen) == sizeof(struct sockaddr_in6)))
+#define IS_SOCK_ADDR_TYPE_VALID(name) (((name)->sa_family == AF_INET) || \
+ ((name)->sa_family == AF_INET6))
+#define SOCK_ADDR_TYPE_MATCH(name, sock) \
+ ((((name)->sa_family == AF_INET) && !(NETCONNTYPE_ISIPV6((sock)->conn->type))) || \
+ (((name)->sa_family == AF_INET6) && (NETCONNTYPE_ISIPV6((sock)->conn->type))))
+#define IPADDR_PORT_TO_SOCKADDR(sockaddr, ipaddr, port) do { \
+ if (IP_IS_ANY_TYPE_VAL(*ipaddr) || IP_IS_V6_VAL(*ipaddr)) { \
+ IP6ADDR_PORT_TO_SOCKADDR((struct sockaddr_in6*)(void*)(sockaddr), ip_2_ip6(ipaddr), port); \
+ } else { \
+ IP4ADDR_PORT_TO_SOCKADDR((struct sockaddr_in*)(void*)(sockaddr), ip_2_ip4(ipaddr), port); \
+ } } while(0)
+#define SOCKADDR_TO_IPADDR_PORT(sockaddr, ipaddr, port) sockaddr_to_ipaddr_port(sockaddr, ipaddr, &(port))
+#define DOMAIN_TO_NETCONN_TYPE(domain, type) (((domain) == AF_INET) ? \
+ (type) : (enum netconn_type)((type) | NETCONN_TYPE_IPV6))
+#elif LWIP_IPV6 /* LWIP_IPV4 && LWIP_IPV6 */
+#define IS_SOCK_ADDR_LEN_VALID(namelen) ((namelen) == sizeof(struct sockaddr_in6))
+#define IS_SOCK_ADDR_TYPE_VALID(name) ((name)->sa_family == AF_INET6)
+#define SOCK_ADDR_TYPE_MATCH(name, sock) 1
+#define IPADDR_PORT_TO_SOCKADDR(sockaddr, ipaddr, port) \
+ IP6ADDR_PORT_TO_SOCKADDR((struct sockaddr_in6*)(void*)(sockaddr), ip_2_ip6(ipaddr), port)
+#define SOCKADDR_TO_IPADDR_PORT(sockaddr, ipaddr, port) \
+ SOCKADDR6_TO_IP6ADDR_PORT((const struct sockaddr_in6*)(const void*)(sockaddr), ipaddr, port)
+#define DOMAIN_TO_NETCONN_TYPE(domain, netconn_type) (netconn_type)
+#else /*-> LWIP_IPV4: LWIP_IPV4 && LWIP_IPV6 */
+#define IS_SOCK_ADDR_LEN_VALID(namelen) ((namelen) == sizeof(struct sockaddr_in))
+#define IS_SOCK_ADDR_TYPE_VALID(name) ((name)->sa_family == AF_INET)
+#define SOCK_ADDR_TYPE_MATCH(name, sock) 1
+#define IPADDR_PORT_TO_SOCKADDR(sockaddr, ipaddr, port) \
+ IP4ADDR_PORT_TO_SOCKADDR((struct sockaddr_in*)(void*)(sockaddr), ip_2_ip4(ipaddr), port)
+#define SOCKADDR_TO_IPADDR_PORT(sockaddr, ipaddr, port) \
+ SOCKADDR4_TO_IP4ADDR_PORT((const struct sockaddr_in*)(const void*)(sockaddr), ipaddr, port)
+#define DOMAIN_TO_NETCONN_TYPE(domain, netconn_type) (netconn_type)
+#endif /* LWIP_IPV6 */
+
+#define IS_SOCK_ADDR_TYPE_VALID_OR_UNSPEC(name) (((name)->sa_family == AF_UNSPEC) || \
+ IS_SOCK_ADDR_TYPE_VALID(name))
+#define SOCK_ADDR_TYPE_MATCH_OR_UNSPEC(name, sock) (((name)->sa_family == AF_UNSPEC) || \
+ SOCK_ADDR_TYPE_MATCH(name, sock))
+#define IS_SOCK_ADDR_ALIGNED(name) ((((mem_ptr_t)(name)) % 4) == 0)
+
+
+#define LWIP_SOCKOPT_CHECK_OPTLEN(sock, optlen, opttype) do { if ((optlen) < sizeof(opttype)) { done_socket(sock); return EINVAL; }}while(0)
+#define LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, opttype) do { \
+ LWIP_SOCKOPT_CHECK_OPTLEN(sock, optlen, opttype); \
+ if ((sock)->conn == NULL) { done_socket(sock); return EINVAL; } }while(0)
+#define LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, opttype) do { \
+ LWIP_SOCKOPT_CHECK_OPTLEN(sock, optlen, opttype); \
+ if (((sock)->conn == NULL) || ((sock)->conn->pcb.tcp == NULL)) { done_socket(sock); return EINVAL; } }while(0)
+#define LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, opttype, netconntype) do { \
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, opttype); \
+ if (NETCONNTYPE_GROUP(netconn_type((sock)->conn)) != netconntype) { done_socket(sock); return ENOPROTOOPT; } }while(0)
+
+
+#define LWIP_SETGETSOCKOPT_DATA_VAR_REF(name) API_VAR_REF(name)
+#define LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(name) API_VAR_DECLARE(struct lwip_setgetsockopt_data, name)
+#define LWIP_SETGETSOCKOPT_DATA_VAR_FREE(name) API_VAR_FREE(MEMP_SOCKET_SETGETSOCKOPT_DATA, name)
+#if LWIP_MPU_COMPATIBLE
+#define LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(name, sock) do { \
+ name = (struct lwip_setgetsockopt_data *)memp_malloc(MEMP_SOCKET_SETGETSOCKOPT_DATA); \
+ if (name == NULL) { \
+ sock_set_errno(sock, ENOMEM); \
+ done_socket(sock); \
+ return -1; \
+ } }while(0)
+#else /* LWIP_MPU_COMPATIBLE */
+#define LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(name, sock)
+#endif /* LWIP_MPU_COMPATIBLE */
+
+#if LWIP_SO_SNDRCVTIMEO_NONSTANDARD
+#define LWIP_SO_SNDRCVTIMEO_OPTTYPE int
+#define LWIP_SO_SNDRCVTIMEO_SET(optval, val) (*(int *)(optval) = (val))
+#define LWIP_SO_SNDRCVTIMEO_GET_MS(optval) ((long)*(const int*)(optval))
+#else
+#define LWIP_SO_SNDRCVTIMEO_OPTTYPE struct timeval
+#define LWIP_SO_SNDRCVTIMEO_SET(optval, val) do { \
+ u32_t loc = (val); \
+ ((struct timeval *)(optval))->tv_sec = (long)((loc) / 1000U); \
+ ((struct timeval *)(optval))->tv_usec = (long)(((loc) % 1000U) * 1000U); }while(0)
+#define LWIP_SO_SNDRCVTIMEO_GET_MS(optval) ((((const struct timeval *)(optval))->tv_sec * 1000) + (((const struct timeval *)(optval))->tv_usec / 1000))
+#endif
+
+
+/** A struct sockaddr replacement that has the same alignment as sockaddr_in/
+ * sockaddr_in6 if instantiated.
+ */
+union sockaddr_aligned {
+ struct sockaddr sa;
+#if LWIP_IPV6
+ struct sockaddr_in6 sin6;
+#endif /* LWIP_IPV6 */
+#if LWIP_IPV4
+ struct sockaddr_in sin;
+#endif /* LWIP_IPV4 */
+};
+
+/* Define the number of IPv4 multicast memberships, default is one per socket */
+#ifndef LWIP_SOCKET_MAX_MEMBERSHIPS
+#define LWIP_SOCKET_MAX_MEMBERSHIPS NUM_SOCKETS
+#endif
+
+#if LWIP_IGMP
+/* This is to keep track of IP_ADD_MEMBERSHIP calls to drop the membership when
+ a socket is closed */
+struct lwip_socket_multicast_pair {
+ /** the socket */
+ struct lwip_sock *sock;
+ /** the interface address */
+ ip4_addr_t if_addr;
+ /** the group address */
+ ip4_addr_t multi_addr;
+};
+
+static struct lwip_socket_multicast_pair socket_ipv4_multicast_memberships[LWIP_SOCKET_MAX_MEMBERSHIPS];
+
+static int lwip_socket_register_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr);
+static void lwip_socket_unregister_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr);
+static void lwip_socket_drop_registered_memberships(int s);
+#endif /* LWIP_IGMP */
+
+#if LWIP_IPV6_MLD
+/* This is to keep track of IP_JOIN_GROUP calls to drop the membership when
+ a socket is closed */
+struct lwip_socket_multicast_mld6_pair {
+ /** the socket */
+ struct lwip_sock *sock;
+ /** the interface index */
+ u8_t if_idx;
+ /** the group address */
+ ip6_addr_t multi_addr;
+};
+
+static struct lwip_socket_multicast_mld6_pair socket_ipv6_multicast_memberships[LWIP_SOCKET_MAX_MEMBERSHIPS];
+
+static int lwip_socket_register_mld6_membership(int s, unsigned int if_idx, const ip6_addr_t *multi_addr);
+static void lwip_socket_unregister_mld6_membership(int s, unsigned int if_idx, const ip6_addr_t *multi_addr);
+static void lwip_socket_drop_registered_mld6_memberships(int s);
+#endif /* LWIP_IPV6_MLD */
+
+/** The global array of available sockets */
+static struct lwip_sock sockets[NUM_SOCKETS];
+
+#if LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL
+#if LWIP_TCPIP_CORE_LOCKING
+/* protect the select_cb_list using core lock */
+#define LWIP_SOCKET_SELECT_DECL_PROTECT(lev)
+#define LWIP_SOCKET_SELECT_PROTECT(lev) LOCK_TCPIP_CORE()
+#define LWIP_SOCKET_SELECT_UNPROTECT(lev) UNLOCK_TCPIP_CORE()
+#else /* LWIP_TCPIP_CORE_LOCKING */
+/* protect the select_cb_list using SYS_LIGHTWEIGHT_PROT */
+#define LWIP_SOCKET_SELECT_DECL_PROTECT(lev) SYS_ARCH_DECL_PROTECT(lev)
+#define LWIP_SOCKET_SELECT_PROTECT(lev) SYS_ARCH_PROTECT(lev)
+#define LWIP_SOCKET_SELECT_UNPROTECT(lev) SYS_ARCH_UNPROTECT(lev)
+/** This counter is increased from lwip_select when the list is changed
+ and checked in select_check_waiters to see if it has changed. */
+static volatile int select_cb_ctr;
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+/** The global list of tasks waiting for select */
+static struct lwip_select_cb *select_cb_list;
+#endif /* LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL */
+
+#define sock_set_errno(sk, e) do { \
+ const int sockerr = (e); \
+ set_errno(sockerr); \
+} while (0)
+
+/* Forward declaration of some functions */
+#if LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL
+static void event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len);
+#define DEFAULT_SOCKET_EVENTCB event_callback
+static void select_check_waiters(int s, int has_recvevent, int has_sendevent, int has_errevent);
+#else
+#define DEFAULT_SOCKET_EVENTCB NULL
+#endif
+#if !LWIP_TCPIP_CORE_LOCKING
+static void lwip_getsockopt_callback(void *arg);
+static void lwip_setsockopt_callback(void *arg);
+#endif
+static int lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *optlen);
+static int lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_t optlen);
+static int free_socket_locked(struct lwip_sock *sock, int is_tcp, struct netconn **conn,
+ union lwip_sock_lastdata *lastdata);
+static void free_socket_free_elements(int is_tcp, struct netconn *conn, union lwip_sock_lastdata *lastdata);
+
+#if LWIP_IPV4 && LWIP_IPV6
+static void
+sockaddr_to_ipaddr_port(const struct sockaddr *sockaddr, ip_addr_t *ipaddr, u16_t *port)
+{
+ if ((sockaddr->sa_family) == AF_INET6) {
+ SOCKADDR6_TO_IP6ADDR_PORT((const struct sockaddr_in6 *)(const void *)(sockaddr), ipaddr, *port);
+ ipaddr->type = IPADDR_TYPE_V6;
+ } else {
+ SOCKADDR4_TO_IP4ADDR_PORT((const struct sockaddr_in *)(const void *)(sockaddr), ipaddr, *port);
+ ipaddr->type = IPADDR_TYPE_V4;
+ }
+}
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+/** LWIP_NETCONN_SEM_PER_THREAD==1: initialize thread-local semaphore */
+void
+lwip_socket_thread_init(void)
+{
+ netconn_thread_init();
+}
+
+/** LWIP_NETCONN_SEM_PER_THREAD==1: destroy thread-local semaphore */
+void
+lwip_socket_thread_cleanup(void)
+{
+ netconn_thread_cleanup();
+}
+
+#if LWIP_NETCONN_FULLDUPLEX
+/* Thread-safe increment of sock->fd_used, with overflow check */
+static int
+sock_inc_used(struct lwip_sock *sock)
+{
+ int ret;
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ LWIP_ASSERT("sock != NULL", sock != NULL);
+
+ SYS_ARCH_PROTECT(lev);
+ if (sock->fd_free_pending) {
+ /* prevent new usage of this socket if free is pending */
+ ret = 0;
+ } else {
+ ++sock->fd_used;
+ ret = 1;
+ LWIP_ASSERT("sock->fd_used != 0", sock->fd_used != 0);
+ }
+ SYS_ARCH_UNPROTECT(lev);
+ return ret;
+}
+
+/* Like sock_inc_used(), but called under SYS_ARCH_PROTECT lock. */
+static int
+sock_inc_used_locked(struct lwip_sock *sock)
+{
+ LWIP_ASSERT("sock != NULL", sock != NULL);
+
+ if (sock->fd_free_pending) {
+ LWIP_ASSERT("sock->fd_used != 0", sock->fd_used != 0);
+ return 0;
+ }
+
+ ++sock->fd_used;
+ LWIP_ASSERT("sock->fd_used != 0", sock->fd_used != 0);
+ return 1;
+}
+
+/* In full-duplex mode,sock->fd_used != 0 prevents a socket descriptor from being
+ * released (and possibly reused) when used from more than one thread
+ * (e.g. read-while-write or close-while-write, etc)
+ * This function is called at the end of functions using (try)get_socket*().
+ */
+static void
+done_socket(struct lwip_sock *sock)
+{
+ int freed = 0;
+ int is_tcp = 0;
+ struct netconn *conn = NULL;
+ union lwip_sock_lastdata lastdata;
+ SYS_ARCH_DECL_PROTECT(lev);
+ LWIP_ASSERT("sock != NULL", sock != NULL);
+
+ SYS_ARCH_PROTECT(lev);
+ LWIP_ASSERT("sock->fd_used > 0", sock->fd_used > 0);
+ if (--sock->fd_used == 0) {
+ if (sock->fd_free_pending) {
+ /* free the socket */
+ sock->fd_used = 1;
+ is_tcp = sock->fd_free_pending & LWIP_SOCK_FD_FREE_TCP;
+ freed = free_socket_locked(sock, is_tcp, &conn, &lastdata);
+ }
+ }
+ SYS_ARCH_UNPROTECT(lev);
+
+ if (freed) {
+ free_socket_free_elements(is_tcp, conn, &lastdata);
+ }
+}
+
+#else /* LWIP_NETCONN_FULLDUPLEX */
+#define sock_inc_used(sock) 1
+#define sock_inc_used_locked(sock) 1
+#define done_socket(sock)
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+
+//tmp
+#define EMSGSIZE 90 /* Message too long */
+#define ENOTCONN 107 /* Transport endpoint is not connected */
+#define ENOPROTOOPT 92 /* Protocol not available */
+#define ENOSYS 38 /* Function not implemented */
+#define EAFNOSUPPORT 97 /* Address family not supported by protocol */
+
+
+/* Translate a socket 'int' into a pointer (only fails if the index is invalid) */
+static struct lwip_sock *
+tryget_socket_unconn_nouse(int fd)
+{
+ int s = fd - LWIP_SOCKET_OFFSET;
+ if ((s < 0) || (s >= NUM_SOCKETS)) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("tryget_socket_unconn(%d): invalid\n", fd));
+ return NULL;
+ }
+ return &sockets[s];
+}
+
+struct lwip_sock *
+lwip_socket_dbg_get_socket(int fd)
+{
+ return tryget_socket_unconn_nouse(fd);
+}
+
+/* Translate a socket 'int' into a pointer (only fails if the index is invalid) */
+static struct lwip_sock *
+tryget_socket_unconn(int fd)
+{
+ struct lwip_sock *ret = tryget_socket_unconn_nouse(fd);
+ if (ret != NULL) {
+ if (!sock_inc_used(ret)) {
+ return NULL;
+ }
+ }
+ return ret;
+}
+
+/* Like tryget_socket_unconn(), but called under SYS_ARCH_PROTECT lock. */
+static struct lwip_sock *
+tryget_socket_unconn_locked(int fd)
+{
+ struct lwip_sock *ret = tryget_socket_unconn_nouse(fd);
+ if (ret != NULL) {
+ if (!sock_inc_used_locked(ret)) {
+ return NULL;
+ }
+ }
+ return ret;
+}
+
+/**
+ * Same as get_socket but doesn't set errno
+ *
+ * @param fd externally used socket index
+ * @return struct lwip_sock for the socket or NULL if not found
+ */
+static struct lwip_sock *
+tryget_socket(int fd)
+{
+ struct lwip_sock *sock = tryget_socket_unconn(fd);
+ if (sock != NULL) {
+ if (sock->conn) {
+ return sock;
+ }
+ done_socket(sock);
+ }
+ return NULL;
+}
+
+/**
+ * Map a externally used socket index to the internal socket representation.
+ *
+ * @param fd externally used socket index
+ * @return struct lwip_sock for the socket or NULL if not found
+ */
+static struct lwip_sock *
+get_socket(int fd)
+{
+ struct lwip_sock *sock = tryget_socket(fd);
+ if (!sock) {
+ if ((fd < LWIP_SOCKET_OFFSET) || (fd >= (LWIP_SOCKET_OFFSET + NUM_SOCKETS))) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): invalid\n", fd));
+ }
+ set_errno(EBADF);
+ return NULL;
+ }
+ return sock;
+}
+
+/**
+ * Allocate a new socket for a given netconn.
+ *
+ * @param newconn the netconn for which to allocate a socket
+ * @param accepted 1 if socket has been created by accept(),
+ * 0 if socket has been created by socket()
+ * @return the index of the new socket; -1 on error
+ */
+static int
+alloc_socket(struct netconn *newconn, int accepted)
+{
+ int i;
+ SYS_ARCH_DECL_PROTECT(lev);
+ LWIP_UNUSED_ARG(accepted);
+
+ /* allocate a new socket identifier */
+ for (i = 0; i < NUM_SOCKETS; ++i) {
+ /* Protect socket array */
+ SYS_ARCH_PROTECT(lev);
+ if (!sockets[i].conn) {
+#if LWIP_NETCONN_FULLDUPLEX
+ if (sockets[i].fd_used) {
+ SYS_ARCH_UNPROTECT(lev);
+ continue;
+ }
+ sockets[i].fd_used = 1;
+ sockets[i].fd_free_pending = 0;
+#endif
+ sockets[i].conn = newconn;
+ /* The socket is not yet known to anyone, so no need to protect
+ after having marked it as used. */
+ SYS_ARCH_UNPROTECT(lev);
+ sockets[i].lastdata.pbuf = NULL;
+#if LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL
+ LWIP_ASSERT("sockets[i].select_waiting == 0", sockets[i].select_waiting == 0);
+ sockets[i].rcvevent = 0;
+ /* TCP sendbuf is empty, but the socket is not yet writable until connected
+ * (unless it has been created by accept()). */
+ sockets[i].sendevent = (NETCONNTYPE_GROUP(newconn->type) == NETCONN_TCP ? (accepted != 0) : 1);
+ sockets[i].errevent = 0;
+#endif /* LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL */
+ return i + LWIP_SOCKET_OFFSET;
+ }
+ SYS_ARCH_UNPROTECT(lev);
+ }
+ return -1;
+}
+
+/** Free a socket (under lock)
+ *
+ * @param sock the socket to free
+ * @param is_tcp != 0 for TCP sockets, used to free lastdata
+ * @param conn the socekt's netconn is stored here, must be freed externally
+ * @param lastdata lastdata is stored here, must be freed externally
+ */
+static int
+free_socket_locked(struct lwip_sock *sock, int is_tcp, struct netconn **conn,
+ union lwip_sock_lastdata *lastdata)
+{
+#if LWIP_NETCONN_FULLDUPLEX
+ LWIP_ASSERT("sock->fd_used > 0", sock->fd_used > 0);
+ sock->fd_used--;
+ if (sock->fd_used > 0) {
+ sock->fd_free_pending = LWIP_SOCK_FD_FREE_FREE | (is_tcp ? LWIP_SOCK_FD_FREE_TCP : 0);
+ return 0;
+ }
+#else /* LWIP_NETCONN_FULLDUPLEX */
+ LWIP_UNUSED_ARG(is_tcp);
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+
+ *lastdata = sock->lastdata;
+ sock->lastdata.pbuf = NULL;
+ *conn = sock->conn;
+ sock->conn = NULL;
+ return 1;
+}
+
+/** Free a socket's leftover members.
+ */
+static void
+free_socket_free_elements(int is_tcp, struct netconn *conn, union lwip_sock_lastdata *lastdata)
+{
+ if (lastdata->pbuf != NULL) {
+ if (is_tcp) {
+ pbuf_free(lastdata->pbuf);
+ } else {
+ netbuf_delete(lastdata->netbuf);
+ }
+ }
+ if (conn != NULL) {
+ /* netconn_prepare_delete() has already been called, here we only free the conn */
+ netconn_delete(conn);
+ }
+}
+
+/** Free a socket. The socket's netconn must have been
+ * delete before!
+ *
+ * @param sock the socket to free
+ * @param is_tcp != 0 for TCP sockets, used to free lastdata
+ */
+static void
+free_socket(struct lwip_sock *sock, int is_tcp)
+{
+ int freed;
+ struct netconn *conn;
+ union lwip_sock_lastdata lastdata;
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ /* Protect socket array */
+ SYS_ARCH_PROTECT(lev);
+
+ freed = free_socket_locked(sock, is_tcp, &conn, &lastdata);
+ SYS_ARCH_UNPROTECT(lev);
+ /* don't use 'sock' after this line, as another task might have allocated it */
+
+ if (freed) {
+ free_socket_free_elements(is_tcp, conn, &lastdata);
+ }
+}
+
+/* Below this, the well-known socket functions are implemented.
+ * Use google.com or opengroup.org to get a good description :-)
+ *
+ * Exceptions are documented!
+ */
+
+int
+lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
+{
+ struct lwip_sock *sock, *nsock;
+ struct netconn *newconn;
+ ip_addr_t naddr;
+ u16_t port = 0;
+ int newsock;
+ err_t err;
+ int recvevent;
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d)...\n", s));
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ /* wait for a new connection */
+ err = netconn_accept(sock->conn, &newconn);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_acept failed, err=%d\n", s, err));
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
+ sock_set_errno(sock, EOPNOTSUPP);
+ } else if (err == ERR_CLSD) {
+ sock_set_errno(sock, EINVAL);
+ } else {
+ sock_set_errno(sock, err_to_errno(err));
+ }
+ done_socket(sock);
+ return -1;
+ }
+ LWIP_ASSERT("newconn != NULL", newconn != NULL);
+
+ newsock = alloc_socket(newconn, 1);
+ if (newsock == -1) {
+ netconn_delete(newconn);
+ sock_set_errno(sock, ENFILE);
+ done_socket(sock);
+ return -1;
+ }
+ LWIP_ASSERT("invalid socket index", (newsock >= LWIP_SOCKET_OFFSET) && (newsock < NUM_SOCKETS + LWIP_SOCKET_OFFSET));
+ nsock = &sockets[newsock - LWIP_SOCKET_OFFSET];
+
+ /* See event_callback: If data comes in right away after an accept, even
+ * though the server task might not have created a new socket yet.
+ * In that case, newconn->socket is counted down (newconn->socket--),
+ * so nsock->rcvevent is >= 1 here!
+ */
+ SYS_ARCH_PROTECT(lev);
+ recvevent = (s16_t)(-1 - newconn->socket);
+ newconn->socket = newsock;
+ SYS_ARCH_UNPROTECT(lev);
+
+ if (newconn->callback) {
+ LOCK_TCPIP_CORE();
+ while (recvevent > 0) {
+ recvevent--;
+ newconn->callback(newconn, NETCONN_EVT_RCVPLUS, 0);
+ }
+ UNLOCK_TCPIP_CORE();
+ }
+
+ /* Note that POSIX only requires us to check addr is non-NULL. addrlen must
+ * not be NULL if addr is valid.
+ */
+ if ((addr != NULL) && (addrlen != NULL)) {
+ union sockaddr_aligned tempaddr;
+ /* get the IP address and port of the remote host */
+ err = netconn_peer(newconn, &naddr, &port);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_peer failed, err=%d\n", s, err));
+ netconn_delete(newconn);
+ free_socket(nsock, 1);
+ sock_set_errno(sock, err_to_errno(err));
+ done_socket(sock);
+ return -1;
+ }
+
+ IPADDR_PORT_TO_SOCKADDR(&tempaddr, &naddr, port);
+ if (*addrlen > tempaddr.sa.sa_len) {
+ *addrlen = tempaddr.sa.sa_len;
+ }
+ MEMCPY(addr, &tempaddr, *addrlen);
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) returning new sock=%d addr=", s, newsock));
+ ip_addr_debug_print_val(SOCKETS_DEBUG, naddr);
+ LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", port));
+ } else {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) returning new sock=%d", s, newsock));
+ }
+
+ sock_set_errno(sock, 0);
+ done_socket(sock);
+ done_socket(nsock);
+ return newsock;
+}
+
+int
+lwip_bind(int s, const struct sockaddr *name, socklen_t namelen)
+{
+ struct lwip_sock *sock;
+ ip_addr_t local_addr;
+ u16_t local_port;
+ err_t err;
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ if (!SOCK_ADDR_TYPE_MATCH(name, sock)) {
+ /* sockaddr does not match socket type (IPv4/IPv6) */
+ sock_set_errno(sock, err_to_errno(ERR_VAL));
+ done_socket(sock);
+ return -1;
+ }
+
+ /* check size, family and alignment of 'name' */
+ LWIP_ERROR("lwip_bind: invalid address", (IS_SOCK_ADDR_LEN_VALID(namelen) &&
+ IS_SOCK_ADDR_TYPE_VALID(name) && IS_SOCK_ADDR_ALIGNED(name)),
+ sock_set_errno(sock, err_to_errno(ERR_ARG)); done_socket(sock); return -1;);
+ LWIP_UNUSED_ARG(namelen);
+
+ SOCKADDR_TO_IPADDR_PORT(name, &local_addr, local_port);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d, addr=", s));
+ ip_addr_debug_print_val(SOCKETS_DEBUG, local_addr);
+ LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", local_port));
+
+#if LWIP_IPV4 && LWIP_IPV6
+ /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */
+ if (IP_IS_V6_VAL(local_addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(&local_addr))) {
+ unmap_ipv4_mapped_ipv6(ip_2_ip4(&local_addr), ip_2_ip6(&local_addr));
+ IP_SET_TYPE_VAL(local_addr, IPADDR_TYPE_V4);
+ }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+ err = netconn_bind(sock->conn, &local_addr, local_port);
+
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) failed, err=%d\n", s, err));
+ sock_set_errno(sock, err_to_errno(err));
+ done_socket(sock);
+ return -1;
+ }
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) succeeded\n", s));
+ sock_set_errno(sock, 0);
+ done_socket(sock);
+ return 0;
+}
+
+int
+lwip_close(int s)
+{
+ struct lwip_sock *sock;
+ int is_tcp = 0;
+ err_t err;
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_close(%d)\n", s));
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ if (sock->conn != NULL) {
+ is_tcp = NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP;
+ } else {
+ LWIP_ASSERT("sock->lastdata == NULL", sock->lastdata.pbuf == NULL);
+ }
+
+#if LWIP_IGMP
+ /* drop all possibly joined IGMP memberships */
+ lwip_socket_drop_registered_memberships(s);
+#endif /* LWIP_IGMP */
+#if LWIP_IPV6_MLD
+ /* drop all possibly joined MLD6 memberships */
+ lwip_socket_drop_registered_mld6_memberships(s);
+#endif /* LWIP_IPV6_MLD */
+
+ err = netconn_prepare_delete(sock->conn);
+ if (err != ERR_OK) {
+ sock_set_errno(sock, err_to_errno(err));
+ done_socket(sock);
+ return -1;
+ }
+
+ free_socket(sock, is_tcp);
+ set_errno(0);
+ return 0;
+}
+
+int
+lwip_connect(int s, const struct sockaddr *name, socklen_t namelen)
+{
+ struct lwip_sock *sock;
+ err_t err;
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ if (!SOCK_ADDR_TYPE_MATCH_OR_UNSPEC(name, sock)) {
+ /* sockaddr does not match socket type (IPv4/IPv6) */
+ sock_set_errno(sock, err_to_errno(ERR_VAL));
+ done_socket(sock);
+ return -1;
+ }
+
+ LWIP_UNUSED_ARG(namelen);
+ if (name->sa_family == AF_UNSPEC) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, AF_UNSPEC)\n", s));
+ err = netconn_disconnect(sock->conn);
+ } else {
+ ip_addr_t remote_addr;
+ u16_t remote_port;
+
+ /* check size, family and alignment of 'name' */
+ LWIP_ERROR("lwip_connect: invalid address", IS_SOCK_ADDR_LEN_VALID(namelen) &&
+ IS_SOCK_ADDR_TYPE_VALID_OR_UNSPEC(name) && IS_SOCK_ADDR_ALIGNED(name),
+ sock_set_errno(sock, err_to_errno(ERR_ARG)); done_socket(sock); return -1;);
+
+ SOCKADDR_TO_IPADDR_PORT(name, &remote_addr, remote_port);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, addr=", s));
+ ip_addr_debug_print_val(SOCKETS_DEBUG, remote_addr);
+ LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", remote_port));
+
+#if LWIP_IPV4 && LWIP_IPV6
+ /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */
+ if (IP_IS_V6_VAL(remote_addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(&remote_addr))) {
+ unmap_ipv4_mapped_ipv6(ip_2_ip4(&remote_addr), ip_2_ip6(&remote_addr));
+ IP_SET_TYPE_VAL(remote_addr, IPADDR_TYPE_V4);
+ }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+ err = netconn_connect(sock->conn, &remote_addr, remote_port);
+ }
+
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) failed, err=%d\n", s, err));
+ sock_set_errno(sock, err_to_errno(err));
+ done_socket(sock);
+ return -1;
+ }
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) succeeded\n", s));
+ sock_set_errno(sock, 0);
+ done_socket(sock);
+ return 0;
+}
+
+/**
+ * Set a socket into listen mode.
+ * The socket may not have been used for another connection previously.
+ *
+ * @param s the socket to set to listening mode
+ * @param backlog (ATTENTION: needs TCP_LISTEN_BACKLOG=1)
+ * @return 0 on success, non-zero on failure
+ */
+int
+lwip_listen(int s, int backlog)
+{
+ struct lwip_sock *sock;
+ err_t err;
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d, backlog=%d)\n", s, backlog));
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ /* limit the "backlog" parameter to fit in an u8_t */
+ backlog = LWIP_MIN(LWIP_MAX(backlog, 0), 0xff);
+
+ err = netconn_listen_with_backlog(sock->conn, (u8_t)backlog);
+
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d) failed, err=%d\n", s, err));
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
+ sock_set_errno(sock, EOPNOTSUPP);
+ } else {
+ sock_set_errno(sock, err_to_errno(err));
+ }
+ done_socket(sock);
+ return -1;
+ }
+
+ sock_set_errno(sock, 0);
+ done_socket(sock);
+ return 0;
+}
+
+#if LWIP_TCP
+/* Helper function to loop over receiving pbufs from netconn
+ * until "len" bytes are received or we're otherwise done.
+ * Keeps sock->lastdata for peeking or partly copying.
+ */
+static ssize_t
+lwip_recv_tcp(struct lwip_sock *sock, void *mem, size_t len, int flags)
+{
+ u8_t apiflags = NETCONN_NOAUTORCVD;
+ ssize_t recvd = 0;
+ ssize_t recv_left = (len <= SSIZE_MAX) ? (ssize_t)len : SSIZE_MAX;
+
+ LWIP_ASSERT("no socket given", sock != NULL);
+ LWIP_ASSERT("this should be checked internally", NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP);
+
+ if (flags & MSG_DONTWAIT) {
+ apiflags |= NETCONN_DONTBLOCK;
+ }
+
+ do {
+ struct pbuf *p;
+ err_t err;
+ u16_t copylen;
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: top while sock->lastdata=%p\n", (void *)sock->lastdata.pbuf));
+ /* Check if there is data left from the last recv operation. */
+ if (sock->lastdata.pbuf) {
+ p = sock->lastdata.pbuf;
+ } else {
+ /* No data was left from the previous operation, so we try to get
+ some from the network. */
+ err = netconn_recv_tcp_pbuf_flags(sock->conn, &p, apiflags);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: netconn_recv err=%d, pbuf=%p\n",
+ err, (void *)p));
+
+ if (err != ERR_OK) {
+ if (recvd > 0) {
+ /* already received data, return that (this trusts in getting the same error from
+ netconn layer again next time netconn_recv is called) */
+ goto lwip_recv_tcp_done;
+ }
+ /* We should really do some error checking here. */
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: p == NULL, error is \"%s\"!\n",
+ lwip_strerr(err)));
+ sock_set_errno(sock, err_to_errno(err));
+ if (err == ERR_CLSD) {
+ return 0;
+ } else {
+ return -1;
+ }
+ }
+ LWIP_ASSERT("p != NULL", p != NULL);
+ sock->lastdata.pbuf = p;
+ }
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: buflen=%"U16_F" recv_left=%d off=%d\n",
+ p->tot_len, (int)recv_left, (int)recvd));
+
+ if (recv_left > p->tot_len) {
+ copylen = p->tot_len;
+ } else {
+ copylen = (u16_t)recv_left;
+ }
+ if (recvd + copylen < recvd) {
+ /* overflow */
+ copylen = (u16_t)(SSIZE_MAX - recvd);
+ }
+
+ /* copy the contents of the received buffer into
+ the supplied memory pointer mem */
+ pbuf_copy_partial(p, (u8_t *)mem + recvd, copylen, 0);
+
+ recvd += copylen;
+
+ /* TCP combines multiple pbufs for one recv */
+ LWIP_ASSERT("invalid copylen, len would underflow", recv_left >= copylen);
+ recv_left -= copylen;
+
+ /* Unless we peek the incoming message... */
+ if ((flags & MSG_PEEK) == 0) {
+ /* ... check if there is data left in the pbuf */
+ LWIP_ASSERT("invalid copylen", p->tot_len >= copylen);
+ if (p->tot_len - copylen > 0) {
+ /* If so, it should be saved in the sock structure for the next recv call.
+ We store the pbuf but hide/free the consumed data: */
+ sock->lastdata.pbuf = pbuf_free_header(p, copylen);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: lastdata now pbuf=%p\n", (void *)sock->lastdata.pbuf));
+ } else {
+ sock->lastdata.pbuf = NULL;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: deleting pbuf=%p\n", (void *)p));
+ pbuf_free(p);
+ }
+ }
+ /* once we have some data to return, only add more if we don't need to wait */
+ apiflags |= NETCONN_DONTBLOCK | NETCONN_NOFIN;
+ /* @todo: do we need to support peeking more than one pbuf? */
+ } while ((recv_left > 0) && !(flags & MSG_PEEK));
+lwip_recv_tcp_done:
+ if ((recvd > 0) && !(flags & MSG_PEEK)) {
+ /* ensure window update after copying all data */
+ netconn_tcp_recvd(sock->conn, (size_t)recvd);
+ }
+ sock_set_errno(sock, 0);
+ return recvd;
+}
+#endif
+
+/* Convert a netbuf's address data to struct sockaddr */
+static int
+lwip_sock_make_addr(struct netconn *conn, ip_addr_t *fromaddr, u16_t port,
+ struct sockaddr *from, socklen_t *fromlen)
+{
+ int truncated = 0;
+ union sockaddr_aligned saddr;
+
+ LWIP_UNUSED_ARG(conn);
+
+ LWIP_ASSERT("fromaddr != NULL", fromaddr != NULL);
+ LWIP_ASSERT("from != NULL", from != NULL);
+ LWIP_ASSERT("fromlen != NULL", fromlen != NULL);
+
+#if LWIP_IPV4 && LWIP_IPV6
+ /* Dual-stack: Map IPv4 addresses to IPv4 mapped IPv6 */
+ if (NETCONNTYPE_ISIPV6(netconn_type(conn)) && IP_IS_V4(fromaddr)) {
+ ip4_2_ipv4_mapped_ipv6(ip_2_ip6(fromaddr), ip_2_ip4(fromaddr));
+ IP_SET_TYPE(fromaddr, IPADDR_TYPE_V6);
+ }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+ IPADDR_PORT_TO_SOCKADDR(&saddr, fromaddr, port);
+ if (*fromlen < saddr.sa.sa_len) {
+ truncated = 1;
+ } else if (*fromlen > saddr.sa.sa_len) {
+ *fromlen = saddr.sa.sa_len;
+ }
+ MEMCPY(from, &saddr, *fromlen);
+ return truncated;
+}
+
+#if LWIP_TCP
+/* Helper function to get a tcp socket's remote address info */
+static int
+lwip_recv_tcp_from(struct lwip_sock *sock, struct sockaddr *from, socklen_t *fromlen, const char *dbg_fn, int dbg_s, ssize_t dbg_ret)
+{
+ if (sock == NULL) {
+ return 0;
+ }
+ LWIP_UNUSED_ARG(dbg_fn);
+ LWIP_UNUSED_ARG(dbg_s);
+ LWIP_UNUSED_ARG(dbg_ret);
+
+#if !SOCKETS_DEBUG
+ if (from && fromlen)
+#endif /* !SOCKETS_DEBUG */
+ {
+ /* get remote addr/port from tcp_pcb */
+ u16_t port;
+ ip_addr_t tmpaddr;
+ netconn_getaddr(sock->conn, &tmpaddr, &port, 0);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("%s(%d): addr=", dbg_fn, dbg_s));
+ ip_addr_debug_print_val(SOCKETS_DEBUG, tmpaddr);
+ LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", port, (int)dbg_ret));
+ if (from && fromlen) {
+ return lwip_sock_make_addr(sock->conn, &tmpaddr, port, from, fromlen);
+ }
+ }
+ return 0;
+}
+#endif
+
+/* Helper function to receive a netbuf from a udp or raw netconn.
+ * Keeps sock->lastdata for peeking.
+ */
+static err_t
+lwip_recvfrom_udp_raw(struct lwip_sock *sock, int flags, struct msghdr *msg, u16_t *datagram_len, int dbg_s)
+{
+ struct netbuf *buf;
+ u8_t apiflags;
+ err_t err;
+ u16_t buflen, copylen, copied;
+ int i;
+
+ LWIP_UNUSED_ARG(dbg_s);
+ LWIP_ERROR("lwip_recvfrom_udp_raw: invalid arguments", (msg->msg_iov != NULL) || (msg->msg_iovlen <= 0), return ERR_ARG;);
+
+ if (flags & MSG_DONTWAIT) {
+ apiflags = NETCONN_DONTBLOCK;
+ } else {
+ apiflags = 0;
+ }
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom_udp_raw[UDP/RAW]: top sock->lastdata=%p\n", (void *)sock->lastdata.netbuf));
+ /* Check if there is data left from the last recv operation. */
+ buf = sock->lastdata.netbuf;
+ if (buf == NULL) {
+ /* No data was left from the previous operation, so we try to get
+ some from the network. */
+ err = netconn_recv_udp_raw_netbuf_flags(sock->conn, &buf, apiflags);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom_udp_raw[UDP/RAW]: netconn_recv err=%d, netbuf=%p\n",
+ err, (void *)buf));
+
+ if (err != ERR_OK) {
+ return err;
+ }
+ LWIP_ASSERT("buf != NULL", buf != NULL);
+ sock->lastdata.netbuf = buf;
+ }
+ buflen = buf->p->tot_len;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom_udp_raw: buflen=%"U16_F"\n", buflen));
+
+ copied = 0;
+ /* copy the pbuf payload into the iovs */
+ for (i = 0; (i < msg->msg_iovlen) && (copied < buflen); i++) {
+ u16_t len_left = (u16_t)(buflen - copied);
+ if (msg->msg_iov[i].iov_len > len_left) {
+ copylen = len_left;
+ } else {
+ copylen = (u16_t)msg->msg_iov[i].iov_len;
+ }
+
+ /* copy the contents of the received buffer into
+ the supplied memory buffer */
+ pbuf_copy_partial(buf->p, (u8_t *)msg->msg_iov[i].iov_base, copylen, copied);
+ copied = (u16_t)(copied + copylen);
+ }
+
+ /* Check to see from where the data was.*/
+#if !SOCKETS_DEBUG
+ if (msg->msg_name && msg->msg_namelen)
+#endif /* !SOCKETS_DEBUG */
+ {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom_udp_raw(%d): addr=", dbg_s));
+ ip_addr_debug_print_val(SOCKETS_DEBUG, *netbuf_fromaddr(buf));
+ LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", netbuf_fromport(buf), copied));
+ if (msg->msg_name && msg->msg_namelen) {
+ lwip_sock_make_addr(sock->conn, netbuf_fromaddr(buf), netbuf_fromport(buf),
+ (struct sockaddr *)msg->msg_name, &msg->msg_namelen);
+ }
+ }
+
+ /* Initialize flag output */
+ msg->msg_flags = 0;
+
+ if (msg->msg_control) {
+ u8_t wrote_msg = 0;
+#if LWIP_NETBUF_RECVINFO
+ /* Check if packet info was recorded */
+ if (buf->flags & NETBUF_FLAG_DESTADDR) {
+ if (IP_IS_V4(&buf->toaddr)) {
+#if LWIP_IPV4
+ if (msg->msg_controllen >= CMSG_SPACE(sizeof(struct in_pktinfo))) {
+ struct cmsghdr *chdr = CMSG_FIRSTHDR(msg); /* This will always return a header!! */
+ struct in_pktinfo *pkti = (struct in_pktinfo *)CMSG_DATA(chdr);
+ chdr->cmsg_level = IPPROTO_IP;
+ chdr->cmsg_type = IP_PKTINFO;
+ chdr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
+ pkti->ipi_ifindex = buf->p->if_idx;
+ inet_addr_from_ip4addr(&pkti->ipi_addr, ip_2_ip4(netbuf_destaddr(buf)));
+ msg->msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo));
+ wrote_msg = 1;
+ } else {
+ msg->msg_flags |= MSG_CTRUNC;
+ }
+#endif /* LWIP_IPV4 */
+ }
+ }
+#endif /* LWIP_NETBUF_RECVINFO */
+
+ if (!wrote_msg) {
+ msg->msg_controllen = 0;
+ }
+ }
+
+ /* If we don't peek the incoming message: zero lastdata pointer and free the netbuf */
+ if ((flags & MSG_PEEK) == 0) {
+ sock->lastdata.netbuf = NULL;
+ netbuf_delete(buf);
+ }
+ if (datagram_len) {
+ *datagram_len = buflen;
+ }
+ return ERR_OK;
+}
+
+ssize_t
+lwip_recvfrom(int s, void *mem, size_t len, int flags,
+ struct sockaddr *from, socklen_t *fromlen)
+{
+ struct lwip_sock *sock;
+ ssize_t ret;
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d, %p, %"SZT_F", 0x%x, ..)\n", s, mem, len, flags));
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+#if LWIP_TCP
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+ ret = lwip_recv_tcp(sock, mem, len, flags);
+ lwip_recv_tcp_from(sock, from, fromlen, "lwip_recvfrom", s, ret);
+ done_socket(sock);
+ return ret;
+ } else
+#endif
+ {
+ u16_t datagram_len = 0;
+ struct iovec vec;
+ struct msghdr msg;
+ err_t err;
+ vec.iov_base = mem;
+ vec.iov_len = len;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+ msg.msg_iov = &vec;
+ msg.msg_iovlen = 1;
+ msg.msg_name = from;
+ msg.msg_namelen = (fromlen ? *fromlen : 0);
+ err = lwip_recvfrom_udp_raw(sock, flags, &msg, &datagram_len, s);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom[UDP/RAW](%d): buf == NULL, error is \"%s\"!\n",
+ s, lwip_strerr(err)));
+ sock_set_errno(sock, err_to_errno(err));
+ done_socket(sock);
+ return -1;
+ }
+ ret = (ssize_t)LWIP_MIN(LWIP_MIN(len, datagram_len), SSIZE_MAX);
+ if (fromlen) {
+ *fromlen = msg.msg_namelen;
+ }
+ }
+
+ sock_set_errno(sock, 0);
+ done_socket(sock);
+ return ret;
+}
+
+ssize_t
+lwip_read(int s, void *mem, size_t len)
+{
+ return lwip_recvfrom(s, mem, len, 0, NULL, NULL);
+}
+
+ssize_t
+lwip_readv(int s, const struct iovec *iov, int iovcnt)
+{
+ struct msghdr msg;
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ /* Hack: we have to cast via number to cast from 'const' pointer to non-const.
+ Blame the opengroup standard for this inconsistency. */
+ msg.msg_iov = LWIP_CONST_CAST(struct iovec *, iov);
+ msg.msg_iovlen = iovcnt;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+ return lwip_recvmsg(s, &msg, 0);
+}
+
+ssize_t
+lwip_recv(int s, void *mem, size_t len, int flags)
+{
+ return lwip_recvfrom(s, mem, len, flags, NULL, NULL);
+}
+
+ssize_t
+lwip_recvmsg(int s, struct msghdr *message, int flags)
+{
+ struct lwip_sock *sock;
+ int i;
+ ssize_t buflen;
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvmsg(%d, message=%p, flags=0x%x)\n", s, (void *)message, flags));
+ LWIP_ERROR("lwip_recvmsg: invalid message pointer", message != NULL, return ERR_ARG;);
+ LWIP_ERROR("lwip_recvmsg: unsupported flags", (flags & ~(MSG_PEEK|MSG_DONTWAIT)) == 0,
+ set_errno(EOPNOTSUPP); return -1;);
+
+ if ((message->msg_iovlen <= 0) || (message->msg_iovlen > IOV_MAX)) {
+ set_errno(EMSGSIZE);
+ return -1;
+ }
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ /* check for valid vectors */
+ buflen = 0;
+ for (i = 0; i < message->msg_iovlen; i++) {
+ if ((message->msg_iov[i].iov_base == NULL) || ((ssize_t)message->msg_iov[i].iov_len <= 0) ||
+ ((size_t)(ssize_t)message->msg_iov[i].iov_len != message->msg_iov[i].iov_len) ||
+ ((ssize_t)(buflen + (ssize_t)message->msg_iov[i].iov_len) <= 0)) {
+ sock_set_errno(sock, err_to_errno(ERR_VAL));
+ done_socket(sock);
+ return -1;
+ }
+ buflen = (ssize_t)(buflen + (ssize_t)message->msg_iov[i].iov_len);
+ }
+
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+#if LWIP_TCP
+ int recv_flags = flags;
+ message->msg_flags = 0;
+ /* recv the data */
+ buflen = 0;
+ for (i = 0; i < message->msg_iovlen; i++) {
+ /* try to receive into this vector's buffer */
+ ssize_t recvd_local = lwip_recv_tcp(sock, message->msg_iov[i].iov_base, message->msg_iov[i].iov_len, recv_flags);
+ if (recvd_local > 0) {
+ /* sum up received bytes */
+ buflen += recvd_local;
+ }
+ if ((recvd_local < 0) || (recvd_local < (int)message->msg_iov[i].iov_len) ||
+ (flags & MSG_PEEK)) {
+ /* returned prematurely (or peeking, which might actually be limitated to the first iov) */
+ if (buflen <= 0) {
+ /* nothing received at all, propagate the error */
+ buflen = recvd_local;
+ }
+ break;
+ }
+ /* pass MSG_DONTWAIT to lwip_recv_tcp() to prevent waiting for more data */
+ recv_flags |= MSG_DONTWAIT;
+ }
+ if (buflen > 0) {
+ /* reset socket error since we have received something */
+ sock_set_errno(sock, 0);
+ }
+ /* " If the socket is connected, the msg_name and msg_namelen members shall be ignored." */
+ done_socket(sock);
+ return buflen;
+#else /* LWIP_TCP */
+ sock_set_errno(sock, err_to_errno(ERR_ARG));
+ done_socket(sock);
+ return -1;
+#endif /* LWIP_TCP */
+ }
+ /* else, UDP and RAW NETCONNs */
+#if LWIP_UDP || LWIP_RAW
+ {
+ u16_t datagram_len = 0;
+ err_t err;
+ err = lwip_recvfrom_udp_raw(sock, flags, message, &datagram_len, s);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvmsg[UDP/RAW](%d): buf == NULL, error is \"%s\"!\n",
+ s, lwip_strerr(err)));
+ sock_set_errno(sock, err_to_errno(err));
+ done_socket(sock);
+ return -1;
+ }
+ if (datagram_len > buflen) {
+ message->msg_flags |= MSG_TRUNC;
+ }
+
+ sock_set_errno(sock, 0);
+ done_socket(sock);
+ return (int)datagram_len;
+ }
+#else /* LWIP_UDP || LWIP_RAW */
+ sock_set_errno(sock, err_to_errno(ERR_ARG));
+ done_socket(sock);
+ return -1;
+#endif /* LWIP_UDP || LWIP_RAW */
+}
+
+ssize_t
+lwip_send(int s, const void *data, size_t size, int flags)
+{
+ struct lwip_sock *sock;
+ err_t err;
+ u8_t write_flags;
+ size_t written;
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d, data=%p, size=%"SZT_F", flags=0x%x)\n",
+ s, data, size, flags));
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
+#if (LWIP_UDP || LWIP_RAW)
+ done_socket(sock);
+ return lwip_sendto(s, data, size, flags, NULL, 0);
+#else /* (LWIP_UDP || LWIP_RAW) */
+ sock_set_errno(sock, err_to_errno(ERR_ARG));
+ done_socket(sock);
+ return -1;
+#endif /* (LWIP_UDP || LWIP_RAW) */
+ }
+
+ write_flags = (u8_t)(NETCONN_COPY |
+ ((flags & MSG_MORE) ? NETCONN_MORE : 0) |
+ ((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0));
+ written = 0;
+ err = netconn_write_partly(sock->conn, data, size, write_flags, &written);
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d) err=%d written=%"SZT_F"\n", s, err, written));
+ sock_set_errno(sock, err_to_errno(err));
+ done_socket(sock);
+ /* casting 'written' to ssize_t is OK here since the netconn API limits it to SSIZE_MAX */
+ return (err == ERR_OK ? (ssize_t)written : -1);
+}
+
+ssize_t
+lwip_sendmsg(int s, const struct msghdr *msg, int flags)
+{
+ struct lwip_sock *sock;
+#if LWIP_TCP
+ u8_t write_flags;
+ size_t written;
+#endif
+ err_t err = ERR_OK;
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ LWIP_ERROR("lwip_sendmsg: invalid msghdr", msg != NULL,
+ sock_set_errno(sock, err_to_errno(ERR_ARG)); done_socket(sock); return -1;);
+ LWIP_ERROR("lwip_sendmsg: invalid msghdr iov", msg->msg_iov != NULL,
+ sock_set_errno(sock, err_to_errno(ERR_ARG)); done_socket(sock); return -1;);
+ LWIP_ERROR("lwip_sendmsg: maximum iovs exceeded", (msg->msg_iovlen > 0) && (msg->msg_iovlen <= IOV_MAX),
+ sock_set_errno(sock, EMSGSIZE); done_socket(sock); return -1;);
+ LWIP_ERROR("lwip_sendmsg: unsupported flags", (flags & ~(MSG_DONTWAIT | MSG_MORE)) == 0,
+ sock_set_errno(sock, EOPNOTSUPP); done_socket(sock); return -1;);
+
+ LWIP_UNUSED_ARG(msg->msg_control);
+ LWIP_UNUSED_ARG(msg->msg_controllen);
+ LWIP_UNUSED_ARG(msg->msg_flags);
+
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+#if LWIP_TCP
+ write_flags = (u8_t)(NETCONN_COPY |
+ ((flags & MSG_MORE) ? NETCONN_MORE : 0) |
+ ((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0));
+
+ written = 0;
+ err = netconn_write_vectors_partly(sock->conn, (struct netvector *)msg->msg_iov, (u16_t)msg->msg_iovlen, write_flags, &written);
+ sock_set_errno(sock, err_to_errno(err));
+ done_socket(sock);
+ /* casting 'written' to ssize_t is OK here since the netconn API limits it to SSIZE_MAX */
+ return (err == ERR_OK ? (ssize_t)written : -1);
+#else /* LWIP_TCP */
+ sock_set_errno(sock, err_to_errno(ERR_ARG));
+ done_socket(sock);
+ return -1;
+#endif /* LWIP_TCP */
+ }
+ /* else, UDP and RAW NETCONNs */
+#if LWIP_UDP || LWIP_RAW
+ {
+ struct netbuf chain_buf;
+ int i;
+ ssize_t size = 0;
+
+ LWIP_UNUSED_ARG(flags);
+ LWIP_ERROR("lwip_sendmsg: invalid msghdr name", (((msg->msg_name == NULL) && (msg->msg_namelen == 0)) ||
+ IS_SOCK_ADDR_LEN_VALID(msg->msg_namelen)),
+ sock_set_errno(sock, err_to_errno(ERR_ARG)); done_socket(sock); return -1;);
+
+ /* initialize chain buffer with destination */
+ memset(&chain_buf, 0, sizeof(struct netbuf));
+ if (msg->msg_name) {
+ u16_t remote_port;
+ SOCKADDR_TO_IPADDR_PORT((const struct sockaddr *)msg->msg_name, &chain_buf.addr, remote_port);
+ netbuf_fromport(&chain_buf) = remote_port;
+ }
+#if LWIP_NETIF_TX_SINGLE_PBUF
+ for (i = 0; i < msg->msg_iovlen; i++) {
+ size += msg->msg_iov[i].iov_len;
+ if ((msg->msg_iov[i].iov_len > INT_MAX) || (size < (int)msg->msg_iov[i].iov_len)) {
+ /* overflow */
+ goto sendmsg_emsgsize;
+ }
+ }
+ if (size > 0xFFFF) {
+ /* overflow */
+ goto sendmsg_emsgsize;
+ }
+ /* Allocate a new netbuf and copy the data into it. */
+ if (netbuf_alloc(&chain_buf, (u16_t)size) == NULL) {
+ err = ERR_MEM;
+ } else {
+ /* flatten the IO vectors */
+ size_t offset = 0;
+ for (i = 0; i < msg->msg_iovlen; i++) {
+ MEMCPY(&((u8_t *)chain_buf.p->payload)[offset], msg->msg_iov[i].iov_base, msg->msg_iov[i].iov_len);
+ offset += msg->msg_iov[i].iov_len;
+ }
+#if LWIP_CHECKSUM_ON_COPY
+ {
+ /* This can be improved by using LWIP_CHKSUM_COPY() and aggregating the checksum for each IO vector */
+ u16_t chksum = ~inet_chksum_pbuf(chain_buf.p);
+ netbuf_set_chksum(&chain_buf, chksum);
+ }
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ err = ERR_OK;
+ }
+#else /* LWIP_NETIF_TX_SINGLE_PBUF */
+ /* create a chained netbuf from the IO vectors. NOTE: we assemble a pbuf chain
+ manually to avoid having to allocate, chain, and delete a netbuf for each iov */
+ for (i = 0; i < msg->msg_iovlen; i++) {
+ struct pbuf *p;
+ if (msg->msg_iov[i].iov_len > 0xFFFF) {
+ /* overflow */
+ goto sendmsg_emsgsize;
+ }
+ p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF);
+ if (p == NULL) {
+ err = ERR_MEM; /* let netbuf_delete() cleanup chain_buf */
+ break;
+ }
+ p->payload = msg->msg_iov[i].iov_base;
+ p->len = p->tot_len = (u16_t)msg->msg_iov[i].iov_len;
+ /* netbuf empty, add new pbuf */
+ if (chain_buf.p == NULL) {
+ chain_buf.p = chain_buf.ptr = p;
+ /* add pbuf to existing pbuf chain */
+ } else {
+ if (chain_buf.p->tot_len + p->len > 0xffff) {
+ /* overflow */
+ pbuf_free(p);
+ goto sendmsg_emsgsize;
+ }
+ pbuf_cat(chain_buf.p, p);
+ }
+ }
+ /* save size of total chain */
+ if (err == ERR_OK) {
+ size = netbuf_len(&chain_buf);
+ }
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+
+ if (err == ERR_OK) {
+#if LWIP_IPV4 && LWIP_IPV6
+ /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */
+ if (IP_IS_V6_VAL(chain_buf.addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(&chain_buf.addr))) {
+ unmap_ipv4_mapped_ipv6(ip_2_ip4(&chain_buf.addr), ip_2_ip6(&chain_buf.addr));
+ IP_SET_TYPE_VAL(chain_buf.addr, IPADDR_TYPE_V4);
+ }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+ /* send the data */
+ err = netconn_send(sock->conn, &chain_buf);
+ }
+
+ /* deallocated the buffer */
+ netbuf_free(&chain_buf);
+
+ sock_set_errno(sock, err_to_errno(err));
+ done_socket(sock);
+ return (err == ERR_OK ? size : -1);
+sendmsg_emsgsize:
+ sock_set_errno(sock, EMSGSIZE);
+ netbuf_free(&chain_buf);
+ done_socket(sock);
+ return -1;
+ }
+#else /* LWIP_UDP || LWIP_RAW */
+ sock_set_errno(sock, err_to_errno(ERR_ARG));
+ done_socket(sock);
+ return -1;
+#endif /* LWIP_UDP || LWIP_RAW */
+}
+
+ssize_t
+lwip_sendto(int s, const void *data, size_t size, int flags,
+ const struct sockaddr *to, socklen_t tolen)
+{
+ struct lwip_sock *sock;
+ err_t err;
+ u16_t short_size;
+ u16_t remote_port;
+ struct netbuf buf;
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+#if LWIP_TCP
+ done_socket(sock);
+ return lwip_send(s, data, size, flags);
+#else /* LWIP_TCP */
+ LWIP_UNUSED_ARG(flags);
+ sock_set_errno(sock, err_to_errno(ERR_ARG));
+ done_socket(sock);
+ return -1;
+#endif /* LWIP_TCP */
+ }
+
+ if (size > LWIP_MIN(0xFFFF, SSIZE_MAX)) {
+ /* cannot fit into one datagram (at least for us) */
+ sock_set_errno(sock, EMSGSIZE);
+ done_socket(sock);
+ return -1;
+ }
+ short_size = (u16_t)size;
+ LWIP_ERROR("lwip_sendto: invalid address", (((to == NULL) && (tolen == 0)) ||
+ (IS_SOCK_ADDR_LEN_VALID(tolen) &&
+ ((to != NULL) && (IS_SOCK_ADDR_TYPE_VALID(to) && IS_SOCK_ADDR_ALIGNED(to))))),
+ sock_set_errno(sock, err_to_errno(ERR_ARG)); done_socket(sock); return -1;);
+ LWIP_UNUSED_ARG(tolen);
+
+ /* initialize a buffer */
+ buf.p = buf.ptr = NULL;
+#if LWIP_CHECKSUM_ON_COPY
+ buf.flags = 0;
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ if (to) {
+ SOCKADDR_TO_IPADDR_PORT(to, &buf.addr, remote_port);
+ } else {
+ remote_port = 0;
+ ip_addr_set_any(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), &buf.addr);
+ }
+ netbuf_fromport(&buf) = remote_port;
+
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_sendto(%d, data=%p, short_size=%"U16_F", flags=0x%x to=",
+ s, data, short_size, flags));
+ ip_addr_debug_print_val(SOCKETS_DEBUG, buf.addr);
+ LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", remote_port));
+
+ /* make the buffer point to the data that should be sent */
+#if LWIP_NETIF_TX_SINGLE_PBUF
+ /* Allocate a new netbuf and copy the data into it. */
+ if (netbuf_alloc(&buf, short_size) == NULL) {
+ err = ERR_MEM;
+ } else {
+#if LWIP_CHECKSUM_ON_COPY
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_RAW) {
+ u16_t chksum = LWIP_CHKSUM_COPY(buf.p->payload, data, short_size);
+ netbuf_set_chksum(&buf, chksum);
+ } else
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ {
+ MEMCPY(buf.p->payload, data, short_size);
+ }
+ err = ERR_OK;
+ }
+#else /* LWIP_NETIF_TX_SINGLE_PBUF */
+ err = netbuf_ref(&buf, data, short_size);
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+ if (err == ERR_OK) {
+#if LWIP_IPV4 && LWIP_IPV6
+ /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */
+ if (IP_IS_V6_VAL(buf.addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(&buf.addr))) {
+ unmap_ipv4_mapped_ipv6(ip_2_ip4(&buf.addr), ip_2_ip6(&buf.addr));
+ IP_SET_TYPE_VAL(buf.addr, IPADDR_TYPE_V4);
+ }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+ /* send the data */
+ err = netconn_send(sock->conn, &buf);
+ }
+
+ /* deallocated the buffer */
+ netbuf_free(&buf);
+
+ sock_set_errno(sock, err_to_errno(err));
+ done_socket(sock);
+ return (err == ERR_OK ? short_size : -1);
+}
+
+int
+lwip_socket(int domain, int type, int protocol)
+{
+ struct netconn *conn;
+ int i;
+
+ LWIP_UNUSED_ARG(domain); /* @todo: check this */
+
+ /* create a netconn */
+ switch (type) {
+ case SOCK_RAW:
+ conn = netconn_new_with_proto_and_callback(DOMAIN_TO_NETCONN_TYPE(domain, NETCONN_RAW),
+ (u8_t)protocol, DEFAULT_SOCKET_EVENTCB);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_RAW, %d) = ",
+ domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
+ break;
+ case SOCK_DGRAM:
+ conn = netconn_new_with_callback(DOMAIN_TO_NETCONN_TYPE(domain,
+ ((protocol == IPPROTO_UDPLITE) ? NETCONN_UDPLITE : NETCONN_UDP)),
+ DEFAULT_SOCKET_EVENTCB);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_DGRAM, %d) = ",
+ domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
+#if LWIP_NETBUF_RECVINFO
+ if (conn) {
+ /* netconn layer enables pktinfo by default, sockets default to off */
+ conn->flags &= ~NETCONN_FLAG_PKTINFO;
+ }
+#endif /* LWIP_NETBUF_RECVINFO */
+ break;
+ case SOCK_STREAM:
+ conn = netconn_new_with_callback(DOMAIN_TO_NETCONN_TYPE(domain, NETCONN_TCP), DEFAULT_SOCKET_EVENTCB);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_STREAM, %d) = ",
+ domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
+ break;
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%d, %d/UNKNOWN, %d) = -1\n",
+ domain, type, protocol));
+ set_errno(EINVAL);
+ return -1;
+ }
+
+ if (!conn) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("-1 / ENOBUFS (could not create netconn)\n"));
+ set_errno(ENOBUFS);
+ return -1;
+ }
+
+ i = alloc_socket(conn, 0);
+
+ if (i == -1) {
+ netconn_delete(conn);
+ set_errno(ENFILE);
+ return -1;
+ }
+ conn->socket = i;
+ done_socket(&sockets[i - LWIP_SOCKET_OFFSET]);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("%d\n", i));
+ set_errno(0);
+ return i;
+}
+
+ssize_t
+lwip_write(int s, const void *data, size_t size)
+{
+ return lwip_send(s, data, size, 0);
+}
+
+ssize_t
+lwip_writev(int s, const struct iovec *iov, int iovcnt)
+{
+ struct msghdr msg;
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ /* Hack: we have to cast via number to cast from 'const' pointer to non-const.
+ Blame the opengroup standard for this inconsistency. */
+ msg.msg_iov = LWIP_CONST_CAST(struct iovec *, iov);
+ msg.msg_iovlen = iovcnt;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+ return lwip_sendmsg(s, &msg, 0);
+}
+
+#if LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL
+/* Add select_cb to select_cb_list. */
+static void
+lwip_link_select_cb(struct lwip_select_cb *select_cb)
+{
+ LWIP_SOCKET_SELECT_DECL_PROTECT(lev);
+
+ /* Protect the select_cb_list */
+ LWIP_SOCKET_SELECT_PROTECT(lev);
+
+ /* Put this select_cb on top of list */
+ select_cb->next = select_cb_list;
+ if (select_cb_list != NULL) {
+ select_cb_list->prev = select_cb;
+ }
+ select_cb_list = select_cb;
+#if !LWIP_TCPIP_CORE_LOCKING
+ /* Increasing this counter tells select_check_waiters that the list has changed. */
+ select_cb_ctr++;
+#endif
+
+ /* Now we can safely unprotect */
+ LWIP_SOCKET_SELECT_UNPROTECT(lev);
+}
+
+/* Remove select_cb from select_cb_list. */
+static void
+lwip_unlink_select_cb(struct lwip_select_cb *select_cb)
+{
+ LWIP_SOCKET_SELECT_DECL_PROTECT(lev);
+
+ /* Take us off the list */
+ LWIP_SOCKET_SELECT_PROTECT(lev);
+ if (select_cb->next != NULL) {
+ select_cb->next->prev = select_cb->prev;
+ }
+ if (select_cb_list == select_cb) {
+ LWIP_ASSERT("select_cb->prev == NULL", select_cb->prev == NULL);
+ select_cb_list = select_cb->next;
+ } else {
+ LWIP_ASSERT("select_cb->prev != NULL", select_cb->prev != NULL);
+ select_cb->prev->next = select_cb->next;
+ }
+#if !LWIP_TCPIP_CORE_LOCKING
+ /* Increasing this counter tells select_check_waiters that the list has changed. */
+ select_cb_ctr++;
+#endif
+ LWIP_SOCKET_SELECT_UNPROTECT(lev);
+}
+#endif /* LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL */
+
+#if LWIP_SOCKET_SELECT
+/**
+ * Go through the readset and writeset lists and see which socket of the sockets
+ * set in the sets has events. On return, readset, writeset and exceptset have
+ * the sockets enabled that had events.
+ *
+ * @param maxfdp1 the highest socket index in the sets
+ * @param readset_in set of sockets to check for read events
+ * @param writeset_in set of sockets to check for write events
+ * @param exceptset_in set of sockets to check for error events
+ * @param readset_out set of sockets that had read events
+ * @param writeset_out set of sockets that had write events
+ * @param exceptset_out set os sockets that had error events
+ * @return number of sockets that had events (read/write/exception) (>= 0)
+ */
+static int
+lwip_selscan(int maxfdp1, fd_set *readset_in, fd_set *writeset_in, fd_set *exceptset_in,
+ fd_set *readset_out, fd_set *writeset_out, fd_set *exceptset_out)
+{
+ int i, nready = 0;
+ fd_set lreadset, lwriteset, lexceptset;
+ struct lwip_sock *sock;
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ FD_ZERO(&lreadset);
+ FD_ZERO(&lwriteset);
+ FD_ZERO(&lexceptset);
+
+ /* Go through each socket in each list to count number of sockets which
+ currently match */
+ for (i = LWIP_SOCKET_OFFSET; i < maxfdp1; i++) {
+ /* if this FD is not in the set, continue */
+ if (!(readset_in && FD_ISSET(i, readset_in)) &&
+ !(writeset_in && FD_ISSET(i, writeset_in)) &&
+ !(exceptset_in && FD_ISSET(i, exceptset_in))) {
+ continue;
+ }
+ /* First get the socket's status (protected)... */
+ SYS_ARCH_PROTECT(lev);
+ sock = tryget_socket_unconn_locked(i);
+ if (sock != NULL) {
+ void *lastdata = sock->lastdata.pbuf;
+ s16_t rcvevent = sock->rcvevent;
+ u16_t sendevent = sock->sendevent;
+ u16_t errevent = sock->errevent;
+ SYS_ARCH_UNPROTECT(lev);
+
+ /* ... then examine it: */
+ /* See if netconn of this socket is ready for read */
+ if (readset_in && FD_ISSET(i, readset_in) && ((lastdata != NULL) || (rcvevent > 0))) {
+ FD_SET(i, &lreadset);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for reading\n", i));
+ nready++;
+ }
+ /* See if netconn of this socket is ready for write */
+ if (writeset_in && FD_ISSET(i, writeset_in) && (sendevent != 0)) {
+ FD_SET(i, &lwriteset);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for writing\n", i));
+ nready++;
+ }
+ /* See if netconn of this socket had an error */
+ if (exceptset_in && FD_ISSET(i, exceptset_in) && (errevent != 0)) {
+ FD_SET(i, &lexceptset);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for exception\n", i));
+ nready++;
+ }
+ done_socket(sock);
+ } else {
+ SYS_ARCH_UNPROTECT(lev);
+ /* no a valid open socket */
+ return -1;
+ }
+ }
+ /* copy local sets to the ones provided as arguments */
+ *readset_out = lreadset;
+ *writeset_out = lwriteset;
+ *exceptset_out = lexceptset;
+
+ LWIP_ASSERT("nready >= 0", nready >= 0);
+ return nready;
+}
+
+#if LWIP_NETCONN_FULLDUPLEX
+/* Mark all of the set sockets in one of the three fdsets passed to select as used.
+ * All sockets are marked (and later unmarked), whether they are open or not.
+ * This is OK as lwip_selscan aborts select when non-open sockets are found.
+ */
+static void
+lwip_select_inc_sockets_used_set(int maxfdp, fd_set *fdset, fd_set *used_sockets)
+{
+ SYS_ARCH_DECL_PROTECT(lev);
+ if (fdset) {
+ int i;
+ for (i = LWIP_SOCKET_OFFSET; i < maxfdp; i++) {
+ /* if this FD is in the set, lock it (unless already done) */
+ if (FD_ISSET(i, fdset) && !FD_ISSET(i, used_sockets)) {
+ struct lwip_sock *sock;
+ SYS_ARCH_PROTECT(lev);
+ sock = tryget_socket_unconn_locked(i);
+ if (sock != NULL) {
+ /* leave the socket used until released by lwip_select_dec_sockets_used */
+ FD_SET(i, used_sockets);
+ }
+ SYS_ARCH_UNPROTECT(lev);
+ }
+ }
+ }
+}
+
+/* Mark all sockets passed to select as used to prevent them from being freed
+ * from other threads while select is running.
+ * Marked sockets are added to 'used_sockets' to mark them only once an be able
+ * to unmark them correctly.
+ */
+static void
+lwip_select_inc_sockets_used(int maxfdp, fd_set *fdset1, fd_set *fdset2, fd_set *fdset3, fd_set *used_sockets)
+{
+ FD_ZERO(used_sockets);
+ lwip_select_inc_sockets_used_set(maxfdp, fdset1, used_sockets);
+ lwip_select_inc_sockets_used_set(maxfdp, fdset2, used_sockets);
+ lwip_select_inc_sockets_used_set(maxfdp, fdset3, used_sockets);
+}
+
+/* Let go all sockets that were marked as used when starting select */
+static void
+lwip_select_dec_sockets_used(int maxfdp, fd_set *used_sockets)
+{
+ int i;
+ for (i = LWIP_SOCKET_OFFSET; i < maxfdp; i++) {
+ /* if this FD is not in the set, continue */
+ if (FD_ISSET(i, used_sockets)) {
+ struct lwip_sock *sock = tryget_socket_unconn_nouse(i);
+ LWIP_ASSERT("socket gone at the end of select", sock != NULL);
+ if (sock != NULL) {
+ done_socket(sock);
+ }
+ }
+ }
+}
+#else /* LWIP_NETCONN_FULLDUPLEX */
+#define lwip_select_inc_sockets_used(maxfdp1, readset, writeset, exceptset, used_sockets)
+#define lwip_select_dec_sockets_used(maxfdp1, used_sockets)
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+
+int
+lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
+ struct timeval *timeout)
+{
+ u32_t waitres = 0;
+ int nready;
+ fd_set lreadset, lwriteset, lexceptset;
+ u32_t msectimeout;
+ int i;
+ int maxfdp2;
+#if LWIP_NETCONN_SEM_PER_THREAD
+ int waited = 0;
+#endif
+#if LWIP_NETCONN_FULLDUPLEX
+ fd_set used_sockets;
+#endif
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select(%d, %p, %p, %p, tvsec=%"S32_F" tvusec=%"S32_F")\n",
+ maxfdp1, (void *)readset, (void *) writeset, (void *) exceptset,
+ timeout ? (s32_t)timeout->tv_sec : (s32_t) - 1,
+ timeout ? (s32_t)timeout->tv_usec : (s32_t) - 1));
+
+ if ((maxfdp1 < 0) || (maxfdp1 > LWIP_SELECT_MAXNFDS)) {
+ set_errno(EINVAL);
+ return -1;
+ }
+
+ lwip_select_inc_sockets_used(maxfdp1, readset, writeset, exceptset, &used_sockets);
+
+ /* Go through each socket in each list to count number of sockets which
+ currently match */
+ nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
+
+ if (nready < 0) {
+ /* one of the sockets in one of the fd_sets was invalid */
+ set_errno(EBADF);
+ lwip_select_dec_sockets_used(maxfdp1, &used_sockets);
+ return -1;
+ } else if (nready > 0) {
+ /* one or more sockets are set, no need to wait */
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: nready=%d\n", nready));
+ } else {
+ /* If we don't have any current events, then suspend if we are supposed to */
+ if (timeout && timeout->tv_sec == 0 && timeout->tv_usec == 0) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: no timeout, returning 0\n"));
+ /* This is OK as the local fdsets are empty and nready is zero,
+ or we would have returned earlier. */
+ } else {
+ /* None ready: add our semaphore to list:
+ We don't actually need any dynamic memory. Our entry on the
+ list is only valid while we are in this function, so it's ok
+ to use local variables (unless we're running in MPU compatible
+ mode). */
+ API_SELECT_CB_VAR_DECLARE(select_cb);
+ API_SELECT_CB_VAR_ALLOC(select_cb, set_errno(ENOMEM); lwip_select_dec_sockets_used(maxfdp1, &used_sockets); return -1);
+ memset(&API_SELECT_CB_VAR_REF(select_cb), 0, sizeof(struct lwip_select_cb));
+
+ API_SELECT_CB_VAR_REF(select_cb).readset = readset;
+ API_SELECT_CB_VAR_REF(select_cb).writeset = writeset;
+ API_SELECT_CB_VAR_REF(select_cb).exceptset = exceptset;
+#if LWIP_NETCONN_SEM_PER_THREAD
+ API_SELECT_CB_VAR_REF(select_cb).sem = LWIP_NETCONN_THREAD_SEM_GET();
+#else /* LWIP_NETCONN_SEM_PER_THREAD */
+ if (sys_sem_new(&API_SELECT_CB_VAR_REF(select_cb).sem, 0) != ERR_OK) {
+ /* failed to create semaphore */
+ set_errno(ENOMEM);
+ lwip_select_dec_sockets_used(maxfdp1, &used_sockets);
+ API_SELECT_CB_VAR_FREE(select_cb);
+ return -1;
+ }
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+ lwip_link_select_cb(&API_SELECT_CB_VAR_REF(select_cb));
+
+ /* Increase select_waiting for each socket we are interested in */
+ maxfdp2 = maxfdp1;
+ for (i = LWIP_SOCKET_OFFSET; i < maxfdp1; i++) {
+ if ((readset && FD_ISSET(i, readset)) ||
+ (writeset && FD_ISSET(i, writeset)) ||
+ (exceptset && FD_ISSET(i, exceptset))) {
+ struct lwip_sock *sock;
+ SYS_ARCH_PROTECT(lev);
+ sock = tryget_socket_unconn_locked(i);
+ if (sock != NULL) {
+ sock->select_waiting++;
+ if (sock->select_waiting == 0) {
+ /* overflow - too many threads waiting */
+ sock->select_waiting--;
+ nready = -1;
+ maxfdp2 = i;
+ SYS_ARCH_UNPROTECT(lev);
+ done_socket(sock);
+ set_errno(EBUSY);
+ break;
+ }
+ SYS_ARCH_UNPROTECT(lev);
+ done_socket(sock);
+ } else {
+ /* Not a valid socket */
+ nready = -1;
+ maxfdp2 = i;
+ SYS_ARCH_UNPROTECT(lev);
+ set_errno(EBADF);
+ break;
+ }
+ }
+ }
+
+ if (nready >= 0) {
+ /* Call lwip_selscan again: there could have been events between
+ the last scan (without us on the list) and putting us on the list! */
+ nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
+ if (!nready) {
+ /* Still none ready, just wait to be woken */
+ if (timeout == 0) {
+ /* Wait forever */
+ msectimeout = 0;
+ } else {
+ long msecs_long = ((timeout->tv_sec * 1000) + ((timeout->tv_usec + 500) / 1000));
+ if (msecs_long <= 0) {
+ /* Wait 1ms at least (0 means wait forever) */
+ msectimeout = 1;
+ } else {
+ msectimeout = (u32_t)msecs_long;
+ }
+ }
+
+ waitres = sys_arch_sem_wait(SELECT_SEM_PTR(API_SELECT_CB_VAR_REF(select_cb).sem), msectimeout);
+#if LWIP_NETCONN_SEM_PER_THREAD
+ waited = 1;
+#endif
+ }
+ }
+
+ /* Decrease select_waiting for each socket we are interested in */
+ for (i = LWIP_SOCKET_OFFSET; i < maxfdp2; i++) {
+ if ((readset && FD_ISSET(i, readset)) ||
+ (writeset && FD_ISSET(i, writeset)) ||
+ (exceptset && FD_ISSET(i, exceptset))) {
+ struct lwip_sock *sock;
+ SYS_ARCH_PROTECT(lev);
+ sock = tryget_socket_unconn_locked(i);
+ if (sock != NULL) {
+ /* for now, handle select_waiting==0... */
+ LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0);
+ if (sock->select_waiting > 0) {
+ sock->select_waiting--;
+ }
+ SYS_ARCH_UNPROTECT(lev);
+ done_socket(sock);
+ } else {
+ SYS_ARCH_UNPROTECT(lev);
+ /* Not a valid socket */
+ nready = -1;
+ set_errno(EBADF);
+ }
+ }
+ }
+
+ lwip_unlink_select_cb(&API_SELECT_CB_VAR_REF(select_cb));
+
+#if LWIP_NETCONN_SEM_PER_THREAD
+ if (API_SELECT_CB_VAR_REF(select_cb).sem_signalled && (!waited || (waitres == SYS_ARCH_TIMEOUT))) {
+ /* don't leave the thread-local semaphore signalled */
+ sys_arch_sem_wait(API_SELECT_CB_VAR_REF(select_cb).sem, 1);
+ }
+#else /* LWIP_NETCONN_SEM_PER_THREAD */
+ sys_sem_free(&API_SELECT_CB_VAR_REF(select_cb).sem);
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+ API_SELECT_CB_VAR_FREE(select_cb);
+
+ if (nready < 0) {
+ /* This happens when a socket got closed while waiting */
+ lwip_select_dec_sockets_used(maxfdp1, &used_sockets);
+ return -1;
+ }
+
+ if (waitres == SYS_ARCH_TIMEOUT) {
+ /* Timeout */
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: timeout expired\n"));
+ /* This is OK as the local fdsets are empty and nready is zero,
+ or we would have returned earlier. */
+ } else {
+ /* See what's set now after waiting */
+ nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: nready=%d\n", nready));
+ }
+ }
+ }
+
+ lwip_select_dec_sockets_used(maxfdp1, &used_sockets);
+ set_errno(0);
+ if (readset) {
+ *readset = lreadset;
+ }
+ if (writeset) {
+ *writeset = lwriteset;
+ }
+ if (exceptset) {
+ *exceptset = lexceptset;
+ }
+ return nready;
+}
+#endif /* LWIP_SOCKET_SELECT */
+
+#if LWIP_SOCKET_POLL
+/** Options for the lwip_pollscan function. */
+enum lwip_pollscan_opts
+{
+ /** Clear revents in each struct pollfd. */
+ LWIP_POLLSCAN_CLEAR = 1,
+
+ /** Increment select_waiting in each struct lwip_sock. */
+ LWIP_POLLSCAN_INC_WAIT = 2,
+
+ /** Decrement select_waiting in each struct lwip_sock. */
+ LWIP_POLLSCAN_DEC_WAIT = 4
+};
+
+/**
+ * Update revents in each struct pollfd.
+ * Optionally update select_waiting in struct lwip_sock.
+ *
+ * @param fds array of structures to update
+ * @param nfds number of structures in fds
+ * @param opts what to update and how
+ * @return number of structures that have revents != 0
+ */
+static int
+lwip_pollscan(struct pollfd *fds, nfds_t nfds, enum lwip_pollscan_opts opts)
+{
+ int nready = 0;
+ nfds_t fdi;
+ struct lwip_sock *sock;
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ /* Go through each struct pollfd in the array. */
+ for (fdi = 0; fdi < nfds; fdi++) {
+ if ((opts & LWIP_POLLSCAN_CLEAR) != 0) {
+ fds[fdi].revents = 0;
+ }
+
+ /* Negative fd means the caller wants us to ignore this struct.
+ POLLNVAL means we already detected that the fd is invalid;
+ if another thread has since opened a new socket with that fd,
+ we must not use that socket. */
+ if (fds[fdi].fd >= 0 && (fds[fdi].revents & POLLNVAL) == 0) {
+ /* First get the socket's status (protected)... */
+ SYS_ARCH_PROTECT(lev);
+ sock = tryget_socket_unconn_locked(fds[fdi].fd);
+ if (sock != NULL) {
+ void* lastdata = sock->lastdata.pbuf;
+ s16_t rcvevent = sock->rcvevent;
+ u16_t sendevent = sock->sendevent;
+ u16_t errevent = sock->errevent;
+
+ if ((opts & LWIP_POLLSCAN_INC_WAIT) != 0) {
+ sock->select_waiting++;
+ if (sock->select_waiting == 0) {
+ /* overflow - too many threads waiting */
+ sock->select_waiting--;
+ nready = -1;
+ SYS_ARCH_UNPROTECT(lev);
+ done_socket(sock);
+ break;
+ }
+ } else if ((opts & LWIP_POLLSCAN_DEC_WAIT) != 0) {
+ /* for now, handle select_waiting==0... */
+ LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0);
+ if (sock->select_waiting > 0) {
+ sock->select_waiting--;
+ }
+ }
+ SYS_ARCH_UNPROTECT(lev);
+ done_socket(sock);
+
+ /* ... then examine it: */
+ /* See if netconn of this socket is ready for read */
+ if ((fds[fdi].events & POLLIN) != 0 && ((lastdata != NULL) || (rcvevent > 0))) {
+ fds[fdi].revents |= POLLIN;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_pollscan: fd=%d ready for reading\n", fds[fdi].fd));
+ }
+ /* See if netconn of this socket is ready for write */
+ if ((fds[fdi].events & POLLOUT) != 0 && (sendevent != 0)) {
+ fds[fdi].revents |= POLLOUT;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_pollscan: fd=%d ready for writing\n", fds[fdi].fd));
+ }
+ /* See if netconn of this socket had an error */
+ if (errevent != 0) {
+ /* POLLERR is output only. */
+ fds[fdi].revents |= POLLERR;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_pollscan: fd=%d ready for exception\n", fds[fdi].fd));
+ }
+ } else {
+ /* Not a valid socket */
+ SYS_ARCH_UNPROTECT(lev);
+ /* POLLNVAL is output only. */
+ fds[fdi].revents |= POLLNVAL;
+ return -1;
+ }
+ }
+
+ /* Will return the number of structures that have events,
+ not the number of events. */
+ if (fds[fdi].revents != 0) {
+ nready++;
+ }
+ }
+
+ LWIP_ASSERT("nready >= 0", nready >= 0);
+ return nready;
+}
+
+#if LWIP_NETCONN_FULLDUPLEX
+/* Mark all sockets as used.
+ *
+ * All sockets are marked (and later unmarked), whether they are open or not.
+ * This is OK as lwip_pollscan aborts select when non-open sockets are found.
+ */
+static void
+lwip_poll_inc_sockets_used(struct pollfd *fds, nfds_t nfds)
+{
+ nfds_t fdi;
+
+ if(fds) {
+ /* Go through each struct pollfd in the array. */
+ for (fdi = 0; fdi < nfds; fdi++) {
+ /* Increase the reference counter */
+ tryget_socket_unconn(fds[fdi].fd);
+ }
+ }
+}
+
+/* Let go all sockets that were marked as used when starting poll */
+static void
+lwip_poll_dec_sockets_used(struct pollfd *fds, nfds_t nfds)
+{
+ nfds_t fdi;
+
+ if(fds) {
+ /* Go through each struct pollfd in the array. */
+ for (fdi = 0; fdi < nfds; fdi++) {
+ struct lwip_sock *sock = tryget_socket_unconn_nouse(fds[fdi].fd);
+ if (sock != NULL) {
+ done_socket(sock);
+ }
+ }
+ }
+}
+#else /* LWIP_NETCONN_FULLDUPLEX */
+#define lwip_poll_inc_sockets_used(fds, nfds)
+#define lwip_poll_dec_sockets_used(fds, nfds)
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+
+int
+lwip_poll(struct pollfd *fds, nfds_t nfds, int timeout)
+{
+ u32_t waitres = 0;
+ int nready;
+ u32_t msectimeout;
+#if LWIP_NETCONN_SEM_PER_THREAD
+ int waited = 0;
+#endif
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_poll(%p, %d, %d)\n",
+ (void*)fds, (int)nfds, timeout));
+ LWIP_ERROR("lwip_poll: invalid fds", ((fds != NULL && nfds > 0) || (fds == NULL && nfds == 0)),
+ set_errno(EINVAL); return -1;);
+
+ lwip_poll_inc_sockets_used(fds, nfds);
+
+ /* Go through each struct pollfd to count number of structures
+ which currently match */
+ nready = lwip_pollscan(fds, nfds, LWIP_POLLSCAN_CLEAR);
+
+ if (nready < 0) {
+ lwip_poll_dec_sockets_used(fds, nfds);
+ return -1;
+ }
+
+ /* If we don't have any current events, then suspend if we are supposed to */
+ if (!nready) {
+ API_SELECT_CB_VAR_DECLARE(select_cb);
+
+ if (timeout == 0) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_poll: no timeout, returning 0\n"));
+ goto return_success;
+ }
+ API_SELECT_CB_VAR_ALLOC(select_cb, set_errno(EAGAIN); lwip_poll_dec_sockets_used(fds, nfds); return -1);
+ memset(&API_SELECT_CB_VAR_REF(select_cb), 0, sizeof(struct lwip_select_cb));
+
+ /* None ready: add our semaphore to list:
+ We don't actually need any dynamic memory. Our entry on the
+ list is only valid while we are in this function, so it's ok
+ to use local variables. */
+
+ API_SELECT_CB_VAR_REF(select_cb).poll_fds = fds;
+ API_SELECT_CB_VAR_REF(select_cb).poll_nfds = nfds;
+#if LWIP_NETCONN_SEM_PER_THREAD
+ API_SELECT_CB_VAR_REF(select_cb).sem = LWIP_NETCONN_THREAD_SEM_GET();
+#else /* LWIP_NETCONN_SEM_PER_THREAD */
+ if (sys_sem_new(&API_SELECT_CB_VAR_REF(select_cb).sem, 0) != ERR_OK) {
+ /* failed to create semaphore */
+ set_errno(EAGAIN);
+ lwip_poll_dec_sockets_used(fds, nfds);
+ API_SELECT_CB_VAR_FREE(select_cb);
+ return -1;
+ }
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+ lwip_link_select_cb(&API_SELECT_CB_VAR_REF(select_cb));
+
+ /* Increase select_waiting for each socket we are interested in.
+ Also, check for events again: there could have been events between
+ the last scan (without us on the list) and putting us on the list! */
+ nready = lwip_pollscan(fds, nfds, LWIP_POLLSCAN_INC_WAIT);
+
+ if (!nready) {
+ /* Still none ready, just wait to be woken */
+ if (timeout < 0) {
+ /* Wait forever */
+ msectimeout = 0;
+ } else {
+ /* timeout == 0 would have been handled earlier. */
+ LWIP_ASSERT("timeout > 0", timeout > 0);
+ msectimeout = timeout;
+ }
+ waitres = sys_arch_sem_wait(SELECT_SEM_PTR(API_SELECT_CB_VAR_REF(select_cb).sem), msectimeout);
+#if LWIP_NETCONN_SEM_PER_THREAD
+ waited = 1;
+#endif
+ }
+
+ /* Decrease select_waiting for each socket we are interested in,
+ and check which events occurred while we waited. */
+ nready = lwip_pollscan(fds, nfds, LWIP_POLLSCAN_DEC_WAIT);
+
+ lwip_unlink_select_cb(&API_SELECT_CB_VAR_REF(select_cb));
+
+#if LWIP_NETCONN_SEM_PER_THREAD
+ if (select_cb.sem_signalled && (!waited || (waitres == SYS_ARCH_TIMEOUT))) {
+ /* don't leave the thread-local semaphore signalled */
+ sys_arch_sem_wait(API_SELECT_CB_VAR_REF(select_cb).sem, 1);
+ }
+#else /* LWIP_NETCONN_SEM_PER_THREAD */
+ sys_sem_free(&API_SELECT_CB_VAR_REF(select_cb).sem);
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+ API_SELECT_CB_VAR_FREE(select_cb);
+
+ if (nready < 0) {
+ /* This happens when a socket got closed while waiting */
+ lwip_poll_dec_sockets_used(fds, nfds);
+ return -1;
+ }
+
+ if (waitres == SYS_ARCH_TIMEOUT) {
+ /* Timeout */
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_poll: timeout expired\n"));
+ goto return_success;
+ }
+ }
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_poll: nready=%d\n", nready));
+return_success:
+ lwip_poll_dec_sockets_used(fds, nfds);
+ set_errno(0);
+ return nready;
+}
+
+/**
+ * Check whether event_callback should wake up a thread waiting in
+ * lwip_poll.
+ */
+static int
+lwip_poll_should_wake(const struct lwip_select_cb *scb, int fd, int has_recvevent, int has_sendevent, int has_errevent)
+{
+ nfds_t fdi;
+ for (fdi = 0; fdi < scb->poll_nfds; fdi++) {
+ const struct pollfd *pollfd = &scb->poll_fds[fdi];
+ if (pollfd->fd == fd) {
+ /* Do not update pollfd->revents right here;
+ that would be a data race because lwip_pollscan
+ accesses revents without protecting. */
+ if (has_recvevent && (pollfd->events & POLLIN) != 0) {
+ return 1;
+ }
+ if (has_sendevent && (pollfd->events & POLLOUT) != 0) {
+ return 1;
+ }
+ if (has_errevent) {
+ /* POLLERR is output only. */
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+#endif /* LWIP_SOCKET_POLL */
+
+#if LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL
+/**
+ * Callback registered in the netconn layer for each socket-netconn.
+ * Processes recvevent (data available) and wakes up tasks waiting for select.
+ *
+ * @note for LWIP_TCPIP_CORE_LOCKING any caller of this function
+ * must have the core lock held when signaling the following events
+ * as they might cause select_list_cb to be checked:
+ * NETCONN_EVT_RCVPLUS
+ * NETCONN_EVT_SENDPLUS
+ * NETCONN_EVT_ERROR
+ * This requirement will be asserted in select_check_waiters()
+ */
+static void
+event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len)
+{
+ int s, check_waiters;
+ struct lwip_sock *sock;
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ LWIP_UNUSED_ARG(len);
+
+ /* Get socket */
+ if (conn) {
+ s = conn->socket;
+ if (s < 0) {
+ /* Data comes in right away after an accept, even though
+ * the server task might not have created a new socket yet.
+ * Just count down (or up) if that's the case and we
+ * will use the data later. Note that only receive events
+ * can happen before the new socket is set up. */
+ SYS_ARCH_PROTECT(lev);
+ if (conn->socket < 0) {
+ if (evt == NETCONN_EVT_RCVPLUS) {
+ /* conn->socket is -1 on initialization
+ lwip_accept adjusts sock->recvevent if conn->socket < -1 */
+ conn->socket--;
+ }
+ SYS_ARCH_UNPROTECT(lev);
+ return;
+ }
+ s = conn->socket;
+ SYS_ARCH_UNPROTECT(lev);
+ }
+
+ sock = get_socket(s);
+ if (!sock) {
+ return;
+ }
+ } else {
+ return;
+ }
+
+ check_waiters = 1;
+ SYS_ARCH_PROTECT(lev);
+ /* Set event as required */
+ switch (evt) {
+ case NETCONN_EVT_RCVPLUS:
+ sock->rcvevent++;
+ if (sock->rcvevent > 1) {
+ check_waiters = 0;
+ }
+ break;
+ case NETCONN_EVT_RCVMINUS:
+ sock->rcvevent--;
+ check_waiters = 0;
+ break;
+ case NETCONN_EVT_SENDPLUS:
+ if (sock->sendevent) {
+ check_waiters = 0;
+ }
+ sock->sendevent = 1;
+ break;
+ case NETCONN_EVT_SENDMINUS:
+ sock->sendevent = 0;
+ check_waiters = 0;
+ break;
+ case NETCONN_EVT_ERROR:
+ sock->errevent = 1;
+ break;
+ default:
+ LWIP_ASSERT("unknown event", 0);
+ break;
+ }
+
+ if (sock->select_waiting && check_waiters) {
+ /* Save which events are active */
+ int has_recvevent, has_sendevent, has_errevent;
+ has_recvevent = sock->rcvevent > 0;
+ has_sendevent = sock->sendevent != 0;
+ has_errevent = sock->errevent != 0;
+ SYS_ARCH_UNPROTECT(lev);
+ /* Check any select calls waiting on this socket */
+ select_check_waiters(s, has_recvevent, has_sendevent, has_errevent);
+ } else {
+ SYS_ARCH_UNPROTECT(lev);
+ }
+ done_socket(sock);
+}
+
+/**
+ * Check if any select waiters are waiting on this socket and its events
+ *
+ * @note on synchronization of select_cb_list:
+ * LWIP_TCPIP_CORE_LOCKING: the select_cb_list must only be accessed while holding
+ * the core lock. We do a single pass through the list and signal any waiters.
+ * Core lock should already be held when calling here!!!!
+
+ * !LWIP_TCPIP_CORE_LOCKING: we use SYS_ARCH_PROTECT but unlock on each iteration
+ * of the loop, thus creating a possibility where a thread could modify the
+ * select_cb_list during our UNPROTECT/PROTECT. We use a generational counter to
+ * detect this change and restart the list walk. The list is expected to be small
+ */
+static void select_check_waiters(int s, int has_recvevent, int has_sendevent, int has_errevent)
+{
+ struct lwip_select_cb *scb;
+#if !LWIP_TCPIP_CORE_LOCKING
+ int last_select_cb_ctr;
+ SYS_ARCH_DECL_PROTECT(lev);
+#endif /* !LWIP_TCPIP_CORE_LOCKING */
+
+ LWIP_ASSERT_CORE_LOCKED();
+
+#if !LWIP_TCPIP_CORE_LOCKING
+ SYS_ARCH_PROTECT(lev);
+again:
+ /* remember the state of select_cb_list to detect changes */
+ last_select_cb_ctr = select_cb_ctr;
+#endif /* !LWIP_TCPIP_CORE_LOCKING */
+ for (scb = select_cb_list; scb != NULL; scb = scb->next) {
+ if (scb->sem_signalled == 0) {
+ /* semaphore not signalled yet */
+ int do_signal = 0;
+#if LWIP_SOCKET_POLL
+ if (scb->poll_fds != NULL) {
+ do_signal = lwip_poll_should_wake(scb, s, has_recvevent, has_sendevent, has_errevent);
+ }
+#endif /* LWIP_SOCKET_POLL */
+#if LWIP_SOCKET_SELECT && LWIP_SOCKET_POLL
+ else
+#endif /* LWIP_SOCKET_SELECT && LWIP_SOCKET_POLL */
+#if LWIP_SOCKET_SELECT
+ {
+ /* Test this select call for our socket */
+ if (has_recvevent) {
+ if (scb->readset && FD_ISSET(s, scb->readset)) {
+ do_signal = 1;
+ }
+ }
+ if (has_sendevent) {
+ if (!do_signal && scb->writeset && FD_ISSET(s, scb->writeset)) {
+ do_signal = 1;
+ }
+ }
+ if (has_errevent) {
+ if (!do_signal && scb->exceptset && FD_ISSET(s, scb->exceptset)) {
+ do_signal = 1;
+ }
+ }
+ }
+#endif /* LWIP_SOCKET_SELECT */
+ if (do_signal) {
+ scb->sem_signalled = 1;
+ /* For !LWIP_TCPIP_CORE_LOCKING, we don't call SYS_ARCH_UNPROTECT() before signaling
+ the semaphore, as this might lead to the select thread taking itself off the list,
+ invalidating the semaphore. */
+ sys_sem_signal(SELECT_SEM_PTR(scb->sem));
+ }
+ }
+#if LWIP_TCPIP_CORE_LOCKING
+ }
+#else
+ /* unlock interrupts with each step */
+ SYS_ARCH_UNPROTECT(lev);
+ /* this makes sure interrupt protection time is short */
+ SYS_ARCH_PROTECT(lev);
+ if (last_select_cb_ctr != select_cb_ctr) {
+ /* someone has changed select_cb_list, restart at the beginning */
+ goto again;
+ }
+ /* remember the state of select_cb_list to detect changes */
+ last_select_cb_ctr = select_cb_ctr;
+ }
+ SYS_ARCH_UNPROTECT(lev);
+#endif
+}
+#endif /* LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL */
+
+/**
+ * Close one end of a full-duplex connection.
+ */
+int
+lwip_shutdown(int s, int how)
+{
+ struct lwip_sock *sock;
+ err_t err;
+ u8_t shut_rx = 0, shut_tx = 0;
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_shutdown(%d, how=%d)\n", s, how));
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ if (sock->conn != NULL) {
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
+ sock_set_errno(sock, EOPNOTSUPP);
+ done_socket(sock);
+ return -1;
+ }
+ } else {
+ sock_set_errno(sock, ENOTCONN);
+ done_socket(sock);
+ return -1;
+ }
+
+ if (how == SHUT_RD) {
+ shut_rx = 1;
+ } else if (how == SHUT_WR) {
+ shut_tx = 1;
+ } else if (how == SHUT_RDWR) {
+ shut_rx = 1;
+ shut_tx = 1;
+ } else {
+ sock_set_errno(sock, EINVAL);
+ done_socket(sock);
+ return -1;
+ }
+ err = netconn_shutdown(sock->conn, shut_rx, shut_tx);
+
+ sock_set_errno(sock, err_to_errno(err));
+ done_socket(sock);
+ return (err == ERR_OK ? 0 : -1);
+}
+
+static int
+lwip_getaddrname(int s, struct sockaddr *name, socklen_t *namelen, u8_t local)
+{
+ struct lwip_sock *sock;
+ union sockaddr_aligned saddr;
+ ip_addr_t naddr;
+ u16_t port;
+ err_t err;
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ /* get the IP address and port */
+ err = netconn_getaddr(sock->conn, &naddr, &port, local);
+ if (err != ERR_OK) {
+ sock_set_errno(sock, err_to_errno(err));
+ done_socket(sock);
+ return -1;
+ }
+
+#if LWIP_IPV4 && LWIP_IPV6
+ /* Dual-stack: Map IPv4 addresses to IPv4 mapped IPv6 */
+ if (NETCONNTYPE_ISIPV6(netconn_type(sock->conn)) &&
+ IP_IS_V4_VAL(naddr)) {
+ ip4_2_ipv4_mapped_ipv6(ip_2_ip6(&naddr), ip_2_ip4(&naddr));
+ IP_SET_TYPE_VAL(naddr, IPADDR_TYPE_V6);
+ }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+ IPADDR_PORT_TO_SOCKADDR(&saddr, &naddr, port);
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getaddrname(%d, addr=", s));
+ ip_addr_debug_print_val(SOCKETS_DEBUG, naddr);
+ LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", port));
+
+ if (*namelen > saddr.sa.sa_len) {
+ *namelen = saddr.sa.sa_len;
+ }
+ MEMCPY(name, &saddr, *namelen);
+
+ sock_set_errno(sock, 0);
+ done_socket(sock);
+ return 0;
+}
+
+int
+lwip_getpeername(int s, struct sockaddr *name, socklen_t *namelen)
+{
+ return lwip_getaddrname(s, name, namelen, 0);
+}
+
+int
+lwip_getsockname(int s, struct sockaddr *name, socklen_t *namelen)
+{
+ return lwip_getaddrname(s, name, namelen, 1);
+}
+
+int
+lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen)
+{
+ int err;
+ struct lwip_sock *sock = get_socket(s);
+#if !LWIP_TCPIP_CORE_LOCKING
+ err_t cberr;
+ LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(data);
+#endif /* !LWIP_TCPIP_CORE_LOCKING */
+
+ if (!sock) {
+ return -1;
+ }
+
+ if ((NULL == optval) || (NULL == optlen)) {
+ sock_set_errno(sock, EFAULT);
+ done_socket(sock);
+ return -1;
+ }
+
+#if LWIP_TCPIP_CORE_LOCKING
+ /* core-locking can just call the -impl function */
+ LOCK_TCPIP_CORE();
+ err = lwip_getsockopt_impl(s, level, optname, optval, optlen);
+ UNLOCK_TCPIP_CORE();
+
+#else /* LWIP_TCPIP_CORE_LOCKING */
+
+#if LWIP_MPU_COMPATIBLE
+ /* MPU_COMPATIBLE copies the optval data, so check for max size here */
+ if (*optlen > LWIP_SETGETSOCKOPT_MAXOPTLEN) {
+ sock_set_errno(sock, ENOBUFS);
+ done_socket(sock);
+ return -1;
+ }
+#endif /* LWIP_MPU_COMPATIBLE */
+
+ LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(data, sock);
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).s = s;
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).level = level;
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optname = optname;
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen = *optlen;
+#if !LWIP_MPU_COMPATIBLE
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval.p = optval;
+#endif /* !LWIP_MPU_COMPATIBLE */
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err = 0;
+#if LWIP_NETCONN_SEM_PER_THREAD
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = LWIP_NETCONN_THREAD_SEM_GET();
+#else
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = &sock->conn->op_completed;
+#endif
+ cberr = tcpip_callback(lwip_getsockopt_callback, &LWIP_SETGETSOCKOPT_DATA_VAR_REF(data));
+ if (cberr != ERR_OK) {
+ LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data);
+ sock_set_errno(sock, err_to_errno(cberr));
+ done_socket(sock);
+ return -1;
+ }
+ sys_arch_sem_wait((sys_sem_t *)(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem), 0);
+
+ /* write back optlen and optval */
+ *optlen = LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen;
+#if LWIP_MPU_COMPATIBLE
+ MEMCPY(optval, LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval,
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen);
+#endif /* LWIP_MPU_COMPATIBLE */
+
+ /* maybe lwip_getsockopt_internal has changed err */
+ err = LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err;
+ LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+ sock_set_errno(sock, err);
+ done_socket(sock);
+ return err ? -1 : 0;
+}
+
+#if !LWIP_TCPIP_CORE_LOCKING
+/** lwip_getsockopt_callback: only used without CORE_LOCKING
+ * to get into the tcpip_thread
+ */
+static void
+lwip_getsockopt_callback(void *arg)
+{
+ struct lwip_setgetsockopt_data *data;
+ LWIP_ASSERT("arg != NULL", arg != NULL);
+ data = (struct lwip_setgetsockopt_data *)arg;
+
+ data->err = lwip_getsockopt_impl(data->s, data->level, data->optname,
+#if LWIP_MPU_COMPATIBLE
+ data->optval,
+#else /* LWIP_MPU_COMPATIBLE */
+ data->optval.p,
+#endif /* LWIP_MPU_COMPATIBLE */
+ &data->optlen);
+
+ sys_sem_signal((sys_sem_t *)(data->completed_sem));
+}
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+static int
+lwip_sockopt_to_ipopt(int optname)
+{
+ /* Map SO_* values to our internal SOF_* values
+ * We should not rely on #defines in socket.h
+ * being in sync with ip.h.
+ */
+ switch (optname) {
+ case SO_BROADCAST:
+ return SOF_BROADCAST;
+ case SO_KEEPALIVE:
+ return SOF_KEEPALIVE;
+ case SO_REUSEADDR:
+ return SOF_REUSEADDR;
+ default:
+ LWIP_ASSERT("Unknown socket option", 0);
+ return 0;
+ }
+}
+
+/** lwip_getsockopt_impl: the actual implementation of getsockopt:
+ * same argument as lwip_getsockopt, either called directly or through callback
+ */
+static int
+lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *optlen)
+{
+ int err = 0;
+ struct lwip_sock *sock = tryget_socket(s);
+ if (!sock) {
+ return EBADF;
+ }
+
+#ifdef LWIP_HOOK_SOCKETS_GETSOCKOPT
+ if (LWIP_HOOK_SOCKETS_GETSOCKOPT(s, sock, level, optname, optval, optlen, &err)) {
+ return err;
+ }
+#endif
+
+ switch (level) {
+
+ /* Level: SOL_SOCKET */
+ case SOL_SOCKET:
+ switch (optname) {
+
+#if LWIP_TCP
+ case SO_ACCEPTCONN:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
+ if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_TCP) {
+ done_socket(sock);
+ return ENOPROTOOPT;
+ }
+ if ((sock->conn->pcb.tcp != NULL) && (sock->conn->pcb.tcp->state == LISTEN)) {
+ *(int *)optval = 1;
+ } else {
+ *(int *)optval = 0;
+ }
+ break;
+#endif /* LWIP_TCP */
+
+ /* The option flags */
+ case SO_BROADCAST:
+ case SO_KEEPALIVE:
+#if SO_REUSE
+ case SO_REUSEADDR:
+#endif /* SO_REUSE */
+ if ((optname == SO_BROADCAST) &&
+ (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP)) {
+ done_socket(sock);
+ return ENOPROTOOPT;
+ }
+
+ optname = lwip_sockopt_to_ipopt(optname);
+
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
+ *(int *)optval = ip_get_option(sock->conn->pcb.ip, optname);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, optname=0x%x, ..) = %s\n",
+ s, optname, (*(int *)optval ? "on" : "off")));
+ break;
+
+ case SO_TYPE:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int);
+ switch (NETCONNTYPE_GROUP(netconn_type(sock->conn))) {
+ case NETCONN_RAW:
+ *(int *)optval = SOCK_RAW;
+ break;
+ case NETCONN_TCP:
+ *(int *)optval = SOCK_STREAM;
+ break;
+ case NETCONN_UDP:
+ *(int *)optval = SOCK_DGRAM;
+ break;
+ default: /* unrecognized socket type */
+ *(int *)optval = netconn_type(sock->conn);
+ LWIP_DEBUGF(SOCKETS_DEBUG,
+ ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE): unrecognized socket type %d\n",
+ s, *(int *)optval));
+ } /* switch (netconn_type(sock->conn)) */
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE) = %d\n",
+ s, *(int *)optval));
+ break;
+
+ case SO_ERROR:
+ LWIP_SOCKOPT_CHECK_OPTLEN(sock, *optlen, int);
+ *(int *)optval = err_to_errno(netconn_err(sock->conn));
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_ERROR) = %d\n",
+ s, *(int *)optval));
+ break;
+
+#if LWIP_SO_SNDTIMEO
+ case SO_SNDTIMEO:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
+ LWIP_SO_SNDRCVTIMEO_SET(optval, netconn_get_sendtimeout(sock->conn));
+ break;
+#endif /* LWIP_SO_SNDTIMEO */
+#if LWIP_SO_RCVTIMEO
+ case SO_RCVTIMEO:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
+ LWIP_SO_SNDRCVTIMEO_SET(optval, netconn_get_recvtimeout(sock->conn));
+ break;
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+ case SO_RCVBUF:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int);
+ *(int *)optval = netconn_get_recvbufsize(sock->conn);
+ break;
+#endif /* LWIP_SO_RCVBUF */
+#if LWIP_SO_LINGER
+ case SO_LINGER: {
+ s16_t conn_linger;
+ struct linger *linger = (struct linger *)optval;
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, struct linger);
+ conn_linger = sock->conn->linger;
+ if (conn_linger >= 0) {
+ linger->l_onoff = 1;
+ linger->l_linger = (int)conn_linger;
+ } else {
+ linger->l_onoff = 0;
+ linger->l_linger = 0;
+ }
+ }
+ break;
+#endif /* LWIP_SO_LINGER */
+#if LWIP_UDP
+ case SO_NO_CHECK:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_UDP);
+#if LWIP_UDPLITE
+ if (udp_is_flag_set(sock->conn->pcb.udp, UDP_FLAGS_UDPLITE)) {
+ /* this flag is only available for UDP, not for UDP lite */
+ done_socket(sock);
+ return EAFNOSUPPORT;
+ }
+#endif /* LWIP_UDPLITE */
+ *(int *)optval = udp_is_flag_set(sock->conn->pcb.udp, UDP_FLAGS_NOCHKSUM) ? 1 : 0;
+ break;
+#endif /* LWIP_UDP*/
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ break;
+ } /* switch (optname) */
+ break;
+
+ /* Level: IPPROTO_IP */
+ case IPPROTO_IP:
+ switch (optname) {
+ case IP_TTL:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
+ *(int *)optval = sock->conn->pcb.ip->ttl;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TTL) = %d\n",
+ s, *(int *)optval));
+ break;
+ case IP_TOS:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
+ *(int *)optval = sock->conn->pcb.ip->tos;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TOS) = %d\n",
+ s, *(int *)optval));
+ break;
+#if LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP
+ case IP_MULTICAST_TTL:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t);
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
+ done_socket(sock);
+ return ENOPROTOOPT;
+ }
+ *(u8_t *)optval = udp_get_multicast_ttl(sock->conn->pcb.udp);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_TTL) = %d\n",
+ s, *(int *)optval));
+ break;
+ case IP_MULTICAST_IF:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, struct in_addr);
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
+ done_socket(sock);
+ return ENOPROTOOPT;
+ }
+ inet_addr_from_ip4addr((struct in_addr *)optval, udp_get_multicast_netif_addr(sock->conn->pcb.udp));
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_IF) = 0x%"X32_F"\n",
+ s, *(u32_t *)optval));
+ break;
+ case IP_MULTICAST_LOOP:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t);
+ if ((sock->conn->pcb.udp->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) {
+ *(u8_t *)optval = 1;
+ } else {
+ *(u8_t *)optval = 0;
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_LOOP) = %d\n",
+ s, *(int *)optval));
+ break;
+#endif /* LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP */
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ break;
+ } /* switch (optname) */
+ break;
+
+#if LWIP_TCP
+ /* Level: IPPROTO_TCP */
+ case IPPROTO_TCP:
+ /* Special case: all IPPROTO_TCP option take an int */
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_TCP);
+ if (sock->conn->pcb.tcp->state == LISTEN) {
+ done_socket(sock);
+ return EINVAL;
+ }
+ switch (optname) {
+ case TCP_NODELAY:
+ *(int *)optval = tcp_nagle_disabled(sock->conn->pcb.tcp);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_NODELAY) = %s\n",
+ s, (*(int *)optval) ? "on" : "off") );
+ break;
+ case TCP_KEEPALIVE:
+ *(int *)optval = (int)sock->conn->pcb.tcp->keep_idle;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPALIVE) = %d\n",
+ s, *(int *)optval));
+ break;
+
+#if LWIP_TCP_KEEPALIVE
+ case TCP_KEEPIDLE:
+ *(int *)optval = (int)(sock->conn->pcb.tcp->keep_idle / 1000);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPIDLE) = %d\n",
+ s, *(int *)optval));
+ break;
+ case TCP_KEEPINTVL:
+ *(int *)optval = (int)(sock->conn->pcb.tcp->keep_intvl / 1000);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPINTVL) = %d\n",
+ s, *(int *)optval));
+ break;
+ case TCP_KEEPCNT:
+ *(int *)optval = (int)sock->conn->pcb.tcp->keep_cnt;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPCNT) = %d\n",
+ s, *(int *)optval));
+ break;
+#endif /* LWIP_TCP_KEEPALIVE */
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ break;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_TCP */
+
+#if LWIP_IPV6
+ /* Level: IPPROTO_IPV6 */
+ case IPPROTO_IPV6:
+ switch (optname) {
+ case IPV6_V6ONLY:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int);
+ *(int *)optval = (netconn_get_ipv6only(sock->conn) ? 1 : 0);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY) = %d\n",
+ s, *(int *)optval));
+ break;
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ break;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_IPV6 */
+
+#if LWIP_UDP && LWIP_UDPLITE
+ /* Level: IPPROTO_UDPLITE */
+ case IPPROTO_UDPLITE:
+ /* Special case: all IPPROTO_UDPLITE option take an int */
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
+ /* If this is no UDP lite socket, ignore any options. */
+ if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn))) {
+ done_socket(sock);
+ return ENOPROTOOPT;
+ }
+ switch (optname) {
+ case UDPLITE_SEND_CSCOV:
+ *(int *)optval = sock->conn->pcb.udp->chksum_len_tx;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) = %d\n",
+ s, (*(int *)optval)) );
+ break;
+ case UDPLITE_RECV_CSCOV:
+ *(int *)optval = sock->conn->pcb.udp->chksum_len_rx;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) = %d\n",
+ s, (*(int *)optval)) );
+ break;
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ break;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_UDP */
+ /* Level: IPPROTO_RAW */
+ case IPPROTO_RAW:
+ switch (optname) {
+#if LWIP_IPV6 && LWIP_RAW
+ case IPV6_CHECKSUM:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_RAW);
+ if (sock->conn->pcb.raw->chksum_reqd == 0) {
+ *(int *)optval = -1;
+ } else {
+ *(int *)optval = sock->conn->pcb.raw->chksum_offset;
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_RAW, IPV6_CHECKSUM) = %d\n",
+ s, (*(int *)optval)) );
+ break;
+#endif /* LWIP_IPV6 && LWIP_RAW */
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_RAW, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ break;
+ } /* switch (optname) */
+ break;
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n",
+ s, level, optname));
+ err = ENOPROTOOPT;
+ break;
+ } /* switch (level) */
+
+ done_socket(sock);
+ return err;
+}
+
+int
+lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen)
+{
+ int err = 0;
+ struct lwip_sock *sock = get_socket(s);
+#if !LWIP_TCPIP_CORE_LOCKING
+ err_t cberr;
+ LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(data);
+#endif /* !LWIP_TCPIP_CORE_LOCKING */
+
+ if (!sock) {
+ return -1;
+ }
+
+ if (NULL == optval) {
+ sock_set_errno(sock, EFAULT);
+ done_socket(sock);
+ return -1;
+ }
+
+#if LWIP_TCPIP_CORE_LOCKING
+ /* core-locking can just call the -impl function */
+ LOCK_TCPIP_CORE();
+ err = lwip_setsockopt_impl(s, level, optname, optval, optlen);
+ UNLOCK_TCPIP_CORE();
+
+#else /* LWIP_TCPIP_CORE_LOCKING */
+
+#if LWIP_MPU_COMPATIBLE
+ /* MPU_COMPATIBLE copies the optval data, so check for max size here */
+ if (optlen > LWIP_SETGETSOCKOPT_MAXOPTLEN) {
+ sock_set_errno(sock, ENOBUFS);
+ done_socket(sock);
+ return -1;
+ }
+#endif /* LWIP_MPU_COMPATIBLE */
+
+ LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(data, sock);
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).s = s;
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).level = level;
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optname = optname;
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen = optlen;
+#if LWIP_MPU_COMPATIBLE
+ MEMCPY(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval, optval, optlen);
+#else /* LWIP_MPU_COMPATIBLE */
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval.pc = (const void *)optval;
+#endif /* LWIP_MPU_COMPATIBLE */
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err = 0;
+#if LWIP_NETCONN_SEM_PER_THREAD
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = LWIP_NETCONN_THREAD_SEM_GET();
+#else
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = &sock->conn->op_completed;
+#endif
+ cberr = tcpip_callback(lwip_setsockopt_callback, &LWIP_SETGETSOCKOPT_DATA_VAR_REF(data));
+ if (cberr != ERR_OK) {
+ LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data);
+ sock_set_errno(sock, err_to_errno(cberr));
+ done_socket(sock);
+ return -1;
+ }
+ sys_arch_sem_wait((sys_sem_t *)(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem), 0);
+
+ /* maybe lwip_getsockopt_internal has changed err */
+ err = LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err;
+ LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+ sock_set_errno(sock, err);
+ done_socket(sock);
+ return err ? -1 : 0;
+}
+
+#if !LWIP_TCPIP_CORE_LOCKING
+/** lwip_setsockopt_callback: only used without CORE_LOCKING
+ * to get into the tcpip_thread
+ */
+static void
+lwip_setsockopt_callback(void *arg)
+{
+ struct lwip_setgetsockopt_data *data;
+ LWIP_ASSERT("arg != NULL", arg != NULL);
+ data = (struct lwip_setgetsockopt_data *)arg;
+
+ data->err = lwip_setsockopt_impl(data->s, data->level, data->optname,
+#if LWIP_MPU_COMPATIBLE
+ data->optval,
+#else /* LWIP_MPU_COMPATIBLE */
+ data->optval.pc,
+#endif /* LWIP_MPU_COMPATIBLE */
+ data->optlen);
+
+ sys_sem_signal((sys_sem_t *)(data->completed_sem));
+}
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+/** lwip_setsockopt_impl: the actual implementation of setsockopt:
+ * same argument as lwip_setsockopt, either called directly or through callback
+ */
+static int
+lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_t optlen)
+{
+ int err = 0;
+ struct lwip_sock *sock = tryget_socket(s);
+ if (!sock) {
+ return EBADF;
+ }
+
+#ifdef LWIP_HOOK_SOCKETS_SETSOCKOPT
+ if (LWIP_HOOK_SOCKETS_SETSOCKOPT(s, sock, level, optname, optval, optlen, &err)) {
+ return err;
+ }
+#endif
+
+ switch (level) {
+
+ /* Level: SOL_SOCKET */
+ case SOL_SOCKET:
+ switch (optname) {
+
+ /* SO_ACCEPTCONN is get-only */
+
+ /* The option flags */
+ case SO_BROADCAST:
+ case SO_KEEPALIVE:
+#if SO_REUSE
+ case SO_REUSEADDR:
+#endif /* SO_REUSE */
+ if ((optname == SO_BROADCAST) &&
+ (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP)) {
+ done_socket(sock);
+ return ENOPROTOOPT;
+ }
+
+ optname = lwip_sockopt_to_ipopt(optname);
+
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
+ if (*(const int *)optval) {
+ ip_set_option(sock->conn->pcb.ip, optname);
+ } else {
+ ip_reset_option(sock->conn->pcb.ip, optname);
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, optname=0x%x, ..) -> %s\n",
+ s, optname, (*(const int *)optval ? "on" : "off")));
+ break;
+
+ /* SO_TYPE is get-only */
+ /* SO_ERROR is get-only */
+
+#if LWIP_SO_SNDTIMEO
+ case SO_SNDTIMEO: {
+ long ms_long;
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
+ ms_long = LWIP_SO_SNDRCVTIMEO_GET_MS(optval);
+ if (ms_long < 0) {
+ done_socket(sock);
+ return EINVAL;
+ }
+ netconn_set_sendtimeout(sock->conn, ms_long);
+ break;
+ }
+#endif /* LWIP_SO_SNDTIMEO */
+#if LWIP_SO_RCVTIMEO
+ case SO_RCVTIMEO: {
+ long ms_long;
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
+ ms_long = LWIP_SO_SNDRCVTIMEO_GET_MS(optval);
+ if (ms_long < 0) {
+ done_socket(sock);
+ return EINVAL;
+ }
+ netconn_set_recvtimeout(sock->conn, (u32_t)ms_long);
+ break;
+ }
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+ case SO_RCVBUF:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, int);
+ netconn_set_recvbufsize(sock->conn, *(const int *)optval);
+ break;
+#endif /* LWIP_SO_RCVBUF */
+#if LWIP_SO_LINGER
+ case SO_LINGER: {
+ const struct linger *linger = (const struct linger *)optval;
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, struct linger);
+ if (linger->l_onoff) {
+ int lingersec = linger->l_linger;
+ if (lingersec < 0) {
+ done_socket(sock);
+ return EINVAL;
+ }
+ if (lingersec > 0xFFFF) {
+ lingersec = 0xFFFF;
+ }
+ sock->conn->linger = (s16_t)lingersec;
+ } else {
+ sock->conn->linger = -1;
+ }
+ }
+ break;
+#endif /* LWIP_SO_LINGER */
+#if LWIP_UDP
+ case SO_NO_CHECK:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_UDP);
+#if LWIP_UDPLITE
+ if (udp_is_flag_set(sock->conn->pcb.udp, UDP_FLAGS_UDPLITE)) {
+ /* this flag is only available for UDP, not for UDP lite */
+ done_socket(sock);
+ return EAFNOSUPPORT;
+ }
+#endif /* LWIP_UDPLITE */
+ if (*(const int *)optval) {
+ udp_set_flags(sock->conn->pcb.udp, UDP_FLAGS_NOCHKSUM);
+ } else {
+ udp_clear_flags(sock->conn->pcb.udp, UDP_FLAGS_NOCHKSUM);
+ }
+ break;
+#endif /* LWIP_UDP */
+ case SO_BINDTODEVICE: {
+ const struct ifreq *iface;
+ struct netif *n = NULL;
+
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, struct ifreq);
+
+ iface = (const struct ifreq *)optval;
+ if (iface->ifr_name[0] != 0) {
+ n = netif_find(iface->ifr_name);
+ if (n == NULL) {
+ done_socket(sock);
+ return ENODEV;
+ }
+ }
+
+ switch (NETCONNTYPE_GROUP(netconn_type(sock->conn))) {
+#if LWIP_TCP
+ case NETCONN_TCP:
+ tcp_bind_netif(sock->conn->pcb.tcp, n);
+ break;
+#endif
+#if LWIP_UDP
+ case NETCONN_UDP:
+ udp_bind_netif(sock->conn->pcb.udp, n);
+ break;
+#endif
+#if LWIP_RAW
+ case NETCONN_RAW:
+ raw_bind_netif(sock->conn->pcb.raw, n);
+ break;
+#endif
+ default:
+ LWIP_ASSERT("Unhandled netconn type in SO_BINDTODEVICE", 0);
+ break;
+ }
+ }
+ break;
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ break;
+ } /* switch (optname) */
+ break;
+
+ /* Level: IPPROTO_IP */
+ case IPPROTO_IP:
+ switch (optname) {
+ case IP_TTL:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
+ sock->conn->pcb.ip->ttl = (u8_t)(*(const int *)optval);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TTL, ..) -> %d\n",
+ s, sock->conn->pcb.ip->ttl));
+ break;
+ case IP_TOS:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
+ sock->conn->pcb.ip->tos = (u8_t)(*(const int *)optval);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TOS, ..)-> %d\n",
+ s, sock->conn->pcb.ip->tos));
+ break;
+#if LWIP_NETBUF_RECVINFO
+ case IP_PKTINFO:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_UDP);
+ if (*(const int *)optval) {
+ sock->conn->flags |= NETCONN_FLAG_PKTINFO;
+ } else {
+ sock->conn->flags &= ~NETCONN_FLAG_PKTINFO;
+ }
+ break;
+#endif /* LWIP_NETBUF_RECVINFO */
+#if LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP
+ case IP_MULTICAST_TTL:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP);
+ udp_set_multicast_ttl(sock->conn->pcb.udp, (u8_t)(*(const u8_t *)optval));
+ break;
+ case IP_MULTICAST_IF: {
+ ip4_addr_t if_addr;
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct in_addr, NETCONN_UDP);
+ inet_addr_to_ip4addr(&if_addr, (const struct in_addr *)optval);
+ udp_set_multicast_netif_addr(sock->conn->pcb.udp, &if_addr);
+ }
+ break;
+ case IP_MULTICAST_LOOP:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP);
+ if (*(const u8_t *)optval) {
+ udp_set_flags(sock->conn->pcb.udp, UDP_FLAGS_MULTICAST_LOOP);
+ } else {
+ udp_clear_flags(sock->conn->pcb.udp, UDP_FLAGS_MULTICAST_LOOP);
+ }
+ break;
+#endif /* LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP */
+#if LWIP_IGMP
+ case IP_ADD_MEMBERSHIP:
+ case IP_DROP_MEMBERSHIP: {
+ /* If this is a TCP or a RAW socket, ignore these options. */
+ err_t igmp_err;
+ const struct ip_mreq *imr = (const struct ip_mreq *)optval;
+ ip4_addr_t if_addr;
+ ip4_addr_t multi_addr;
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct ip_mreq, NETCONN_UDP);
+ inet_addr_to_ip4addr(&if_addr, &imr->imr_interface);
+ inet_addr_to_ip4addr(&multi_addr, &imr->imr_multiaddr);
+ if (optname == IP_ADD_MEMBERSHIP) {
+ if (!lwip_socket_register_membership(s, &if_addr, &multi_addr)) {
+ /* cannot track membership (out of memory) */
+ err = ENOMEM;
+ igmp_err = ERR_OK;
+ } else {
+ igmp_err = igmp_joingroup(&if_addr, &multi_addr);
+ }
+ } else {
+ igmp_err = igmp_leavegroup(&if_addr, &multi_addr);
+ lwip_socket_unregister_membership(s, &if_addr, &multi_addr);
+ }
+ if (igmp_err != ERR_OK) {
+ err = EADDRNOTAVAIL;
+ }
+ }
+ break;
+#endif /* LWIP_IGMP */
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ break;
+ } /* switch (optname) */
+ break;
+
+#if LWIP_TCP
+ /* Level: IPPROTO_TCP */
+ case IPPROTO_TCP:
+ /* Special case: all IPPROTO_TCP option take an int */
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_TCP);
+ if (sock->conn->pcb.tcp->state == LISTEN) {
+ done_socket(sock);
+ return EINVAL;
+ }
+ switch (optname) {
+ case TCP_NODELAY:
+ if (*(const int *)optval) {
+ tcp_nagle_disable(sock->conn->pcb.tcp);
+ } else {
+ tcp_nagle_enable(sock->conn->pcb.tcp);
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_NODELAY) -> %s\n",
+ s, (*(const int *)optval) ? "on" : "off") );
+ break;
+ case TCP_KEEPALIVE:
+ sock->conn->pcb.tcp->keep_idle = (u32_t)(*(const int *)optval);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPALIVE) -> %"U32_F"\n",
+ s, sock->conn->pcb.tcp->keep_idle));
+ break;
+
+#if LWIP_TCP_KEEPALIVE
+ case TCP_KEEPIDLE:
+ sock->conn->pcb.tcp->keep_idle = 1000 * (u32_t)(*(const int *)optval);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPIDLE) -> %"U32_F"\n",
+ s, sock->conn->pcb.tcp->keep_idle));
+ break;
+ case TCP_KEEPINTVL:
+ sock->conn->pcb.tcp->keep_intvl = 1000 * (u32_t)(*(const int *)optval);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPINTVL) -> %"U32_F"\n",
+ s, sock->conn->pcb.tcp->keep_intvl));
+ break;
+ case TCP_KEEPCNT:
+ sock->conn->pcb.tcp->keep_cnt = (u32_t)(*(const int *)optval);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPCNT) -> %"U32_F"\n",
+ s, sock->conn->pcb.tcp->keep_cnt));
+ break;
+#endif /* LWIP_TCP_KEEPALIVE */
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ break;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_TCP*/
+
+#if LWIP_IPV6
+ /* Level: IPPROTO_IPV6 */
+ case IPPROTO_IPV6:
+ switch (optname) {
+ case IPV6_V6ONLY:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
+ if (*(const int *)optval) {
+ netconn_set_ipv6only(sock->conn, 1);
+ } else {
+ netconn_set_ipv6only(sock->conn, 0);
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY, ..) -> %d\n",
+ s, (netconn_get_ipv6only(sock->conn) ? 1 : 0)));
+ break;
+#if LWIP_IPV6_MLD
+ case IPV6_JOIN_GROUP:
+ case IPV6_LEAVE_GROUP: {
+ /* If this is a TCP or a RAW socket, ignore these options. */
+ err_t mld6_err;
+ struct netif *netif;
+ ip6_addr_t multi_addr;
+ const struct ipv6_mreq *imr = (const struct ipv6_mreq *)optval;
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct ipv6_mreq, NETCONN_UDP);
+ inet6_addr_to_ip6addr(&multi_addr, &imr->ipv6mr_multiaddr);
+ LWIP_ASSERT("Invalid netif index", imr->ipv6mr_interface <= 0xFFu);
+ netif = netif_get_by_index((u8_t)imr->ipv6mr_interface);
+ if (netif == NULL) {
+ err = EADDRNOTAVAIL;
+ break;
+ }
+
+ if (optname == IPV6_JOIN_GROUP) {
+ if (!lwip_socket_register_mld6_membership(s, imr->ipv6mr_interface, &multi_addr)) {
+ /* cannot track membership (out of memory) */
+ err = ENOMEM;
+ mld6_err = ERR_OK;
+ } else {
+ mld6_err = mld6_joingroup_netif(netif, &multi_addr);
+ }
+ } else {
+ mld6_err = mld6_leavegroup_netif(netif, &multi_addr);
+ lwip_socket_unregister_mld6_membership(s, imr->ipv6mr_interface, &multi_addr);
+ }
+ if (mld6_err != ERR_OK) {
+ err = EADDRNOTAVAIL;
+ }
+ }
+ break;
+#endif /* LWIP_IPV6_MLD */
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ break;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_IPV6 */
+
+#if LWIP_UDP && LWIP_UDPLITE
+ /* Level: IPPROTO_UDPLITE */
+ case IPPROTO_UDPLITE:
+ /* Special case: all IPPROTO_UDPLITE option take an int */
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
+ /* If this is no UDP lite socket, ignore any options. */
+ if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn))) {
+ done_socket(sock);
+ return ENOPROTOOPT;
+ }
+ switch (optname) {
+ case UDPLITE_SEND_CSCOV:
+ if ((*(const int *)optval != 0) && ((*(const int *)optval < 8) || (*(const int *)optval > 0xffff))) {
+ /* don't allow illegal values! */
+ sock->conn->pcb.udp->chksum_len_tx = 8;
+ } else {
+ sock->conn->pcb.udp->chksum_len_tx = (u16_t) * (const int *)optval;
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) -> %d\n",
+ s, (*(const int *)optval)) );
+ break;
+ case UDPLITE_RECV_CSCOV:
+ if ((*(const int *)optval != 0) && ((*(const int *)optval < 8) || (*(const int *)optval > 0xffff))) {
+ /* don't allow illegal values! */
+ sock->conn->pcb.udp->chksum_len_rx = 8;
+ } else {
+ sock->conn->pcb.udp->chksum_len_rx = (u16_t) * (const int *)optval;
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) -> %d\n",
+ s, (*(const int *)optval)) );
+ break;
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ break;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_UDP */
+ /* Level: IPPROTO_RAW */
+ case IPPROTO_RAW:
+ switch (optname) {
+#if LWIP_IPV6 && LWIP_RAW
+ case IPV6_CHECKSUM:
+ /* It should not be possible to disable the checksum generation with ICMPv6
+ * as per RFC 3542 chapter 3.1 */
+ if (sock->conn->pcb.raw->protocol == IPPROTO_ICMPV6) {
+ done_socket(sock);
+ return EINVAL;
+ }
+
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_RAW);
+ if (*(const int *)optval < 0) {
+ sock->conn->pcb.raw->chksum_reqd = 0;
+ } else if (*(const int *)optval & 1) {
+ /* Per RFC3542, odd offsets are not allowed */
+ done_socket(sock);
+ return EINVAL;
+ } else {
+ sock->conn->pcb.raw->chksum_reqd = 1;
+ sock->conn->pcb.raw->chksum_offset = (u16_t) * (const int *)optval;
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_RAW, IPV6_CHECKSUM, ..) -> %d\n",
+ s, sock->conn->pcb.raw->chksum_reqd));
+ break;
+#endif /* LWIP_IPV6 && LWIP_RAW */
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_RAW, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ break;
+ } /* switch (optname) */
+ break;
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n",
+ s, level, optname));
+ err = ENOPROTOOPT;
+ break;
+ } /* switch (level) */
+
+ done_socket(sock);
+ return err;
+}
+
+int
+lwip_ioctl(int s, long cmd, void *argp)
+{
+ struct lwip_sock *sock = get_socket(s);
+ u8_t val;
+#if LWIP_SO_RCVBUF
+ int recv_avail;
+#endif /* LWIP_SO_RCVBUF */
+
+ if (!sock) {
+ return -1;
+ }
+
+ switch (cmd) {
+#if LWIP_SO_RCVBUF || LWIP_FIONREAD_LINUXMODE
+ case FIONREAD:
+ if (!argp) {
+ sock_set_errno(sock, EINVAL);
+ done_socket(sock);
+ return -1;
+ }
+#if LWIP_FIONREAD_LINUXMODE
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
+ struct netbuf *nb;
+ if (sock->lastdata.netbuf) {
+ nb = sock->lastdata.netbuf;
+ *((int *)argp) = nb->p->tot_len;
+ } else {
+ struct netbuf *rxbuf;
+ err_t err = netconn_recv_udp_raw_netbuf_flags(sock->conn, &rxbuf, NETCONN_DONTBLOCK);
+ if (err != ERR_OK) {
+ *((int *)argp) = 0;
+ } else {
+ sock->lastdata.netbuf = rxbuf;
+ *((int *)argp) = rxbuf->p->tot_len;
+ }
+ }
+ done_socket(sock);
+ return 0;
+ }
+#endif /* LWIP_FIONREAD_LINUXMODE */
+
+#if LWIP_SO_RCVBUF
+ /* we come here if either LWIP_FIONREAD_LINUXMODE==0 or this is a TCP socket */
+ SYS_ARCH_GET(sock->conn->recv_avail, recv_avail);
+ if (recv_avail < 0) {
+ recv_avail = 0;
+ }
+
+ /* Check if there is data left from the last recv operation. /maq 041215 */
+ if (sock->lastdata.netbuf) {
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+ recv_avail += sock->lastdata.pbuf->tot_len;
+ } else {
+ recv_avail += sock->lastdata.netbuf->p->tot_len;
+ }
+ }
+ *((int *)argp) = recv_avail;
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONREAD, %p) = %"U16_F"\n", s, argp, *((u16_t *)argp)));
+ sock_set_errno(sock, 0);
+ done_socket(sock);
+ return 0;
+#else /* LWIP_SO_RCVBUF */
+ break;
+#endif /* LWIP_SO_RCVBUF */
+#endif /* LWIP_SO_RCVBUF || LWIP_FIONREAD_LINUXMODE */
+
+ case (long)FIONBIO:
+ val = 0;
+ if (argp && *(int *)argp) {
+ val = 1;
+ }
+ netconn_set_nonblocking(sock->conn, val);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONBIO, %d)\n", s, val));
+ sock_set_errno(sock, 0);
+ done_socket(sock);
+ return 0;
+
+ default:
+ break;
+ } /* switch (cmd) */
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, UNIMPL: 0x%lx, %p)\n", s, cmd, argp));
+ sock_set_errno(sock, ENOSYS); /* not yet implemented */
+ done_socket(sock);
+ return -1;
+}
+
+/** A minimal implementation of fcntl.
+ * Currently only the commands F_GETFL and F_SETFL are implemented.
+ * The flag O_NONBLOCK and access modes are supported for F_GETFL, only
+ * the flag O_NONBLOCK is implemented for F_SETFL.
+ */
+int
+lwip_fcntl(int s, int cmd, int val)
+{
+ struct lwip_sock *sock = get_socket(s);
+ int ret = -1;
+ int op_mode = 0;
+
+ if (!sock) {
+ return -1;
+ }
+
+ switch (cmd) {
+ case F_GETFL:
+ ret = netconn_is_nonblocking(sock->conn) ? O_NONBLOCK : 0;
+ sock_set_errno(sock, 0);
+
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+#if LWIP_TCPIP_CORE_LOCKING
+ LOCK_TCPIP_CORE();
+#else
+ SYS_ARCH_DECL_PROTECT(lev);
+ /* the proper thing to do here would be to get into the tcpip_thread,
+ but locking should be OK as well since we only *read* some flags */
+ SYS_ARCH_PROTECT(lev);
+#endif
+#if LWIP_TCP
+ if (sock->conn->pcb.tcp) {
+ if (!(sock->conn->pcb.tcp->flags & TF_RXCLOSED)) {
+ op_mode |= O_RDONLY;
+ }
+ if (!(sock->conn->pcb.tcp->flags & TF_FIN)) {
+ op_mode |= O_WRONLY;
+ }
+ }
+#endif
+#if LWIP_TCPIP_CORE_LOCKING
+ UNLOCK_TCPIP_CORE();
+#else
+ SYS_ARCH_UNPROTECT(lev);
+#endif
+ } else {
+ op_mode |= O_RDWR;
+ }
+
+ /* ensure O_RDWR for (O_RDONLY|O_WRONLY) != O_RDWR cases */
+ ret |= (op_mode == (O_RDONLY | O_WRONLY)) ? O_RDWR : op_mode;
+
+ break;
+ case F_SETFL:
+ /* Bits corresponding to the file access mode and the file creation flags [..] that are set in arg shall be ignored */
+ val &= ~(O_RDONLY | O_WRONLY | O_RDWR);
+ if ((val & ~O_NONBLOCK) == 0) {
+ /* only O_NONBLOCK, all other bits are zero */
+ netconn_set_nonblocking(sock->conn, val & O_NONBLOCK);
+ ret = 0;
+ sock_set_errno(sock, 0);
+ } else {
+ sock_set_errno(sock, ENOSYS); /* not yet implemented */
+ }
+ break;
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_fcntl(%d, UNIMPL: %d, %d)\n", s, cmd, val));
+ sock_set_errno(sock, ENOSYS); /* not yet implemented */
+ break;
+ }
+ done_socket(sock);
+ return ret;
+}
+
+#if LWIP_COMPAT_SOCKETS == 2 && LWIP_POSIX_SOCKETS_IO_NAMES
+int
+fcntl(int s, int cmd, ...)
+{
+ va_list ap;
+ int val;
+
+ va_start(ap, cmd);
+ val = va_arg(ap, int);
+ va_end(ap);
+ return lwip_fcntl(s, cmd, val);
+}
+#endif
+
+const char *
+lwip_inet_ntop(int af, const void *src, char *dst, socklen_t size)
+{
+ const char *ret = NULL;
+ int size_int = (int)size;
+ if (size_int < 0) {
+ set_errno(ENOSPC);
+ return NULL;
+ }
+ switch (af) {
+#if LWIP_IPV4
+ case AF_INET:
+ ret = ip4addr_ntoa_r((const ip4_addr_t *)src, dst, size_int);
+ if (ret == NULL) {
+ set_errno(ENOSPC);
+ }
+ break;
+#endif
+#if LWIP_IPV6
+ case AF_INET6:
+ ret = ip6addr_ntoa_r((const ip6_addr_t *)src, dst, size_int);
+ if (ret == NULL) {
+ set_errno(ENOSPC);
+ }
+ break;
+#endif
+ default:
+ set_errno(EAFNOSUPPORT);
+ break;
+ }
+ return ret;
+}
+
+int
+lwip_inet_pton(int af, const char *src, void *dst)
+{
+ int err;
+ switch (af) {
+#if LWIP_IPV4
+ case AF_INET:
+ err = ip4addr_aton(src, (ip4_addr_t *)dst);
+ break;
+#endif
+#if LWIP_IPV6
+ case AF_INET6: {
+ /* convert into temporary variable since ip6_addr_t might be larger
+ than in6_addr when scopes are enabled */
+ ip6_addr_t addr;
+ err = ip6addr_aton(src, &addr);
+ if (err) {
+ memcpy(dst, &addr.addr, sizeof(addr.addr));
+ }
+ break;
+ }
+#endif
+ default:
+ err = -1;
+ set_errno(EAFNOSUPPORT);
+ break;
+ }
+ return err;
+}
+
+#if LWIP_IGMP
+/** Register a new IGMP membership. On socket close, the membership is dropped automatically.
+ *
+ * ATTENTION: this function is called from tcpip_thread (or under CORE_LOCK).
+ *
+ * @return 1 on success, 0 on failure
+ */
+static int
+lwip_socket_register_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr)
+{
+ struct lwip_sock *sock = get_socket(s);
+ int i;
+
+ if (!sock) {
+ return 0;
+ }
+
+ for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
+ if (socket_ipv4_multicast_memberships[i].sock == NULL) {
+ socket_ipv4_multicast_memberships[i].sock = sock;
+ ip4_addr_copy(socket_ipv4_multicast_memberships[i].if_addr, *if_addr);
+ ip4_addr_copy(socket_ipv4_multicast_memberships[i].multi_addr, *multi_addr);
+ done_socket(sock);
+ return 1;
+ }
+ }
+ done_socket(sock);
+ return 0;
+}
+
+/** Unregister a previously registered membership. This prevents dropping the membership
+ * on socket close.
+ *
+ * ATTENTION: this function is called from tcpip_thread (or under CORE_LOCK).
+ */
+static void
+lwip_socket_unregister_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr)
+{
+ struct lwip_sock *sock = get_socket(s);
+ int i;
+
+ if (!sock) {
+ return;
+ }
+
+ for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
+ if ((socket_ipv4_multicast_memberships[i].sock == sock) &&
+ ip4_addr_cmp(&socket_ipv4_multicast_memberships[i].if_addr, if_addr) &&
+ ip4_addr_cmp(&socket_ipv4_multicast_memberships[i].multi_addr, multi_addr)) {
+ socket_ipv4_multicast_memberships[i].sock = NULL;
+ ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].if_addr);
+ ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].multi_addr);
+ break;
+ }
+ }
+ done_socket(sock);
+}
+
+/** Drop all memberships of a socket that were not dropped explicitly via setsockopt.
+ *
+ * ATTENTION: this function is NOT called from tcpip_thread (or under CORE_LOCK).
+ */
+static void
+lwip_socket_drop_registered_memberships(int s)
+{
+ struct lwip_sock *sock = get_socket(s);
+ int i;
+
+ if (!sock) {
+ return;
+ }
+
+ for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
+ if (socket_ipv4_multicast_memberships[i].sock == sock) {
+ ip_addr_t multi_addr, if_addr;
+ ip_addr_copy_from_ip4(multi_addr, socket_ipv4_multicast_memberships[i].multi_addr);
+ ip_addr_copy_from_ip4(if_addr, socket_ipv4_multicast_memberships[i].if_addr);
+ socket_ipv4_multicast_memberships[i].sock = NULL;
+ ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].if_addr);
+ ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].multi_addr);
+
+ netconn_join_leave_group(sock->conn, &multi_addr, &if_addr, NETCONN_LEAVE);
+ }
+ }
+ done_socket(sock);
+}
+#endif /* LWIP_IGMP */
+
+#if LWIP_IPV6_MLD
+/** Register a new MLD6 membership. On socket close, the membership is dropped automatically.
+ *
+ * ATTENTION: this function is called from tcpip_thread (or under CORE_LOCK).
+ *
+ * @return 1 on success, 0 on failure
+ */
+static int
+lwip_socket_register_mld6_membership(int s, unsigned int if_idx, const ip6_addr_t *multi_addr)
+{
+ struct lwip_sock *sock = get_socket(s);
+ int i;
+
+ if (!sock) {
+ return 0;
+ }
+
+ for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
+ if (socket_ipv6_multicast_memberships[i].sock == NULL) {
+ socket_ipv6_multicast_memberships[i].sock = sock;
+ socket_ipv6_multicast_memberships[i].if_idx = (u8_t)if_idx;
+ ip6_addr_copy(socket_ipv6_multicast_memberships[i].multi_addr, *multi_addr);
+ done_socket(sock);
+ return 1;
+ }
+ }
+ done_socket(sock);
+ return 0;
+}
+
+/** Unregister a previously registered MLD6 membership. This prevents dropping the membership
+ * on socket close.
+ *
+ * ATTENTION: this function is called from tcpip_thread (or under CORE_LOCK).
+ */
+static void
+lwip_socket_unregister_mld6_membership(int s, unsigned int if_idx, const ip6_addr_t *multi_addr)
+{
+ struct lwip_sock *sock = get_socket(s);
+ int i;
+
+ if (!sock) {
+ return;
+ }
+
+ for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
+ if ((socket_ipv6_multicast_memberships[i].sock == sock) &&
+ (socket_ipv6_multicast_memberships[i].if_idx == if_idx) &&
+ ip6_addr_cmp(&socket_ipv6_multicast_memberships[i].multi_addr, multi_addr)) {
+ socket_ipv6_multicast_memberships[i].sock = NULL;
+ socket_ipv6_multicast_memberships[i].if_idx = NETIF_NO_INDEX;
+ ip6_addr_set_zero(&socket_ipv6_multicast_memberships[i].multi_addr);
+ break;
+ }
+ }
+ done_socket(sock);
+}
+
+/** Drop all MLD6 memberships of a socket that were not dropped explicitly via setsockopt.
+ *
+ * ATTENTION: this function is NOT called from tcpip_thread (or under CORE_LOCK).
+ */
+static void
+lwip_socket_drop_registered_mld6_memberships(int s)
+{
+ struct lwip_sock *sock = get_socket(s);
+ int i;
+
+ if (!sock) {
+ return;
+ }
+
+ for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
+ if (socket_ipv6_multicast_memberships[i].sock == sock) {
+ ip_addr_t multi_addr;
+ u8_t if_idx;
+
+ ip_addr_copy_from_ip6(multi_addr, socket_ipv6_multicast_memberships[i].multi_addr);
+ if_idx = socket_ipv6_multicast_memberships[i].if_idx;
+
+ socket_ipv6_multicast_memberships[i].sock = NULL;
+ socket_ipv6_multicast_memberships[i].if_idx = NETIF_NO_INDEX;
+ ip6_addr_set_zero(&socket_ipv6_multicast_memberships[i].multi_addr);
+
+ netconn_join_leave_group_netif(sock->conn, &multi_addr, if_idx, NETCONN_LEAVE);
+ }
+ }
+ done_socket(sock);
+}
+#endif /* LWIP_IPV6_MLD */
+
+#endif /* LWIP_SOCKET */
diff --git a/resources/ethernet/LwIP/api/tcpip.c b/resources/ethernet/LwIP/api/tcpip.c
new file mode 100644
index 00000000..34c9a991
--- /dev/null
+++ b/resources/ethernet/LwIP/api/tcpip.c
@@ -0,0 +1,658 @@
+/**
+ * @file
+ * Sequential API Main thread module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if !NO_SYS /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/priv/tcpip_priv.h"
+#include "lwip/sys.h"
+#include "lwip/memp.h"
+#include "lwip/mem.h"
+#include "lwip/init.h"
+#include "lwip/ip.h"
+#include "lwip/pbuf.h"
+#include "lwip/etharp.h"
+#include "netif/ethernet.h"
+
+#define TCPIP_MSG_VAR_REF(name) API_VAR_REF(name)
+#define TCPIP_MSG_VAR_DECLARE(name) API_VAR_DECLARE(struct tcpip_msg, name)
+#define TCPIP_MSG_VAR_ALLOC(name) API_VAR_ALLOC(struct tcpip_msg, MEMP_TCPIP_MSG_API, name, ERR_MEM)
+#define TCPIP_MSG_VAR_FREE(name) API_VAR_FREE(MEMP_TCPIP_MSG_API, name)
+
+/* global variables */
+static tcpip_init_done_fn tcpip_init_done;
+static void *tcpip_init_done_arg;
+static sys_mbox_t tcpip_mbox;
+
+#if LWIP_TCPIP_CORE_LOCKING
+/** The global semaphore to lock the stack. */
+sys_mutex_t lock_tcpip_core;
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+static void tcpip_thread_handle_msg(struct tcpip_msg *msg);
+
+#if !LWIP_TIMERS
+/* wait for a message with timers disabled (e.g. pass a timer-check trigger into tcpip_thread) */
+#define TCPIP_MBOX_FETCH(mbox, msg) sys_mbox_fetch(mbox, msg)
+#else /* !LWIP_TIMERS */
+/* wait for a message, timeouts are processed while waiting */
+#define TCPIP_MBOX_FETCH(mbox, msg) tcpip_timeouts_mbox_fetch(mbox, msg)
+/**
+ * Wait (forever) for a message to arrive in an mbox.
+ * While waiting, timeouts are processed.
+ *
+ * @param mbox the mbox to fetch the message from
+ * @param msg the place to store the message
+ */
+static void
+tcpip_timeouts_mbox_fetch(sys_mbox_t *mbox, void **msg)
+{
+ u32_t sleeptime, res;
+
+again:
+ LWIP_ASSERT_CORE_LOCKED();
+
+ sleeptime = sys_timeouts_sleeptime();
+ if (sleeptime == SYS_TIMEOUTS_SLEEPTIME_INFINITE) {
+ UNLOCK_TCPIP_CORE();
+ sys_arch_mbox_fetch(mbox, msg, 0);
+ LOCK_TCPIP_CORE();
+ return;
+ } else if (sleeptime == 0) {
+ sys_check_timeouts();
+ /* We try again to fetch a message from the mbox. */
+ goto again;
+ }
+
+ UNLOCK_TCPIP_CORE();
+ res = sys_arch_mbox_fetch(mbox, msg, sleeptime);
+ LOCK_TCPIP_CORE();
+ if (res == SYS_ARCH_TIMEOUT) {
+ /* If a SYS_ARCH_TIMEOUT value is returned, a timeout occurred
+ before a message could be fetched. */
+ sys_check_timeouts();
+ /* We try again to fetch a message from the mbox. */
+ goto again;
+ }
+}
+#endif /* !LWIP_TIMERS */
+
+/**
+ * The main lwIP thread. This thread has exclusive access to lwIP core functions
+ * (unless access to them is not locked). Other threads communicate with this
+ * thread using message boxes.
+ *
+ * It also starts all the timers to make sure they are running in the right
+ * thread context.
+ *
+ * @param arg unused argument
+ */
+static void
+tcpip_thread(void *arg)
+{
+ struct tcpip_msg *msg;
+ LWIP_UNUSED_ARG(arg);
+
+ LWIP_MARK_TCPIP_THREAD();
+
+ LOCK_TCPIP_CORE();
+ if (tcpip_init_done != NULL) {
+ tcpip_init_done(tcpip_init_done_arg);
+ }
+
+ while (1) { /* MAIN Loop */
+ LWIP_TCPIP_THREAD_ALIVE();
+ /* wait for a message, timeouts are processed while waiting */
+ TCPIP_MBOX_FETCH(&tcpip_mbox, (void **)&msg);
+ if (msg == NULL) {
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: NULL\n"));
+ LWIP_ASSERT("tcpip_thread: invalid message", 0);
+ continue;
+ }
+ tcpip_thread_handle_msg(msg);
+ }
+}
+
+/* Handle a single tcpip_msg
+ * This is in its own function for access by tests only.
+ */
+static void
+tcpip_thread_handle_msg(struct tcpip_msg *msg)
+{
+ switch (msg->type) {
+#if !LWIP_TCPIP_CORE_LOCKING
+ case TCPIP_MSG_API:
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API message %p\n", (void *)msg));
+ msg->msg.api_msg.function(msg->msg.api_msg.msg);
+ break;
+ case TCPIP_MSG_API_CALL:
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API CALL message %p\n", (void *)msg));
+ msg->msg.api_call.arg->err = msg->msg.api_call.function(msg->msg.api_call.arg);
+ sys_sem_signal(msg->msg.api_call.sem);
+ break;
+#endif /* !LWIP_TCPIP_CORE_LOCKING */
+
+#if !LWIP_TCPIP_CORE_LOCKING_INPUT
+ case TCPIP_MSG_INPKT:
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: PACKET %p\n", (void *)msg));
+ if (msg->msg.inp.input_fn(msg->msg.inp.p, msg->msg.inp.netif) != ERR_OK) {
+ pbuf_free(msg->msg.inp.p);
+ }
+ memp_free(MEMP_TCPIP_MSG_INPKT, msg);
+ break;
+#endif /* !LWIP_TCPIP_CORE_LOCKING_INPUT */
+
+#if LWIP_TCPIP_TIMEOUT && LWIP_TIMERS
+ case TCPIP_MSG_TIMEOUT:
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: TIMEOUT %p\n", (void *)msg));
+ sys_timeout(msg->msg.tmo.msecs, msg->msg.tmo.h, msg->msg.tmo.arg);
+ memp_free(MEMP_TCPIP_MSG_API, msg);
+ break;
+ case TCPIP_MSG_UNTIMEOUT:
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: UNTIMEOUT %p\n", (void *)msg));
+ sys_untimeout(msg->msg.tmo.h, msg->msg.tmo.arg);
+ memp_free(MEMP_TCPIP_MSG_API, msg);
+ break;
+#endif /* LWIP_TCPIP_TIMEOUT && LWIP_TIMERS */
+
+ case TCPIP_MSG_CALLBACK:
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK %p\n", (void *)msg));
+ msg->msg.cb.function(msg->msg.cb.ctx);
+ memp_free(MEMP_TCPIP_MSG_API, msg);
+ break;
+
+ case TCPIP_MSG_CALLBACK_STATIC:
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK_STATIC %p\n", (void *)msg));
+ msg->msg.cb.function(msg->msg.cb.ctx);
+ break;
+
+ default:
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: %d\n", msg->type));
+ LWIP_ASSERT("tcpip_thread: invalid message", 0);
+ break;
+ }
+}
+
+#ifdef TCPIP_THREAD_TEST
+/** Work on queued items in single-threaded test mode */
+int
+tcpip_thread_poll_one(void)
+{
+ int ret = 0;
+ struct tcpip_msg *msg;
+
+ if (sys_arch_mbox_tryfetch(&tcpip_mbox, (void **)&msg) != SYS_ARCH_TIMEOUT) {
+ LOCK_TCPIP_CORE();
+ if (msg != NULL) {
+ tcpip_thread_handle_msg(msg);
+ ret = 1;
+ }
+ UNLOCK_TCPIP_CORE();
+ }
+ return ret;
+}
+#endif
+
+/**
+ * Pass a received packet to tcpip_thread for input processing
+ *
+ * @param p the received packet
+ * @param inp the network interface on which the packet was received
+ * @param input_fn input function to call
+ */
+err_t
+tcpip_inpkt(struct pbuf *p, struct netif *inp, netif_input_fn input_fn)
+{
+#if LWIP_TCPIP_CORE_LOCKING_INPUT
+ err_t ret;
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_inpkt: PACKET %p/%p\n", (void *)p, (void *)inp));
+ LOCK_TCPIP_CORE();
+ ret = input_fn(p, inp);
+ UNLOCK_TCPIP_CORE();
+ return ret;
+#else /* LWIP_TCPIP_CORE_LOCKING_INPUT */
+ struct tcpip_msg *msg;
+
+ LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));
+
+ msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_INPKT);
+ if (msg == NULL) {
+ return ERR_MEM;
+ }
+
+ msg->type = TCPIP_MSG_INPKT;
+ msg->msg.inp.p = p;
+ msg->msg.inp.netif = inp;
+ msg->msg.inp.input_fn = input_fn;
+ if (sys_mbox_trypost(&tcpip_mbox, msg) != ERR_OK) {
+ memp_free(MEMP_TCPIP_MSG_INPKT, msg);
+ return ERR_MEM;
+ }
+ return ERR_OK;
+#endif /* LWIP_TCPIP_CORE_LOCKING_INPUT */
+}
+
+/**
+ * @ingroup lwip_os
+ * Pass a received packet to tcpip_thread for input processing with
+ * ethernet_input or ip_input. Don't call directly, pass to netif_add()
+ * and call netif->input().
+ *
+ * @param p the received packet, p->payload pointing to the Ethernet header or
+ * to an IP header (if inp doesn't have NETIF_FLAG_ETHARP or
+ * NETIF_FLAG_ETHERNET flags)
+ * @param inp the network interface on which the packet was received
+ */
+err_t
+tcpip_input(struct pbuf *p, struct netif *inp)
+{
+#if LWIP_ETHERNET
+ if (inp->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) {
+ return tcpip_inpkt(p, inp, ethernet_input);
+ } else
+#endif /* LWIP_ETHERNET */
+ return tcpip_inpkt(p, inp, ip_input);
+}
+
+/**
+ * @ingroup lwip_os
+ * Call a specific function in the thread context of
+ * tcpip_thread for easy access synchronization.
+ * A function called in that way may access lwIP core code
+ * without fearing concurrent access.
+ * Blocks until the request is posted.
+ * Must not be called from interrupt context!
+ *
+ * @param function the function to call
+ * @param ctx parameter passed to f
+ * @return ERR_OK if the function was called, another err_t if not
+ *
+ * @see tcpip_try_callback
+ */
+err_t
+tcpip_callback(tcpip_callback_fn function, void *ctx)
+{
+ struct tcpip_msg *msg;
+
+ LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));
+
+ msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
+ if (msg == NULL) {
+ return ERR_MEM;
+ }
+
+ msg->type = TCPIP_MSG_CALLBACK;
+ msg->msg.cb.function = function;
+ msg->msg.cb.ctx = ctx;
+
+ sys_mbox_post(&tcpip_mbox, msg);
+ return ERR_OK;
+}
+
+/**
+ * @ingroup lwip_os
+ * Call a specific function in the thread context of
+ * tcpip_thread for easy access synchronization.
+ * A function called in that way may access lwIP core code
+ * without fearing concurrent access.
+ * Does NOT block when the request cannot be posted because the
+ * tcpip_mbox is full, but returns ERR_MEM instead.
+ * Can be called from interrupt context.
+ *
+ * @param function the function to call
+ * @param ctx parameter passed to f
+ * @return ERR_OK if the function was called, another err_t if not
+ *
+ * @see tcpip_callback
+ */
+err_t
+tcpip_try_callback(tcpip_callback_fn function, void *ctx)
+{
+ struct tcpip_msg *msg;
+
+ LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));
+
+ msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
+ if (msg == NULL) {
+ return ERR_MEM;
+ }
+
+ msg->type = TCPIP_MSG_CALLBACK;
+ msg->msg.cb.function = function;
+ msg->msg.cb.ctx = ctx;
+
+ if (sys_mbox_trypost(&tcpip_mbox, msg) != ERR_OK) {
+ memp_free(MEMP_TCPIP_MSG_API, msg);
+ return ERR_MEM;
+ }
+ return ERR_OK;
+}
+
+#if LWIP_TCPIP_TIMEOUT && LWIP_TIMERS
+/**
+ * call sys_timeout in tcpip_thread
+ *
+ * @param msecs time in milliseconds for timeout
+ * @param h function to be called on timeout
+ * @param arg argument to pass to timeout function h
+ * @return ERR_MEM on memory error, ERR_OK otherwise
+ */
+err_t
+tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg)
+{
+ struct tcpip_msg *msg;
+
+ LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));
+
+ msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
+ if (msg == NULL) {
+ return ERR_MEM;
+ }
+
+ msg->type = TCPIP_MSG_TIMEOUT;
+ msg->msg.tmo.msecs = msecs;
+ msg->msg.tmo.h = h;
+ msg->msg.tmo.arg = arg;
+ sys_mbox_post(&tcpip_mbox, msg);
+ return ERR_OK;
+}
+
+/**
+ * call sys_untimeout in tcpip_thread
+ *
+ * @param h function to be called on timeout
+ * @param arg argument to pass to timeout function h
+ * @return ERR_MEM on memory error, ERR_OK otherwise
+ */
+err_t
+tcpip_untimeout(sys_timeout_handler h, void *arg)
+{
+ struct tcpip_msg *msg;
+
+ LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));
+
+ msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
+ if (msg == NULL) {
+ return ERR_MEM;
+ }
+
+ msg->type = TCPIP_MSG_UNTIMEOUT;
+ msg->msg.tmo.h = h;
+ msg->msg.tmo.arg = arg;
+ sys_mbox_post(&tcpip_mbox, msg);
+ return ERR_OK;
+}
+#endif /* LWIP_TCPIP_TIMEOUT && LWIP_TIMERS */
+
+
+/**
+ * Sends a message to TCPIP thread to call a function. Caller thread blocks on
+ * on a provided semaphore, which ist NOT automatically signalled by TCPIP thread,
+ * this has to be done by the user.
+ * It is recommended to use LWIP_TCPIP_CORE_LOCKING since this is the way
+ * with least runtime overhead.
+ *
+ * @param fn function to be called from TCPIP thread
+ * @param apimsg argument to API function
+ * @param sem semaphore to wait on
+ * @return ERR_OK if the function was called, another err_t if not
+ */
+err_t
+tcpip_send_msg_wait_sem(tcpip_callback_fn fn, void *apimsg, sys_sem_t *sem)
+{
+#if LWIP_TCPIP_CORE_LOCKING
+ LWIP_UNUSED_ARG(sem);
+ LOCK_TCPIP_CORE();
+ fn(apimsg);
+ UNLOCK_TCPIP_CORE();
+ return ERR_OK;
+#else /* LWIP_TCPIP_CORE_LOCKING */
+ TCPIP_MSG_VAR_DECLARE(msg);
+
+ LWIP_ASSERT("semaphore not initialized", sys_sem_valid(sem));
+ LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));
+
+ TCPIP_MSG_VAR_ALLOC(msg);
+ TCPIP_MSG_VAR_REF(msg).type = TCPIP_MSG_API;
+ TCPIP_MSG_VAR_REF(msg).msg.api_msg.function = fn;
+ TCPIP_MSG_VAR_REF(msg).msg.api_msg.msg = apimsg;
+ sys_mbox_post(&tcpip_mbox, &TCPIP_MSG_VAR_REF(msg));
+ sys_arch_sem_wait(sem, 0);
+ TCPIP_MSG_VAR_FREE(msg);
+ return ERR_OK;
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+}
+
+/**
+ * Synchronously calls function in TCPIP thread and waits for its completion.
+ * It is recommended to use LWIP_TCPIP_CORE_LOCKING (preferred) or
+ * LWIP_NETCONN_SEM_PER_THREAD.
+ * If not, a semaphore is created and destroyed on every call which is usually
+ * an expensive/slow operation.
+ * @param fn Function to call
+ * @param call Call parameters
+ * @return Return value from tcpip_api_call_fn
+ */
+err_t
+tcpip_api_call(tcpip_api_call_fn fn, struct tcpip_api_call_data *call)
+{
+#if LWIP_TCPIP_CORE_LOCKING
+ err_t err;
+ LOCK_TCPIP_CORE();
+ err = fn(call);
+ UNLOCK_TCPIP_CORE();
+ return err;
+#else /* LWIP_TCPIP_CORE_LOCKING */
+ TCPIP_MSG_VAR_DECLARE(msg);
+
+#if !LWIP_NETCONN_SEM_PER_THREAD
+ err_t err = sys_sem_new(&call->sem, 0);
+ if (err != ERR_OK) {
+ return err;
+ }
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+ LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));
+
+ TCPIP_MSG_VAR_ALLOC(msg);
+ TCPIP_MSG_VAR_REF(msg).type = TCPIP_MSG_API_CALL;
+ TCPIP_MSG_VAR_REF(msg).msg.api_call.arg = call;
+ TCPIP_MSG_VAR_REF(msg).msg.api_call.function = fn;
+#if LWIP_NETCONN_SEM_PER_THREAD
+ TCPIP_MSG_VAR_REF(msg).msg.api_call.sem = LWIP_NETCONN_THREAD_SEM_GET();
+#else /* LWIP_NETCONN_SEM_PER_THREAD */
+ TCPIP_MSG_VAR_REF(msg).msg.api_call.sem = &call->sem;
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+ sys_mbox_post(&tcpip_mbox, &TCPIP_MSG_VAR_REF(msg));
+ sys_arch_sem_wait(TCPIP_MSG_VAR_REF(msg).msg.api_call.sem, 0);
+ TCPIP_MSG_VAR_FREE(msg);
+
+#if !LWIP_NETCONN_SEM_PER_THREAD
+ sys_sem_free(&call->sem);
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+ return call->err;
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+}
+
+/**
+ * @ingroup lwip_os
+ * Allocate a structure for a static callback message and initialize it.
+ * The message has a special type such that lwIP never frees it.
+ * This is intended to be used to send "static" messages from interrupt context,
+ * e.g. the message is allocated once and posted several times from an IRQ
+ * using tcpip_callbackmsg_trycallback().
+ * Example usage: Trigger execution of an ethernet IRQ DPC routine in lwIP thread context.
+ *
+ * @param function the function to call
+ * @param ctx parameter passed to function
+ * @return a struct pointer to pass to tcpip_callbackmsg_trycallback().
+ *
+ * @see tcpip_callbackmsg_trycallback()
+ * @see tcpip_callbackmsg_delete()
+ */
+struct tcpip_callback_msg *
+tcpip_callbackmsg_new(tcpip_callback_fn function, void *ctx)
+{
+ struct tcpip_msg *msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
+ if (msg == NULL) {
+ return NULL;
+ }
+ msg->type = TCPIP_MSG_CALLBACK_STATIC;
+ msg->msg.cb.function = function;
+ msg->msg.cb.ctx = ctx;
+ return (struct tcpip_callback_msg *)msg;
+}
+
+/**
+ * @ingroup lwip_os
+ * Free a callback message allocated by tcpip_callbackmsg_new().
+ *
+ * @param msg the message to free
+ *
+ * @see tcpip_callbackmsg_new()
+ */
+void
+tcpip_callbackmsg_delete(struct tcpip_callback_msg *msg)
+{
+ memp_free(MEMP_TCPIP_MSG_API, msg);
+}
+
+/**
+ * @ingroup lwip_os
+ * Try to post a callback-message to the tcpip_thread tcpip_mbox.
+ *
+ * @param msg pointer to the message to post
+ * @return sys_mbox_trypost() return code
+ *
+ * @see tcpip_callbackmsg_new()
+ */
+err_t
+tcpip_callbackmsg_trycallback(struct tcpip_callback_msg *msg)
+{
+ LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));
+ return sys_mbox_trypost(&tcpip_mbox, msg);
+}
+
+/**
+ * @ingroup lwip_os
+ * Try to post a callback-message to the tcpip_thread mbox.
+ * Same as @ref tcpip_callbackmsg_trycallback but calls sys_mbox_trypost_fromisr(),
+ * mainly to help FreeRTOS, where calls differ between task level and ISR level.
+ *
+ * @param msg pointer to the message to post
+ * @return sys_mbox_trypost_fromisr() return code (without change, so this
+ * knowledge can be used to e.g. propagate "bool needs_scheduling")
+ *
+ * @see tcpip_callbackmsg_new()
+ */
+err_t
+tcpip_callbackmsg_trycallback_fromisr(struct tcpip_callback_msg *msg)
+{
+ LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));
+ return sys_mbox_trypost_fromisr(&tcpip_mbox, msg);
+}
+
+/**
+ * @ingroup lwip_os
+ * Initialize this module:
+ * - initialize all sub modules
+ * - start the tcpip_thread
+ *
+ * @param initfunc a function to call when tcpip_thread is running and finished initializing
+ * @param arg argument to pass to initfunc
+ */
+void
+tcpip_init(tcpip_init_done_fn initfunc, void *arg)
+{
+ lwip_init();
+
+ tcpip_init_done = initfunc;
+ tcpip_init_done_arg = arg;
+ if (sys_mbox_new(&tcpip_mbox, TCPIP_MBOX_SIZE) != ERR_OK) {
+ LWIP_ASSERT("failed to create tcpip_thread mbox", 0);
+ }
+#if LWIP_TCPIP_CORE_LOCKING
+ if (sys_mutex_new(&lock_tcpip_core) != ERR_OK) {
+ LWIP_ASSERT("failed to create lock_tcpip_core", 0);
+ }
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+ sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO);
+}
+
+/**
+ * Simple callback function used with tcpip_callback to free a pbuf
+ * (pbuf_free has a wrong signature for tcpip_callback)
+ *
+ * @param p The pbuf (chain) to be dereferenced.
+ */
+static void
+pbuf_free_int(void *p)
+{
+ struct pbuf *q = (struct pbuf *)p;
+ pbuf_free(q);
+}
+
+/**
+ * A simple wrapper function that allows you to free a pbuf from interrupt context.
+ *
+ * @param p The pbuf (chain) to be dereferenced.
+ * @return ERR_OK if callback could be enqueued, an err_t if not
+ */
+err_t
+pbuf_free_callback(struct pbuf *p)
+{
+ return tcpip_try_callback(pbuf_free_int, p);
+}
+
+/**
+ * A simple wrapper function that allows you to free heap memory from
+ * interrupt context.
+ *
+ * @param m the heap memory to free
+ * @return ERR_OK if callback could be enqueued, an err_t if not
+ */
+err_t
+mem_free_callback(void *m)
+{
+ return tcpip_try_callback(mem_free, m);
+}
+
+#endif /* !NO_SYS */
diff --git a/resources/ethernet/LwIP/apps/altcp_tls/altcp_tls_mbedtls.c b/resources/ethernet/LwIP/apps/altcp_tls/altcp_tls_mbedtls.c
new file mode 100644
index 00000000..e629695a
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/altcp_tls/altcp_tls_mbedtls.c
@@ -0,0 +1,1178 @@
+/**
+ * @file
+ * Application layered TCP/TLS connection API (to be used from TCPIP thread)
+ *
+ * This file provides a TLS layer using mbedTLS
+ */
+
+/*
+ * Copyright (c) 2017 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ *
+ * Watch out:
+ * - 'sent' is always called with len==0 to the upper layer. This is because keeping
+ * track of the ratio of application data and TLS overhead would be too much.
+ *
+ * Mandatory security-related configuration:
+ * - define ALTCP_MBEDTLS_RNG_FN to mbedtls_entropy_func to use the standard mbedTLS
+ * entropy and ensure to add at least one strong entropy source to your mbedtls port
+ * (implement mbedtls_platform_entropy_poll or mbedtls_hardware_poll providing strong
+ * entropy)
+ * - define ALTCP_MBEDTLS_ENTROPY_PTR and ALTCP_MBEDTLS_ENTROPY_LEN to something providing
+ * GOOD custom entropy
+ *
+ * Missing things / @todo:
+ * - some unhandled/untested things migh be caught by LWIP_ASSERTs...
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/altcp_tls_mbedtls_opts.h"
+
+#if LWIP_ALTCP_TLS && LWIP_ALTCP_TLS_MBEDTLS
+
+#include "lwip/altcp.h"
+#include "lwip/altcp_tls.h"
+#include "lwip/priv/altcp_priv.h"
+
+#include "altcp_tls_mbedtls_structs.h"
+#include "altcp_tls_mbedtls_mem.h"
+
+/* @todo: which includes are really needed? */
+#include "mbedtls/entropy.h"
+#include "mbedtls/ctr_drbg.h"
+#include "mbedtls/certs.h"
+#include "mbedtls/x509.h"
+#include "mbedtls/ssl.h"
+#include "mbedtls/net.h"
+#include "mbedtls/error.h"
+#include "mbedtls/debug.h"
+#include "mbedtls/platform.h"
+#include "mbedtls/memory_buffer_alloc.h"
+#include "mbedtls/ssl_cache.h"
+
+#include "mbedtls/ssl_internal.h" /* to call mbedtls_flush_output after ERR_MEM */
+
+#include
+
+#ifndef ALTCP_MBEDTLS_ENTROPY_PTR
+#define ALTCP_MBEDTLS_ENTROPY_PTR NULL
+#endif
+#ifndef ALTCP_MBEDTLS_ENTROPY_LEN
+#define ALTCP_MBEDTLS_ENTROPY_LEN 0
+#endif
+
+/* Variable prototype, the actual declaration is at the end of this file
+ since it contains pointers to static functions declared here */
+extern const struct altcp_functions altcp_mbedtls_functions;
+
+/** Our global mbedTLS configuration (server-specific, not connection-specific) */
+struct altcp_tls_config {
+ mbedtls_ssl_config conf;
+ mbedtls_entropy_context entropy;
+ mbedtls_ctr_drbg_context ctr_drbg;
+ mbedtls_x509_crt *cert;
+ mbedtls_pk_context *pkey;
+ mbedtls_x509_crt *ca;
+#if defined(MBEDTLS_SSL_CACHE_C) && ALTCP_MBEDTLS_SESSION_CACHE_TIMEOUT_SECONDS
+ /** Inter-connection cache for fast connection startup */
+ struct mbedtls_ssl_cache_context cache;
+#endif
+};
+
+static err_t altcp_mbedtls_lower_recv(void *arg, struct altcp_pcb *inner_conn, struct pbuf *p, err_t err);
+static err_t altcp_mbedtls_setup(void *conf, struct altcp_pcb *conn, struct altcp_pcb *inner_conn);
+static err_t altcp_mbedtls_lower_recv_process(struct altcp_pcb *conn, altcp_mbedtls_state_t *state);
+static err_t altcp_mbedtls_handle_rx_appldata(struct altcp_pcb *conn, altcp_mbedtls_state_t *state);
+static int altcp_mbedtls_bio_send(void *ctx, const unsigned char *dataptr, size_t size);
+
+
+/* callback functions from inner/lower connection: */
+
+/** Accept callback from lower connection (i.e. TCP)
+ * Allocate one of our structures, assign it to the new connection's 'state' and
+ * call the new connection's 'accepted' callback. If that succeeds, we wait
+ * to receive connection setup handshake bytes from the client.
+ */
+static err_t
+altcp_mbedtls_lower_accept(void *arg, struct altcp_pcb *accepted_conn, err_t err)
+{
+ struct altcp_pcb *listen_conn = (struct altcp_pcb *)arg;
+ if (listen_conn && listen_conn->state && listen_conn->accept) {
+ err_t setup_err;
+ altcp_mbedtls_state_t *listen_state = (altcp_mbedtls_state_t *)listen_conn->state;
+ /* create a new altcp_conn to pass to the next 'accept' callback */
+ struct altcp_pcb *new_conn = altcp_alloc();
+ if (new_conn == NULL) {
+ return ERR_MEM;
+ }
+ setup_err = altcp_mbedtls_setup(listen_state->conf, new_conn, accepted_conn);
+ if (setup_err != ERR_OK) {
+ altcp_free(new_conn);
+ return setup_err;
+ }
+ return listen_conn->accept(listen_conn->arg, new_conn, err);
+ }
+ return ERR_ARG;
+}
+
+/** Connected callback from lower connection (i.e. TCP).
+ * Not really implemented/tested yet...
+ */
+static err_t
+altcp_mbedtls_lower_connected(void *arg, struct altcp_pcb *inner_conn, err_t err)
+{
+ struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+ LWIP_UNUSED_ARG(inner_conn); /* for LWIP_NOASSERT */
+ if (conn && conn->state) {
+ LWIP_ASSERT("pcb mismatch", conn->inner_conn == inner_conn);
+ /* upper connected is called when handshake is done */
+ if (err != ERR_OK) {
+ if (conn->connected) {
+ return conn->connected(conn->arg, conn, err);
+ }
+ }
+ return altcp_mbedtls_lower_recv_process(conn, (altcp_mbedtls_state_t *)conn->state);
+ }
+ return ERR_VAL;
+}
+
+/* Call recved for possibly more than an u16_t */
+static void
+altcp_mbedtls_lower_recved(struct altcp_pcb *inner_conn, int recvd_cnt)
+{
+ while (recvd_cnt > 0) {
+ u16_t recvd_part = (u16_t)LWIP_MIN(recvd_cnt, 0xFFFF);
+ altcp_recved(inner_conn, recvd_part);
+ recvd_cnt -= recvd_part;
+ }
+}
+
+/** Recv callback from lower connection (i.e. TCP)
+ * This one mainly differs between connection setup/handshake (data is fed into mbedTLS only)
+ * and application phase (data is decoded by mbedTLS and passed on to the application).
+ */
+static err_t
+altcp_mbedtls_lower_recv(void *arg, struct altcp_pcb *inner_conn, struct pbuf *p, err_t err)
+{
+ altcp_mbedtls_state_t *state;
+ struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+
+ LWIP_ASSERT("no err expected", err == ERR_OK);
+ LWIP_UNUSED_ARG(err);
+
+ if (!conn) {
+ /* no connection given as arg? should not happen, but prevent pbuf/conn leaks */
+ if (p != NULL) {
+ pbuf_free(p);
+ }
+ altcp_close(inner_conn);
+ return ERR_CLSD;
+ }
+ state = (altcp_mbedtls_state_t *)conn->state;
+ LWIP_ASSERT("pcb mismatch", conn->inner_conn == inner_conn);
+ if (!state) {
+ /* already closed */
+ if (p != NULL) {
+ pbuf_free(p);
+ }
+ altcp_close(inner_conn);
+ return ERR_CLSD;
+ }
+
+ /* handle NULL pbuf (inner connection closed) */
+ if (p == NULL) {
+ /* remote host sent FIN, remember this (SSL state is destroyed
+ when both sides are closed only!) */
+ if ((state->flags & (ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE | ALTCP_MBEDTLS_FLAGS_UPPER_CALLED)) ==
+ (ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE | ALTCP_MBEDTLS_FLAGS_UPPER_CALLED)) {
+ /* need to notify upper layer (e.g. 'accept' called or 'connect' succeeded) */
+ if ((state->rx != NULL) || (state->rx_app != NULL)) {
+ state->flags |= ALTCP_MBEDTLS_FLAGS_RX_CLOSE_QUEUED;
+ /* this is a normal close (FIN) but we have unprocessed data, so delay the FIN */
+ altcp_mbedtls_handle_rx_appldata(conn, state);
+ return ERR_OK;
+ }
+ state->flags |= ALTCP_MBEDTLS_FLAGS_RX_CLOSED;
+ if (conn->recv) {
+ return conn->recv(conn->arg, conn, NULL, ERR_OK);
+ }
+ } else {
+ /* before connection setup is done: call 'err' */
+ if (conn->err) {
+ conn->err(conn->arg, ERR_CLSD);
+ }
+ altcp_close(conn);
+ }
+ return ERR_OK;
+ }
+
+ /* If we come here, the connection is in good state (handshake phase or application data phase).
+ Queue up the pbuf for processing as handshake data or application data. */
+ if (state->rx == NULL) {
+ state->rx = p;
+ } else {
+ LWIP_ASSERT("rx pbuf overflow", (int)p->tot_len + (int)p->len <= 0xFFFF);
+ pbuf_cat(state->rx, p);
+ }
+ return altcp_mbedtls_lower_recv_process(conn, state);
+}
+
+static err_t
+altcp_mbedtls_lower_recv_process(struct altcp_pcb *conn, altcp_mbedtls_state_t *state)
+{
+ if (!(state->flags & ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE)) {
+ /* handle connection setup (handshake not done) */
+ int ret = mbedtls_ssl_handshake(&state->ssl_context);
+ /* try to send data... */
+ altcp_output(conn->inner_conn);
+ if (state->bio_bytes_read) {
+ /* acknowledge all bytes read */
+ altcp_mbedtls_lower_recved(conn->inner_conn, state->bio_bytes_read);
+ state->bio_bytes_read = 0;
+ }
+
+ if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
+ /* handshake not done, wait for more recv calls */
+ LWIP_ASSERT("in this state, the rx chain should be empty", state->rx == NULL);
+ return ERR_OK;
+ }
+ if (ret != 0) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_ssl_handshake failed: %d\n", ret));
+ /* handshake failed, connection has to be closed */
+ if (conn->err) {
+ conn->err(conn->arg, ERR_CLSD);
+ }
+
+ if (altcp_close(conn) != ERR_OK) {
+ altcp_abort(conn);
+ }
+ return ERR_OK;
+ }
+ /* If we come here, handshake succeeded. */
+ LWIP_ASSERT("state", state->bio_bytes_read == 0);
+ LWIP_ASSERT("state", state->bio_bytes_appl == 0);
+ state->flags |= ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE;
+ /* issue "connect" callback" to upper connection (this can only happen for active open) */
+ if (conn->connected) {
+ err_t err;
+ err = conn->connected(conn->arg, conn, ERR_OK);
+ if (err != ERR_OK) {
+ return err;
+ }
+ }
+ if (state->rx == NULL) {
+ return ERR_OK;
+ }
+ }
+ /* handle application data */
+ return altcp_mbedtls_handle_rx_appldata(conn, state);
+}
+
+/* Pass queued decoded rx data to application */
+static err_t
+altcp_mbedtls_pass_rx_data(struct altcp_pcb *conn, altcp_mbedtls_state_t *state)
+{
+ err_t err;
+ struct pbuf *buf;
+ LWIP_ASSERT("conn != NULL", conn != NULL);
+ LWIP_ASSERT("state != NULL", state != NULL);
+ buf = state->rx_app;
+ if (buf) {
+ state->rx_app = NULL;
+ if (conn->recv) {
+ u16_t tot_len = buf->tot_len;
+ /* this needs to be increased first because the 'recved' call may come nested */
+ state->rx_passed_unrecved += tot_len;
+ state->flags |= ALTCP_MBEDTLS_FLAGS_UPPER_CALLED;
+ err = conn->recv(conn->arg, conn, buf, ERR_OK);
+ if (err != ERR_OK) {
+ if (err == ERR_ABRT) {
+ return ERR_ABRT;
+ }
+ /* not received, leave the pbuf(s) queued (and decrease 'unrecved' again) */
+ LWIP_ASSERT("state == conn->state", state == conn->state);
+ state->rx_app = buf;
+ state->rx_passed_unrecved -= tot_len;
+ LWIP_ASSERT("state->rx_passed_unrecved >= 0", state->rx_passed_unrecved >= 0);
+ if (state->rx_passed_unrecved < 0) {
+ state->rx_passed_unrecved = 0;
+ }
+ return err;
+ }
+ } else {
+ pbuf_free(buf);
+ }
+ } else if ((state->flags & (ALTCP_MBEDTLS_FLAGS_RX_CLOSE_QUEUED | ALTCP_MBEDTLS_FLAGS_RX_CLOSED)) ==
+ ALTCP_MBEDTLS_FLAGS_RX_CLOSE_QUEUED) {
+ state->flags |= ALTCP_MBEDTLS_FLAGS_RX_CLOSED;
+ if (conn->recv) {
+ return conn->recv(conn->arg, conn, NULL, ERR_OK);
+ }
+ }
+
+ /* application may have close the connection */
+ if (conn->state != state) {
+ /* return error code to ensure altcp_mbedtls_handle_rx_appldata() exits the loop */
+ return ERR_CLSD;
+ }
+ return ERR_OK;
+}
+
+/* Helper function that processes rx application data stored in rx pbuf chain */
+static err_t
+altcp_mbedtls_handle_rx_appldata(struct altcp_pcb *conn, altcp_mbedtls_state_t *state)
+{
+ int ret;
+ LWIP_ASSERT("state != NULL", state != NULL);
+ if (!(state->flags & ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE)) {
+ /* handshake not done yet */
+ return ERR_VAL;
+ }
+ do {
+ /* allocate a full-sized unchained PBUF_POOL: this is for RX! */
+ struct pbuf *buf = pbuf_alloc(PBUF_RAW, PBUF_POOL_BUFSIZE, PBUF_POOL);
+ if (buf == NULL) {
+ /* We're short on pbufs, try again later from 'poll' or 'recv' callbacks.
+ @todo: close on excessive allocation failures or leave this up to upper conn? */
+ return ERR_OK;
+ }
+
+ /* decrypt application data, this pulls encrypted RX data off state->rx pbuf chain */
+ ret = mbedtls_ssl_read(&state->ssl_context, (unsigned char *)buf->payload, PBUF_POOL_BUFSIZE);
+ if (ret < 0) {
+ if (ret == MBEDTLS_ERR_SSL_CLIENT_RECONNECT) {
+ /* client is initiating a new connection using the same source port -> close connection or make handshake */
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("new connection on same source port\n"));
+ LWIP_ASSERT("TODO: new connection on same source port, close this connection", 0);
+ } else if ((ret != MBEDTLS_ERR_SSL_WANT_READ) && (ret != MBEDTLS_ERR_SSL_WANT_WRITE)) {
+ if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("connection was closed gracefully\n"));
+ } else if (ret == MBEDTLS_ERR_NET_CONN_RESET) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("connection was reset by peer\n"));
+ }
+ pbuf_free(buf);
+ return ERR_OK;
+ } else {
+ pbuf_free(buf);
+ return ERR_OK;
+ }
+ pbuf_free(buf);
+ altcp_abort(conn);
+ return ERR_ABRT;
+ } else {
+ err_t err;
+ if (ret) {
+ LWIP_ASSERT("bogus receive length", ret <= PBUF_POOL_BUFSIZE);
+ /* trim pool pbuf to actually decoded length */
+ pbuf_realloc(buf, (u16_t)ret);
+
+ state->bio_bytes_appl += ret;
+ if (mbedtls_ssl_get_bytes_avail(&state->ssl_context) == 0) {
+ /* Record is done, now we know the share between application and protocol bytes
+ and can adjust the RX window by the protocol bytes.
+ The rest is 'recved' by the application calling our 'recved' fn. */
+ int overhead_bytes;
+ LWIP_ASSERT("bogus byte counts", state->bio_bytes_read > state->bio_bytes_appl);
+ overhead_bytes = state->bio_bytes_read - state->bio_bytes_appl;
+ altcp_mbedtls_lower_recved(conn->inner_conn, overhead_bytes);
+ state->bio_bytes_read = 0;
+ state->bio_bytes_appl = 0;
+ }
+
+ if (state->rx_app == NULL) {
+ state->rx_app = buf;
+ } else {
+ pbuf_cat(state->rx_app, buf);
+ }
+ } else {
+ pbuf_free(buf);
+ buf = NULL;
+ }
+ err = altcp_mbedtls_pass_rx_data(conn, state);
+ if (err != ERR_OK) {
+ if (err == ERR_ABRT) {
+ /* recv callback needs to return this as the pcb is deallocated */
+ return ERR_ABRT;
+ }
+ /* we hide all other errors as we retry feeding the pbuf to the app later */
+ return ERR_OK;
+ }
+ }
+ } while (ret > 0);
+ return ERR_OK;
+}
+
+/** Receive callback function called from mbedtls (set via mbedtls_ssl_set_bio)
+ * This function mainly copies data from pbufs and frees the pbufs after copying.
+ */
+static int
+altcp_mbedtls_bio_recv(void *ctx, unsigned char *buf, size_t len)
+{
+ struct altcp_pcb *conn = (struct altcp_pcb *)ctx;
+ altcp_mbedtls_state_t *state;
+ struct pbuf *p;
+ u16_t ret;
+ u16_t copy_len;
+ err_t err;
+
+ LWIP_UNUSED_ARG(err); /* for LWIP_NOASSERT */
+ if ((conn == NULL) || (conn->state == NULL)) {
+ return MBEDTLS_ERR_NET_INVALID_CONTEXT;
+ }
+ state = (altcp_mbedtls_state_t *)conn->state;
+ p = state->rx;
+
+ /* @todo: return MBEDTLS_ERR_NET_CONN_RESET/MBEDTLS_ERR_NET_RECV_FAILED? */
+
+ if ((p == NULL) || ((p->len == 0) && (p->next == NULL))) {
+ if (p) {
+ pbuf_free(p);
+ }
+ state->rx = NULL;
+ if ((state->flags & (ALTCP_MBEDTLS_FLAGS_RX_CLOSE_QUEUED | ALTCP_MBEDTLS_FLAGS_RX_CLOSED)) ==
+ ALTCP_MBEDTLS_FLAGS_RX_CLOSE_QUEUED) {
+ /* close queued but not passed up yet */
+ return 0;
+ }
+ return MBEDTLS_ERR_SSL_WANT_READ;
+ }
+ /* limit number of bytes again to copy from first pbuf in a chain only */
+ copy_len = (u16_t)LWIP_MIN(len, p->len);
+ /* copy the data */
+ ret = pbuf_copy_partial(p, buf, copy_len, 0);
+ LWIP_ASSERT("ret == copy_len", ret == copy_len);
+ /* hide the copied bytes from the pbuf */
+ err = pbuf_remove_header(p, ret);
+ LWIP_ASSERT("error", err == ERR_OK);
+ if (p->len == 0) {
+ /* the first pbuf has been fully read, free it */
+ state->rx = p->next;
+ p->next = NULL;
+ pbuf_free(p);
+ }
+
+ state->bio_bytes_read += (int)ret;
+ return ret;
+}
+
+/** Sent callback from lower connection (i.e. TCP)
+ * This only informs the upper layer to try to send more, not about
+ * the number of ACKed bytes.
+ */
+static err_t
+altcp_mbedtls_lower_sent(void *arg, struct altcp_pcb *inner_conn, u16_t len)
+{
+ struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+ LWIP_UNUSED_ARG(inner_conn); /* for LWIP_NOASSERT */
+ LWIP_UNUSED_ARG(len);
+ if (conn) {
+ altcp_mbedtls_state_t *state = (altcp_mbedtls_state_t *)conn->state;
+ LWIP_ASSERT("pcb mismatch", conn->inner_conn == inner_conn);
+ if (!state || !(state->flags & ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE)) {
+ /* @todo: do something here? */
+ return ERR_OK;
+ }
+ /* try to send more if we failed before */
+ mbedtls_ssl_flush_output(&state->ssl_context);
+ /* call upper sent with len==0 if the application already sent data */
+ if ((state->flags & ALTCP_MBEDTLS_FLAGS_APPLDATA_SENT) && conn->sent) {
+ return conn->sent(conn->arg, conn, 0);
+ }
+ }
+ return ERR_OK;
+}
+
+/** Poll callback from lower connection (i.e. TCP)
+ * Just pass this on to the application.
+ * @todo: retry sending?
+ */
+static err_t
+altcp_mbedtls_lower_poll(void *arg, struct altcp_pcb *inner_conn)
+{
+ struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+ LWIP_UNUSED_ARG(inner_conn); /* for LWIP_NOASSERT */
+ if (conn) {
+ LWIP_ASSERT("pcb mismatch", conn->inner_conn == inner_conn);
+ /* check if there's unreceived rx data */
+ if (conn->state) {
+ altcp_mbedtls_state_t *state = (altcp_mbedtls_state_t *)conn->state;
+ /* try to send more if we failed before */
+ mbedtls_ssl_flush_output(&state->ssl_context);
+ if (altcp_mbedtls_handle_rx_appldata(conn, state) == ERR_ABRT) {
+ return ERR_ABRT;
+ }
+ }
+ if (conn->poll) {
+ return conn->poll(conn->arg, conn);
+ }
+ }
+ return ERR_OK;
+}
+
+static void
+altcp_mbedtls_lower_err(void *arg, err_t err)
+{
+ struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+ if (conn) {
+ conn->inner_conn = NULL; /* already freed */
+ if (conn->err) {
+ conn->err(conn->arg, err);
+ }
+ altcp_free(conn);
+ }
+}
+
+/* setup functions */
+
+static void
+altcp_mbedtls_remove_callbacks(struct altcp_pcb *inner_conn)
+{
+ altcp_arg(inner_conn, NULL);
+ altcp_recv(inner_conn, NULL);
+ altcp_sent(inner_conn, NULL);
+ altcp_err(inner_conn, NULL);
+ altcp_poll(inner_conn, NULL, inner_conn->pollinterval);
+}
+
+static void
+altcp_mbedtls_setup_callbacks(struct altcp_pcb *conn, struct altcp_pcb *inner_conn)
+{
+ altcp_arg(inner_conn, conn);
+ altcp_recv(inner_conn, altcp_mbedtls_lower_recv);
+ altcp_sent(inner_conn, altcp_mbedtls_lower_sent);
+ altcp_err(inner_conn, altcp_mbedtls_lower_err);
+ /* tcp_poll is set when interval is set by application */
+ /* listen is set totally different :-) */
+}
+
+static err_t
+altcp_mbedtls_setup(void *conf, struct altcp_pcb *conn, struct altcp_pcb *inner_conn)
+{
+ int ret;
+ struct altcp_tls_config *config = (struct altcp_tls_config *)conf;
+ altcp_mbedtls_state_t *state;
+ if (!conf) {
+ return ERR_ARG;
+ }
+ LWIP_ASSERT("invalid inner_conn", conn != inner_conn);
+
+ /* allocate mbedtls context */
+ state = altcp_mbedtls_alloc(conf);
+ if (state == NULL) {
+ return ERR_MEM;
+ }
+ /* initialize mbedtls context: */
+ mbedtls_ssl_init(&state->ssl_context);
+ ret = mbedtls_ssl_setup(&state->ssl_context, &config->conf);
+ if (ret != 0) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_ssl_setup failed\n"));
+ /* @todo: convert 'ret' to err_t */
+ altcp_mbedtls_free(conf, state);
+ return ERR_MEM;
+ }
+ /* tell mbedtls about our I/O functions */
+ mbedtls_ssl_set_bio(&state->ssl_context, conn, altcp_mbedtls_bio_send, altcp_mbedtls_bio_recv, NULL);
+
+ altcp_mbedtls_setup_callbacks(conn, inner_conn);
+ conn->inner_conn = inner_conn;
+ conn->fns = &altcp_mbedtls_functions;
+ conn->state = state;
+ return ERR_OK;
+}
+
+struct altcp_pcb *
+altcp_tls_wrap(struct altcp_tls_config *config, struct altcp_pcb *inner_pcb)
+{
+ struct altcp_pcb *ret;
+ if (inner_pcb == NULL) {
+ return NULL;
+ }
+ ret = altcp_alloc();
+ if (ret != NULL) {
+ if (altcp_mbedtls_setup(config, ret, inner_pcb) != ERR_OK) {
+ altcp_free(ret);
+ return NULL;
+ }
+ }
+ return ret;
+}
+
+void *
+altcp_tls_context(struct altcp_pcb *conn)
+{
+ if (conn && conn->state) {
+ altcp_mbedtls_state_t *state = (altcp_mbedtls_state_t *)conn->state;
+ return &state->ssl_context;
+ }
+ return NULL;
+}
+
+#if ALTCP_MBEDTLS_DEBUG != LWIP_DBG_OFF
+static void
+altcp_mbedtls_debug(void *ctx, int level, const char *file, int line, const char *str)
+{
+ LWIP_UNUSED_ARG(ctx);
+ LWIP_UNUSED_ARG(level);
+ LWIP_UNUSED_ARG(file);
+ LWIP_UNUSED_ARG(line);
+ LWIP_UNUSED_ARG(str);
+
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("%s:%04d: %s", file, line, str));
+}
+#endif
+
+#ifndef ALTCP_MBEDTLS_RNG_FN
+/** ATTENTION: It is *really* important to *NOT* use this dummy RNG in production code!!!! */
+static int
+dummy_rng(void *ctx, unsigned char *buffer, size_t len)
+{
+ static size_t ctr;
+ size_t i;
+ LWIP_UNUSED_ARG(ctx);
+ for (i = 0; i < len; i++) {
+ buffer[i] = (unsigned char)++ctr;
+ }
+ return 0;
+}
+#define ALTCP_MBEDTLS_RNG_FN dummy_rng
+#endif /* ALTCP_MBEDTLS_RNG_FN */
+
+/** Create new TLS configuration
+ * ATTENTION: Server certificate and private key have to be added outside this function!
+ */
+static struct altcp_tls_config *
+altcp_tls_create_config(int is_server, int have_cert, int have_pkey, int have_ca)
+{
+ size_t sz;
+ int ret;
+ struct altcp_tls_config *conf;
+ mbedtls_x509_crt *mem;
+
+ if (TCP_WND < MBEDTLS_SSL_MAX_CONTENT_LEN) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG|LWIP_DBG_LEVEL_SERIOUS,
+ ("altcp_tls: TCP_WND is smaller than the RX decryption buffer, connection RX might stall!\n"));
+ }
+
+ altcp_mbedtls_mem_init();
+
+ sz = sizeof(struct altcp_tls_config);
+ if (have_cert) {
+ sz += sizeof(mbedtls_x509_crt);
+ }
+ if (have_ca) {
+ sz += sizeof(mbedtls_x509_crt);
+ }
+ if (have_pkey) {
+ sz += sizeof(mbedtls_pk_context);
+ }
+
+ conf = (struct altcp_tls_config *)altcp_mbedtls_alloc_config(sz);
+ if (conf == NULL) {
+ return NULL;
+ }
+ mem = (mbedtls_x509_crt *)(conf + 1);
+ if (have_cert) {
+ conf->cert = mem;
+ mem++;
+ }
+ if (have_ca) {
+ conf->ca = mem;
+ mem++;
+ }
+ if (have_pkey) {
+ conf->pkey = (mbedtls_pk_context *)mem;
+ }
+
+ mbedtls_ssl_config_init(&conf->conf);
+ mbedtls_entropy_init(&conf->entropy);
+ mbedtls_ctr_drbg_init(&conf->ctr_drbg);
+
+ /* Seed the RNG */
+ ret = mbedtls_ctr_drbg_seed(&conf->ctr_drbg, ALTCP_MBEDTLS_RNG_FN, &conf->entropy, ALTCP_MBEDTLS_ENTROPY_PTR, ALTCP_MBEDTLS_ENTROPY_LEN);
+ if (ret != 0) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_ctr_drbg_seed failed: %d\n", ret));
+ altcp_mbedtls_free_config(conf);
+ return NULL;
+ }
+
+ /* Setup ssl context (@todo: what's different for a client here? -> might better be done on listen/connect) */
+ ret = mbedtls_ssl_config_defaults(&conf->conf, is_server ? MBEDTLS_SSL_IS_SERVER : MBEDTLS_SSL_IS_CLIENT,
+ MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);
+ if (ret != 0) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_ssl_config_defaults failed: %d\n", ret));
+ altcp_mbedtls_free_config(conf);
+ return NULL;
+ }
+ mbedtls_ssl_conf_authmode(&conf->conf, MBEDTLS_SSL_VERIFY_OPTIONAL);
+
+ mbedtls_ssl_conf_rng(&conf->conf, mbedtls_ctr_drbg_random, &conf->ctr_drbg);
+#if ALTCP_MBEDTLS_DEBUG != LWIP_DBG_OFF
+ mbedtls_ssl_conf_dbg(&conf->conf, altcp_mbedtls_debug, stdout);
+#endif
+#if defined(MBEDTLS_SSL_CACHE_C) && ALTCP_MBEDTLS_SESSION_CACHE_TIMEOUT_SECONDS
+ mbedtls_ssl_conf_session_cache(&conf->conf, &conf->cache, mbedtls_ssl_cache_get, mbedtls_ssl_cache_set);
+ mbedtls_ssl_cache_set_timeout(&conf->cache, 30);
+ mbedtls_ssl_cache_set_max_entries(&conf->cache, 30);
+#endif
+
+ return conf;
+}
+
+/** Create new TLS configuration
+ * This is a suboptimal version that gets the encrypted private key and its password,
+ * as well as the server certificate.
+ */
+struct altcp_tls_config *
+altcp_tls_create_config_server_privkey_cert(const u8_t *privkey, size_t privkey_len,
+ const u8_t *privkey_pass, size_t privkey_pass_len,
+ const u8_t *cert, size_t cert_len)
+{
+ int ret;
+ mbedtls_x509_crt *srvcert;
+ mbedtls_pk_context *pkey;
+ struct altcp_tls_config *conf = altcp_tls_create_config(1, 1, 1, 0);
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ srvcert = conf->cert;
+ mbedtls_x509_crt_init(srvcert);
+
+ pkey = conf->pkey;
+ mbedtls_pk_init(pkey);
+
+ /* Load the certificates and private key */
+ ret = mbedtls_x509_crt_parse(srvcert, cert, cert_len);
+ if (ret != 0) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_x509_crt_parse failed: %d\n", ret));
+ altcp_mbedtls_free_config(conf);
+ return NULL;
+ }
+
+ ret = mbedtls_pk_parse_key(pkey, (const unsigned char *) privkey, privkey_len, privkey_pass, privkey_pass_len);
+ if (ret != 0) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_pk_parse_public_key failed: %d\n", ret));
+ mbedtls_x509_crt_free(srvcert);
+ altcp_mbedtls_free_config(conf);
+ return NULL;
+ }
+
+ mbedtls_ssl_conf_ca_chain(&conf->conf, srvcert->next, NULL);
+ ret = mbedtls_ssl_conf_own_cert(&conf->conf, srvcert, pkey);
+ if (ret != 0) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_ssl_conf_own_cert failed: %d\n", ret));
+ mbedtls_x509_crt_free(srvcert);
+ mbedtls_pk_free(pkey);
+ altcp_mbedtls_free_config(conf);
+ return NULL;
+ }
+ return conf;
+}
+
+static struct altcp_tls_config *
+altcp_tls_create_config_client_common(const u8_t *ca, size_t ca_len, int is_2wayauth)
+{
+ int ret;
+ struct altcp_tls_config *conf = altcp_tls_create_config(0, is_2wayauth, is_2wayauth, ca != NULL);
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ /* Initialize the CA certificate if provided
+ * CA certificate is optional (to save memory) but recommended for production environment
+ * Without CA certificate, connection will be prone to man-in-the-middle attacks */
+ if (ca) {
+ mbedtls_x509_crt_init(conf->ca);
+ ret = mbedtls_x509_crt_parse(conf->ca, ca, ca_len);
+ if (ret != 0) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_x509_crt_parse ca failed: %d 0x%x", ret, -1*ret));
+ altcp_mbedtls_free_config(conf);
+ return NULL;
+ }
+
+ mbedtls_ssl_conf_ca_chain(&conf->conf, conf->ca, NULL);
+ }
+ return conf;
+}
+
+struct altcp_tls_config *
+altcp_tls_create_config_client(const u8_t *ca, size_t ca_len)
+{
+ return altcp_tls_create_config_client_common(ca, ca_len, 0);
+}
+
+struct altcp_tls_config *
+altcp_tls_create_config_client_2wayauth(const u8_t *ca, size_t ca_len, const u8_t *privkey, size_t privkey_len,
+ const u8_t *privkey_pass, size_t privkey_pass_len,
+ const u8_t *cert, size_t cert_len)
+{
+ int ret;
+ struct altcp_tls_config *conf;
+
+ if (!cert || !privkey) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("altcp_tls_create_config_client_2wayauth: certificate and priv key required"));
+ return NULL;
+ }
+
+ conf = altcp_tls_create_config_client_common(ca, ca_len, 1);
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ /* Initialize the client certificate and corresponding private key */
+ mbedtls_x509_crt_init(conf->cert);
+ ret = mbedtls_x509_crt_parse(conf->cert, cert, cert_len);
+ if (ret != 0) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_x509_crt_parse cert failed: %d 0x%x", ret, -1*ret));
+ altcp_mbedtls_free_config(conf->cert);
+ return NULL;
+ }
+
+ mbedtls_pk_init(conf->pkey);
+ ret = mbedtls_pk_parse_key(conf->pkey, privkey, privkey_len, privkey_pass, privkey_pass_len);
+ if (ret != 0) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_pk_parse_key failed: %d 0x%x", ret, -1*ret));
+ altcp_mbedtls_free_config(conf);
+ return NULL;
+ }
+
+ ret = mbedtls_ssl_conf_own_cert(&conf->conf, conf->cert, conf->pkey);
+ if (ret != 0) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_ssl_conf_own_cert failed: %d 0x%x", ret, -1*ret));
+ altcp_mbedtls_free_config(conf);
+ return NULL;
+ }
+
+ return conf;
+}
+
+void
+altcp_tls_free_config(struct altcp_tls_config *conf)
+{
+ if (conf->pkey) {
+ mbedtls_pk_free(conf->pkey);
+ }
+ if (conf->cert) {
+ mbedtls_x509_crt_free(conf->cert);
+ }
+ if (conf->ca) {
+ mbedtls_x509_crt_free(conf->ca);
+ }
+ altcp_mbedtls_free_config(conf);
+}
+
+/* "virtual" functions */
+static void
+altcp_mbedtls_set_poll(struct altcp_pcb *conn, u8_t interval)
+{
+ if (conn != NULL) {
+ altcp_poll(conn->inner_conn, altcp_mbedtls_lower_poll, interval);
+ }
+}
+
+static void
+altcp_mbedtls_recved(struct altcp_pcb *conn, u16_t len)
+{
+ u16_t lower_recved;
+ altcp_mbedtls_state_t *state;
+ if (conn == NULL) {
+ return;
+ }
+ state = (altcp_mbedtls_state_t *)conn->state;
+ if (state == NULL) {
+ return;
+ }
+ if (!(state->flags & ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE)) {
+ return;
+ }
+ lower_recved = len;
+ if (lower_recved > state->rx_passed_unrecved) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("bogus recved count (len > state->rx_passed_unrecved / %d / %d)",
+ len, state->rx_passed_unrecved));
+ lower_recved = (u16_t)state->rx_passed_unrecved;
+ }
+ state->rx_passed_unrecved -= lower_recved;
+
+ altcp_recved(conn->inner_conn, lower_recved);
+}
+
+static err_t
+altcp_mbedtls_connect(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port, altcp_connected_fn connected)
+{
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+ conn->connected = connected;
+ return altcp_connect(conn->inner_conn, ipaddr, port, altcp_mbedtls_lower_connected);
+}
+
+static struct altcp_pcb *
+altcp_mbedtls_listen(struct altcp_pcb *conn, u8_t backlog, err_t *err)
+{
+ struct altcp_pcb *lpcb;
+ if (conn == NULL) {
+ return NULL;
+ }
+ lpcb = altcp_listen_with_backlog_and_err(conn->inner_conn, backlog, err);
+ if (lpcb != NULL) {
+ conn->inner_conn = lpcb;
+ altcp_accept(lpcb, altcp_mbedtls_lower_accept);
+ return conn;
+ }
+ return NULL;
+}
+
+static void
+altcp_mbedtls_abort(struct altcp_pcb *conn)
+{
+ if (conn != NULL) {
+ altcp_abort(conn->inner_conn);
+ }
+}
+
+static err_t
+altcp_mbedtls_close(struct altcp_pcb *conn)
+{
+ struct altcp_pcb *inner_conn;
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+ inner_conn = conn->inner_conn;
+ if (inner_conn) {
+ err_t err;
+ altcp_poll_fn oldpoll = inner_conn->poll;
+ altcp_mbedtls_remove_callbacks(conn->inner_conn);
+ err = altcp_close(conn->inner_conn);
+ if (err != ERR_OK) {
+ /* not closed, set up all callbacks again */
+ altcp_mbedtls_setup_callbacks(conn, inner_conn);
+ /* poll callback is not included in the above */
+ altcp_poll(inner_conn, oldpoll, inner_conn->pollinterval);
+ return err;
+ }
+ conn->inner_conn = NULL;
+ }
+ altcp_free(conn);
+ return ERR_OK;
+}
+
+/** Allow caller of altcp_write() to limit to negotiated chunk size
+ * or remaining sndbuf space of inner_conn.
+ */
+static u16_t
+altcp_mbedtls_sndbuf(struct altcp_pcb *conn)
+{
+ if (conn) {
+ altcp_mbedtls_state_t *state;
+ state = (altcp_mbedtls_state_t*)conn->state;
+ if (!state || !(state->flags & ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE)) {
+ return 0;
+ }
+ if (conn->inner_conn) {
+ u16_t sndbuf = altcp_sndbuf(conn->inner_conn);
+ /* Take care of record header, IV, AuthTag */
+ int ssl_expan = mbedtls_ssl_get_record_expansion(&state->ssl_context);
+ if (ssl_expan > 0) {
+ size_t ssl_added = (u16_t)LWIP_MIN(ssl_expan, 0xFFFF);
+ /* internal sndbuf smaller than our offset */
+ if (ssl_added < sndbuf) {
+ size_t max_len = 0xFFFF;
+ size_t ret;
+#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH)
+ /* @todo: adjust ssl_added to real value related to negociated cipher */
+ size_t max_frag_len = mbedtls_ssl_get_max_frag_len(&state->ssl_context);
+ max_len = LWIP_MIN(max_frag_len, max_len);
+#endif
+ /* Adjust sndbuf of inner_conn with what added by SSL */
+ ret = LWIP_MIN(sndbuf - ssl_added, max_len);
+ LWIP_ASSERT("sndbuf overflow", ret <= 0xFFFF);
+ return (u16_t)ret;
+ }
+ }
+ }
+ }
+ /* fallback: use sendbuf of the inner connection */
+ return altcp_default_sndbuf(conn);
+}
+
+/** Write data to a TLS connection. Calls into mbedTLS, which in turn calls into
+ * @ref altcp_mbedtls_bio_send() to send the encrypted data
+ */
+static err_t
+altcp_mbedtls_write(struct altcp_pcb *conn, const void *dataptr, u16_t len, u8_t apiflags)
+{
+ int ret;
+ altcp_mbedtls_state_t *state;
+
+ LWIP_UNUSED_ARG(apiflags);
+
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+
+ state = (altcp_mbedtls_state_t *)conn->state;
+ if (state == NULL) {
+ /* @todo: which error? */
+ return ERR_CLSD;
+ }
+ if (!(state->flags & ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE)) {
+ /* @todo: which error? */
+ return ERR_VAL;
+ }
+
+ /* HACK: if thre is something left to send, try to flush it and only
+ allow sending more if this succeeded (this is a hack because neither
+ returning 0 nor MBEDTLS_ERR_SSL_WANT_WRITE worked for me) */
+ if (state->ssl_context.out_left) {
+ mbedtls_ssl_flush_output(&state->ssl_context);
+ if (state->ssl_context.out_left) {
+ return ERR_MEM;
+ }
+ }
+ ret = mbedtls_ssl_write(&state->ssl_context, (const unsigned char *)dataptr, len);
+ /* try to send data... */
+ altcp_output(conn->inner_conn);
+ if (ret >= 0) {
+ if (ret == len) {
+ state->flags |= ALTCP_MBEDTLS_FLAGS_APPLDATA_SENT;
+ return ERR_OK;
+ } else {
+ /* @todo/@fixme: assumption: either everything sent or error */
+ LWIP_ASSERT("ret <= 0", 0);
+ return ERR_MEM;
+ }
+ } else {
+ if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
+ /* @todo: convert error to err_t */
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("unhandled error", 0);
+ return ERR_VAL;
+ }
+}
+
+/** Send callback function called from mbedtls (set via mbedtls_ssl_set_bio)
+ * This function is either called during handshake or when sending application
+ * data via @ref altcp_mbedtls_write (or altcp_write)
+ */
+static int
+altcp_mbedtls_bio_send(void *ctx, const unsigned char *dataptr, size_t size)
+{
+ struct altcp_pcb *conn = (struct altcp_pcb *) ctx;
+ int written = 0;
+ size_t size_left = size;
+ u8_t apiflags = TCP_WRITE_FLAG_COPY;
+
+ LWIP_ASSERT("conn != NULL", conn != NULL);
+ if ((conn == NULL) || (conn->inner_conn == NULL)) {
+ return MBEDTLS_ERR_NET_INVALID_CONTEXT;
+ }
+
+ while (size_left) {
+ u16_t write_len = (u16_t)LWIP_MIN(size_left, 0xFFFF);
+ err_t err = altcp_write(conn->inner_conn, (const void *)dataptr, write_len, apiflags);
+ if (err == ERR_OK) {
+ written += write_len;
+ size_left -= write_len;
+ } else if (err == ERR_MEM) {
+ if (written) {
+ return written;
+ }
+ return 0; /* MBEDTLS_ERR_SSL_WANT_WRITE; */
+ } else {
+ LWIP_ASSERT("tls_write, tcp_write: err != ERR MEM", 0);
+ /* @todo: return MBEDTLS_ERR_NET_CONN_RESET or MBEDTLS_ERR_NET_SEND_FAILED */
+ return MBEDTLS_ERR_NET_SEND_FAILED;
+ }
+ }
+ return written;
+}
+
+static u16_t
+altcp_mbedtls_mss(struct altcp_pcb *conn)
+{
+ if (conn == NULL) {
+ return 0;
+ }
+ /* @todo: LWIP_MIN(mss, mbedtls_ssl_get_max_frag_len()) ? */
+ return altcp_mss(conn->inner_conn);
+}
+
+static void
+altcp_mbedtls_dealloc(struct altcp_pcb *conn)
+{
+ /* clean up and free tls state */
+ if (conn) {
+ altcp_mbedtls_state_t *state = (altcp_mbedtls_state_t *)conn->state;
+ if (state) {
+ mbedtls_ssl_free(&state->ssl_context);
+ state->flags = 0;
+ if (state->rx) {
+ /* free leftover (unhandled) rx pbufs */
+ pbuf_free(state->rx);
+ state->rx = NULL;
+ }
+ altcp_mbedtls_free(state->conf, state);
+ conn->state = NULL;
+ }
+ }
+}
+
+const struct altcp_functions altcp_mbedtls_functions = {
+ altcp_mbedtls_set_poll,
+ altcp_mbedtls_recved,
+ altcp_default_bind,
+ altcp_mbedtls_connect,
+ altcp_mbedtls_listen,
+ altcp_mbedtls_abort,
+ altcp_mbedtls_close,
+ altcp_default_shutdown,
+ altcp_mbedtls_write,
+ altcp_default_output,
+ altcp_mbedtls_mss,
+ altcp_mbedtls_sndbuf,
+ altcp_default_sndqueuelen,
+ altcp_default_nagle_disable,
+ altcp_default_nagle_enable,
+ altcp_default_nagle_disabled,
+ altcp_default_setprio,
+ altcp_mbedtls_dealloc,
+ altcp_default_get_tcp_addrinfo,
+ altcp_default_get_ip,
+ altcp_default_get_port
+#ifdef LWIP_DEBUG
+ , altcp_default_dbg_get_tcp_state
+#endif
+};
+
+#endif /* LWIP_ALTCP_TLS && LWIP_ALTCP_TLS_MBEDTLS */
+#endif /* LWIP_ALTCP */
diff --git a/resources/ethernet/LwIP/apps/altcp_tls/altcp_tls_mbedtls_mem.c b/resources/ethernet/LwIP/apps/altcp_tls/altcp_tls_mbedtls_mem.c
new file mode 100644
index 00000000..664347f2
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/altcp_tls/altcp_tls_mbedtls_mem.c
@@ -0,0 +1,210 @@
+/**
+ * @file
+ * Application layered TCP connection API (to be used from TCPIP thread)
+ *
+ * This file contains memory management functions for a TLS layer using mbedTLS.
+ *
+ * ATTENTION: For production usage, you might want to override this file with
+ * your own implementation since this implementation simply uses the
+ * lwIP heap without caring for fragmentation or leaving heap for
+ * other parts of lwIP!
+ */
+
+/*
+ * Copyright (c) 2017 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ *
+ * Missing things / @todo:
+ * - RX data is acknowledged after receiving (tcp_recved is called when enqueueing
+ * the pbuf for mbedTLS receive, not when processed by mbedTLS or the inner
+ * connection; altcp_recved() from inner connection does nothing)
+ * - TX data is marked as 'sent' (i.e. acknowledged; sent callback is called) right
+ * after enqueueing for transmission, not when actually ACKed be the remote host.
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/altcp_tls_mbedtls_opts.h"
+
+#if LWIP_ALTCP_TLS && LWIP_ALTCP_TLS_MBEDTLS
+
+#include "altcp_tls_mbedtls_mem.h"
+#include "altcp_tls_mbedtls_structs.h"
+#include "lwip/mem.h"
+
+#include "mbedtls/platform.h"
+
+#include
+
+#ifndef ALTCP_MBEDTLS_MEM_DEBUG
+#define ALTCP_MBEDTLS_MEM_DEBUG LWIP_DBG_OFF
+#endif
+
+#if defined(MBEDTLS_PLATFORM_MEMORY) && \
+ (!defined(MBEDTLS_PLATFORM_FREE_MACRO) || \
+ defined(MBEDTLS_PLATFORM_CALLOC_MACRO))
+#define ALTCP_MBEDTLS_PLATFORM_ALLOC 1
+#else
+#define ALTCP_MBEDTLS_PLATFORM_ALLOC 0
+#endif
+
+#if ALTCP_MBEDTLS_PLATFORM_ALLOC
+
+#ifndef ALTCP_MBEDTLS_PLATFORM_ALLOC_STATS
+#define ALTCP_MBEDTLS_PLATFORM_ALLOC_STATS 0
+#endif
+
+/* This is an example/debug implementation of alloc/free functions only */
+typedef struct altcp_mbedtls_malloc_helper_s {
+ size_t c;
+ size_t len;
+} altcp_mbedtls_malloc_helper_t;
+
+#if ALTCP_MBEDTLS_PLATFORM_ALLOC_STATS
+typedef struct altcp_mbedtls_malloc_stats_s {
+ size_t allocedBytes;
+ size_t allocCnt;
+ size_t maxBytes;
+ size_t totalBytes;
+} altcp_mbedtls_malloc_stats_t;
+altcp_mbedtls_malloc_stats_t altcp_mbedtls_malloc_stats;
+volatile int altcp_mbedtls_malloc_clear_stats;
+#endif
+
+static void *
+tls_malloc(size_t c, size_t len)
+{
+ altcp_mbedtls_malloc_helper_t *hlpr;
+ void *ret;
+ size_t alloc_size;
+#if ALTCP_MBEDTLS_PLATFORM_ALLOC_STATS
+ if (altcp_mbedtls_malloc_clear_stats) {
+ altcp_mbedtls_malloc_clear_stats = 0;
+ memset(&altcp_mbedtls_malloc_stats, 0, sizeof(altcp_mbedtls_malloc_stats));
+ }
+#endif
+ alloc_size = sizeof(altcp_mbedtls_malloc_helper_t) + (c * len);
+ /* check for maximum allocation size, mainly to prevent mem_size_t overflow */
+ if (alloc_size > MEM_SIZE) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_MEM_DEBUG, ("mbedtls allocation too big: %c * %d bytes vs MEM_SIZE=%d",
+ (int)c, (int)len, (int)MEM_SIZE));
+ return NULL;
+ }
+ hlpr = (altcp_mbedtls_malloc_helper_t *)mem_malloc((mem_size_t)alloc_size);
+ if (hlpr == NULL) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_MEM_DEBUG, ("mbedtls alloc callback failed for %c * %d bytes", (int)c, (int)len));
+ return NULL;
+ }
+#if ALTCP_MBEDTLS_PLATFORM_ALLOC_STATS
+ altcp_mbedtls_malloc_stats.allocCnt++;
+ altcp_mbedtls_malloc_stats.allocedBytes += c * len;
+ if (altcp_mbedtls_malloc_stats.allocedBytes > altcp_mbedtls_malloc_stats.maxBytes) {
+ altcp_mbedtls_malloc_stats.maxBytes = altcp_mbedtls_malloc_stats.allocedBytes;
+ }
+ altcp_mbedtls_malloc_stats.totalBytes += c * len;
+#endif
+ hlpr->c = c;
+ hlpr->len = len;
+ ret = hlpr + 1;
+ /* zeroing the allocated chunk is required by mbedTLS! */
+ memset(ret, 0, c * len);
+ return ret;
+}
+
+static void
+tls_free(void *ptr)
+{
+ altcp_mbedtls_malloc_helper_t *hlpr;
+ if (ptr == NULL) {
+ /* this obviously happened in mbedtls... */
+ return;
+ }
+ hlpr = ((altcp_mbedtls_malloc_helper_t *)ptr) - 1;
+#if ALTCP_MBEDTLS_PLATFORM_ALLOC_STATS
+ if (!altcp_mbedtls_malloc_clear_stats) {
+ altcp_mbedtls_malloc_stats.allocedBytes -= hlpr->c * hlpr->len;
+ }
+#endif
+ mem_free(hlpr);
+}
+#endif /* ALTCP_MBEDTLS_PLATFORM_ALLOC*/
+
+void
+altcp_mbedtls_mem_init(void)
+{
+ /* not much to do here when using the heap */
+
+#if ALTCP_MBEDTLS_PLATFORM_ALLOC
+ /* set mbedtls allocation methods */
+ mbedtls_platform_set_calloc_free(&tls_malloc, &tls_free);
+#endif
+}
+
+altcp_mbedtls_state_t *
+altcp_mbedtls_alloc(void *conf)
+{
+ altcp_mbedtls_state_t *ret = (altcp_mbedtls_state_t *)mem_calloc(1, sizeof(altcp_mbedtls_state_t));
+ if (ret != NULL) {
+ ret->conf = conf;
+ }
+ return ret;
+}
+
+void
+altcp_mbedtls_free(void *conf, altcp_mbedtls_state_t *state)
+{
+ LWIP_UNUSED_ARG(conf);
+ LWIP_ASSERT("state != NULL", state != NULL);
+ mem_free(state);
+}
+
+void *
+altcp_mbedtls_alloc_config(size_t size)
+{
+ void *ret;
+ size_t checked_size = (mem_size_t)size;
+ if (size != checked_size) {
+ /* allocation too big (mem_size_t overflow) */
+ return NULL;
+ }
+ ret = (altcp_mbedtls_state_t *)mem_calloc(1, (mem_size_t)size);
+ return ret;
+}
+
+void
+altcp_mbedtls_free_config(void *item)
+{
+ LWIP_ASSERT("item != NULL", item != NULL);
+ mem_free(item);
+}
+
+#endif /* LWIP_ALTCP_TLS && LWIP_ALTCP_TLS_MBEDTLS */
+#endif /* LWIP_ALTCP */
diff --git a/resources/ethernet/LwIP/apps/altcp_tls/altcp_tls_mbedtls_mem.h b/resources/ethernet/LwIP/apps/altcp_tls/altcp_tls_mbedtls_mem.h
new file mode 100644
index 00000000..8458193a
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/altcp_tls/altcp_tls_mbedtls_mem.h
@@ -0,0 +1,72 @@
+/**
+ * @file
+ * Application layered TCP/TLS connection API (to be used from TCPIP thread)
+ *
+ * This file contains memory management function prototypes for a TLS layer using mbedTLS.
+ *
+ * Memory management contains:
+ * - allocating/freeing altcp_mbedtls_state_t
+ * - allocating/freeing memory used in the mbedTLS library
+ */
+
+/*
+ * Copyright (c) 2017 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ *
+ */
+#ifndef LWIP_HDR_ALTCP_MBEDTLS_MEM_H
+#define LWIP_HDR_ALTCP_MBEDTLS_MEM_H
+
+#include "lwip/opt.h"
+
+#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/altcp_tls_mbedtls_opts.h"
+
+#if LWIP_ALTCP_TLS && LWIP_ALTCP_TLS_MBEDTLS
+
+#include "altcp_tls_mbedtls_structs.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void altcp_mbedtls_mem_init(void);
+altcp_mbedtls_state_t *altcp_mbedtls_alloc(void *conf);
+void altcp_mbedtls_free(void *conf, altcp_mbedtls_state_t *state);
+void *altcp_mbedtls_alloc_config(size_t size);
+void altcp_mbedtls_free_config(void *item);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_ALTCP_TLS && LWIP_ALTCP_TLS_MBEDTLS */
+#endif /* LWIP_ALTCP */
+#endif /* LWIP_HDR_ALTCP_MBEDTLS_MEM_H */
diff --git a/resources/ethernet/LwIP/apps/altcp_tls/altcp_tls_mbedtls_structs.h b/resources/ethernet/LwIP/apps/altcp_tls/altcp_tls_mbedtls_structs.h
new file mode 100644
index 00000000..02a756d9
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/altcp_tls/altcp_tls_mbedtls_structs.h
@@ -0,0 +1,83 @@
+/**
+ * @file
+ * Application layered TCP/TLS connection API (to be used from TCPIP thread)
+ *
+ * This file contains structure definitions for a TLS layer using mbedTLS.
+ */
+
+/*
+ * Copyright (c) 2017 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ *
+ */
+#ifndef LWIP_HDR_ALTCP_MBEDTLS_STRUCTS_H
+#define LWIP_HDR_ALTCP_MBEDTLS_STRUCTS_H
+
+#include "lwip/opt.h"
+
+#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/altcp_tls_mbedtls_opts.h"
+
+#if LWIP_ALTCP_TLS && LWIP_ALTCP_TLS_MBEDTLS
+
+#include "lwip/altcp.h"
+#include "lwip/pbuf.h"
+
+#include "mbedtls/ssl.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE 0x01
+#define ALTCP_MBEDTLS_FLAGS_UPPER_CALLED 0x02
+#define ALTCP_MBEDTLS_FLAGS_RX_CLOSE_QUEUED 0x04
+#define ALTCP_MBEDTLS_FLAGS_RX_CLOSED 0x08
+#define ALTCP_MBEDTLS_FLAGS_APPLDATA_SENT 0x10
+
+typedef struct altcp_mbedtls_state_s {
+ void *conf;
+ mbedtls_ssl_context ssl_context;
+ /* chain of rx pbufs (before decryption) */
+ struct pbuf *rx;
+ struct pbuf *rx_app;
+ u8_t flags;
+ int rx_passed_unrecved;
+ int bio_bytes_read;
+ int bio_bytes_appl;
+} altcp_mbedtls_state_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_ALTCP_TLS && LWIP_ALTCP_TLS_MBEDTLS */
+#endif /* LWIP_ALTCP */
+#endif /* LWIP_HDR_ALTCP_MBEDTLS_STRUCTS_H */
diff --git a/resources/ethernet/LwIP/apps/http/altcp_proxyconnect.c b/resources/ethernet/LwIP/apps/http/altcp_proxyconnect.c
new file mode 100644
index 00000000..55d99c49
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/http/altcp_proxyconnect.c
@@ -0,0 +1,584 @@
+/**
+ * @file
+ * Application layered TCP connection API that executes a proxy-connect.
+ *
+ * This file provides a starting layer that executes a proxy-connect e.g. to
+ * set up TLS connections through a http proxy.
+ */
+
+/*
+ * Copyright (c) 2018 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ *
+ */
+
+#include "lwip/apps/altcp_proxyconnect.h"
+
+#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/altcp.h"
+#include "lwip/priv/altcp_priv.h"
+
+#include "lwip/altcp_tcp.h"
+#include "lwip/altcp_tls.h"
+
+#include "lwip/mem.h"
+#include "lwip/init.h"
+
+#include
+
+/** This string is passed in the HTTP header as "User-Agent: " */
+#ifndef ALTCP_PROXYCONNECT_CLIENT_AGENT
+#define ALTCP_PROXYCONNECT_CLIENT_AGENT "lwIP/" LWIP_VERSION_STRING " (http://savannah.nongnu.org/projects/lwip)"
+#endif
+
+#define ALTCP_PROXYCONNECT_FLAGS_CONNECT_STARTED 0x01
+#define ALTCP_PROXYCONNECT_FLAGS_HANDSHAKE_DONE 0x02
+
+typedef struct altcp_proxyconnect_state_s
+{
+ ip_addr_t outer_addr;
+ u16_t outer_port;
+ struct altcp_proxyconnect_config *conf;
+ u8_t flags;
+} altcp_proxyconnect_state_t;
+
+/* Variable prototype, the actual declaration is at the end of this file
+ since it contains pointers to static functions declared here */
+extern const struct altcp_functions altcp_proxyconnect_functions;
+
+/* memory management functions: */
+
+static altcp_proxyconnect_state_t *
+altcp_proxyconnect_state_alloc(void)
+{
+ altcp_proxyconnect_state_t *ret = (altcp_proxyconnect_state_t *)mem_calloc(1, sizeof(altcp_proxyconnect_state_t));
+ return ret;
+}
+
+static void
+altcp_proxyconnect_state_free(altcp_proxyconnect_state_t *state)
+{
+ LWIP_ASSERT("state != NULL", state != NULL);
+ mem_free(state);
+}
+
+/* helper functions */
+
+#define PROXY_CONNECT "CONNECT %s:%d HTTP/1.1\r\n" /* HOST, PORT */ \
+ "User-Agent: %s\r\n" /* User-Agent */\
+ "Proxy-Connection: keep-alive\r\n" \
+ "Connection: keep-alive\r\n" \
+ "\r\n"
+#define PROXY_CONNECT_FORMAT(host, port) PROXY_CONNECT, host, port, ALTCP_PROXYCONNECT_CLIENT_AGENT
+
+/* Format the http proxy connect request via snprintf */
+static int
+altcp_proxyconnect_format_request(char *buffer, size_t bufsize, const char *host, int port)
+{
+ return snprintf(buffer, bufsize, PROXY_CONNECT_FORMAT(host, port));
+}
+
+/* Create and send the http proxy connect request */
+static err_t
+altcp_proxyconnect_send_request(struct altcp_pcb *conn)
+{
+ int len, len2;
+ mem_size_t alloc_len;
+ char *buffer, *host;
+ altcp_proxyconnect_state_t *state = (altcp_proxyconnect_state_t *)conn->state;
+
+ if (!state) {
+ return ERR_VAL;
+ }
+ /* Use printf with zero length to get the required allocation size */
+ len = altcp_proxyconnect_format_request(NULL, 0, "", state->outer_port);
+ if (len < 0) {
+ return ERR_VAL;
+ }
+ /* add allocation size for IP address strings */
+#if LWIP_IPV6
+ len += 40; /* worst-case IPv6 address length */
+#else
+ len += 16; /* worst-case IPv4 address length */
+#endif
+ alloc_len = (mem_size_t)len;
+ if ((len < 0) || (int)alloc_len != len) {
+ /* overflow */
+ return ERR_MEM;
+ }
+ /* Allocate a bufer for the request string */
+ buffer = (char *)mem_malloc(alloc_len);
+ if (buffer == NULL) {
+ return ERR_MEM;
+ }
+ host = ipaddr_ntoa(&state->outer_addr);
+ len2 = altcp_proxyconnect_format_request(buffer, alloc_len, host, state->outer_port);
+ if ((len2 > 0) && (len2 <= len) && (len2 <= 0xFFFF)) {
+ err_t err = altcp_write(conn->inner_conn, buffer, (u16_t)len2, TCP_WRITE_FLAG_COPY);
+ if (err != ERR_OK) {
+ /* @todo: abort? */
+ mem_free(buffer);
+ return err;
+ }
+ }
+ mem_free(buffer);
+ return ERR_OK;
+}
+
+/* callback functions from inner/lower connection: */
+
+/** Connected callback from lower connection (i.e. TCP).
+ * Not really implemented/tested yet...
+ */
+static err_t
+altcp_proxyconnect_lower_connected(void *arg, struct altcp_pcb *inner_conn, err_t err)
+{
+ struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+ if (conn && conn->state) {
+ LWIP_ASSERT("pcb mismatch", conn->inner_conn == inner_conn);
+ LWIP_UNUSED_ARG(inner_conn); /* for LWIP_NOASSERT */
+ /* upper connected is called when handshake is done */
+ if (err != ERR_OK) {
+ if (conn->connected) {
+ if (conn->connected(conn->arg, conn, err) == ERR_ABRT) {
+ return ERR_ABRT;
+ }
+ return ERR_OK;
+ }
+ }
+ /* send proxy connect request here */
+ return altcp_proxyconnect_send_request(conn);
+ }
+ return ERR_VAL;
+}
+
+/** Recv callback from lower connection (i.e. TCP)
+ * This one mainly differs between connection setup (wait for proxy OK string)
+ * and application phase (data is passed on to the application).
+ */
+static err_t
+altcp_proxyconnect_lower_recv(void *arg, struct altcp_pcb *inner_conn, struct pbuf *p, err_t err)
+{
+ altcp_proxyconnect_state_t *state;
+ struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+
+ LWIP_ASSERT("no err expected", err == ERR_OK);
+ LWIP_UNUSED_ARG(err);
+
+ if (!conn) {
+ /* no connection given as arg? should not happen, but prevent pbuf/conn leaks */
+ if (p != NULL) {
+ pbuf_free(p);
+ }
+ altcp_close(inner_conn);
+ return ERR_CLSD;
+ }
+ state = (altcp_proxyconnect_state_t *)conn->state;
+ LWIP_ASSERT("pcb mismatch", conn->inner_conn == inner_conn);
+ if (!state) {
+ /* already closed */
+ if (p != NULL) {
+ pbuf_free(p);
+ }
+ altcp_close(inner_conn);
+ return ERR_CLSD;
+ }
+ if (state->flags & ALTCP_PROXYCONNECT_FLAGS_HANDSHAKE_DONE) {
+ /* application phase, just pass this through */
+ if (conn->recv) {
+ return conn->recv(conn->arg, conn, p, err);
+ }
+ pbuf_free(p);
+ return ERR_OK;
+ } else {
+ /* setup phase */
+ /* handle NULL pbuf (inner connection closed) */
+ if (p == NULL) {
+ if (altcp_close(conn) != ERR_OK) {
+ altcp_abort(conn);
+ return ERR_ABRT;
+ }
+ return ERR_OK;
+ } else {
+ /* @todo: parse setup phase rx data
+ for now, we just wait for the end of the header... */
+ u16_t idx = pbuf_memfind(p, "\r\n\r\n", 4, 0);
+ altcp_recved(inner_conn, p->tot_len);
+ pbuf_free(p);
+ if (idx != 0xFFFF) {
+ state->flags |= ALTCP_PROXYCONNECT_FLAGS_HANDSHAKE_DONE;
+ if (conn->connected) {
+ return conn->connected(conn->arg, conn, ERR_OK);
+ }
+ }
+ return ERR_OK;
+ }
+ }
+}
+
+/** Sent callback from lower connection (i.e. TCP)
+ * This only informs the upper layer to try to send more, not about
+ * the number of ACKed bytes.
+ */
+static err_t
+altcp_proxyconnect_lower_sent(void *arg, struct altcp_pcb *inner_conn, u16_t len)
+{
+ struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+ LWIP_UNUSED_ARG(len);
+ if (conn) {
+ altcp_proxyconnect_state_t *state = (altcp_proxyconnect_state_t *)conn->state;
+ LWIP_ASSERT("pcb mismatch", conn->inner_conn == inner_conn);
+ LWIP_UNUSED_ARG(inner_conn); /* for LWIP_NOASSERT */
+ if (!state || !(state->flags & ALTCP_PROXYCONNECT_FLAGS_HANDSHAKE_DONE)) {
+ /* @todo: do something here? */
+ return ERR_OK;
+ }
+ /* pass this on to upper sent */
+ if (conn->sent) {
+ return conn->sent(conn->arg, conn, len);
+ }
+ }
+ return ERR_OK;
+}
+
+/** Poll callback from lower connection (i.e. TCP)
+ * Just pass this on to the application.
+ * @todo: retry sending?
+ */
+static err_t
+altcp_proxyconnect_lower_poll(void *arg, struct altcp_pcb *inner_conn)
+{
+ struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+ if (conn) {
+ LWIP_ASSERT("pcb mismatch", conn->inner_conn == inner_conn);
+ LWIP_UNUSED_ARG(inner_conn); /* for LWIP_NOASSERT */
+ if (conn->poll) {
+ return conn->poll(conn->arg, conn);
+ }
+ }
+ return ERR_OK;
+}
+
+static void
+altcp_proxyconnect_lower_err(void *arg, err_t err)
+{
+ struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+ if (conn) {
+ conn->inner_conn = NULL; /* already freed */
+ if (conn->err) {
+ conn->err(conn->arg, err);
+ }
+ altcp_free(conn);
+ }
+}
+
+
+/* setup functions */
+
+static void
+altcp_proxyconnect_setup_callbacks(struct altcp_pcb *conn, struct altcp_pcb *inner_conn)
+{
+ altcp_arg(inner_conn, conn);
+ altcp_recv(inner_conn, altcp_proxyconnect_lower_recv);
+ altcp_sent(inner_conn, altcp_proxyconnect_lower_sent);
+ altcp_err(inner_conn, altcp_proxyconnect_lower_err);
+ /* tcp_poll is set when interval is set by application */
+ /* listen is set totally different :-) */
+}
+
+static err_t
+altcp_proxyconnect_setup(struct altcp_proxyconnect_config *config, struct altcp_pcb *conn, struct altcp_pcb *inner_conn)
+{
+ altcp_proxyconnect_state_t *state;
+ if (!config) {
+ return ERR_ARG;
+ }
+ LWIP_ASSERT("invalid inner_conn", conn != inner_conn);
+
+ /* allocate proxyconnect context */
+ state = altcp_proxyconnect_state_alloc();
+ if (state == NULL) {
+ return ERR_MEM;
+ }
+ state->flags = 0;
+ state->conf = config;
+ altcp_proxyconnect_setup_callbacks(conn, inner_conn);
+ conn->inner_conn = inner_conn;
+ conn->fns = &altcp_proxyconnect_functions;
+ conn->state = state;
+ return ERR_OK;
+}
+
+/** Allocate a new altcp layer connecting through a proxy.
+ * This function gets the inner pcb passed.
+ *
+ * @param config struct altcp_proxyconnect_config that contains the proxy settings
+ * @param inner_pcb pcb that makes the connection to the proxy (i.e. tcp pcb)
+ */
+struct altcp_pcb *
+altcp_proxyconnect_new(struct altcp_proxyconnect_config *config, struct altcp_pcb *inner_pcb)
+{
+ struct altcp_pcb *ret;
+ if (inner_pcb == NULL) {
+ return NULL;
+ }
+ ret = altcp_alloc();
+ if (ret != NULL) {
+ if (altcp_proxyconnect_setup(config, ret, inner_pcb) != ERR_OK) {
+ altcp_free(ret);
+ return NULL;
+ }
+ }
+ return ret;
+}
+
+/** Allocate a new altcp layer connecting through a proxy.
+ * This function allocates the inner pcb as tcp pcb, resulting in a direct tcp
+ * connection to the proxy.
+ *
+ * @param config struct altcp_proxyconnect_config that contains the proxy settings
+ * @param ip_type IP type of the connection (@ref lwip_ip_addr_type)
+ */
+struct altcp_pcb *
+altcp_proxyconnect_new_tcp(struct altcp_proxyconnect_config *config, u8_t ip_type)
+{
+ struct altcp_pcb *inner_pcb, *ret;
+
+ /* inner pcb is tcp */
+ inner_pcb = altcp_tcp_new_ip_type(ip_type);
+ if (inner_pcb == NULL) {
+ return NULL;
+ }
+ ret = altcp_proxyconnect_new(config, inner_pcb);
+ if (ret == NULL) {
+ altcp_close(inner_pcb);
+ }
+ return ret;
+}
+
+/** Allocator function to allocate a proxy connect altcp pcb connecting directly
+ * via tcp to the proxy.
+ *
+ * The returned pcb is a chain: altcp_proxyconnect - altcp_tcp - tcp pcb
+ *
+ * This function is meant for use with @ref altcp_new.
+ *
+ * @param arg struct altcp_proxyconnect_config that contains the proxy settings
+ * @param ip_type IP type of the connection (@ref lwip_ip_addr_type)
+ */
+struct altcp_pcb *
+altcp_proxyconnect_alloc(void *arg, u8_t ip_type)
+{
+ return altcp_proxyconnect_new_tcp((struct altcp_proxyconnect_config *)arg, ip_type);
+}
+
+
+#if LWIP_ALTCP_TLS
+
+/** Allocator function to allocate a TLS connection through a proxy.
+ *
+ * The returned pcb is a chain: altcp_tls - altcp_proxyconnect - altcp_tcp - tcp pcb
+ *
+ * This function is meant for use with @ref altcp_new.
+ *
+ * @param arg struct altcp_proxyconnect_tls_config that contains the proxy settings
+ * and tls settings
+ * @param ip_type IP type of the connection (@ref lwip_ip_addr_type)
+ */
+struct altcp_pcb *
+altcp_proxyconnect_tls_alloc(void *arg, u8_t ip_type)
+{
+ struct altcp_proxyconnect_tls_config *cfg = (struct altcp_proxyconnect_tls_config *)arg;
+ struct altcp_pcb *proxy_pcb;
+ struct altcp_pcb *tls_pcb;
+
+ proxy_pcb = altcp_proxyconnect_new_tcp(&cfg->proxy, ip_type);
+ tls_pcb = altcp_tls_wrap(cfg->tls_config, proxy_pcb);
+
+ if (tls_pcb == NULL) {
+ altcp_close(proxy_pcb);
+ }
+ return tls_pcb;
+}
+#endif /* LWIP_ALTCP_TLS */
+
+/* "virtual" functions */
+static void
+altcp_proxyconnect_set_poll(struct altcp_pcb *conn, u8_t interval)
+{
+ if (conn != NULL) {
+ altcp_poll(conn->inner_conn, altcp_proxyconnect_lower_poll, interval);
+ }
+}
+
+static void
+altcp_proxyconnect_recved(struct altcp_pcb *conn, u16_t len)
+{
+ altcp_proxyconnect_state_t *state;
+ if (conn == NULL) {
+ return;
+ }
+ state = (altcp_proxyconnect_state_t *)conn->state;
+ if (state == NULL) {
+ return;
+ }
+ if (!(state->flags & ALTCP_PROXYCONNECT_FLAGS_HANDSHAKE_DONE)) {
+ return;
+ }
+ altcp_recved(conn->inner_conn, len);
+}
+
+static err_t
+altcp_proxyconnect_connect(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port, altcp_connected_fn connected)
+{
+ altcp_proxyconnect_state_t *state;
+
+ if ((conn == NULL) || (ipaddr == NULL)) {
+ return ERR_VAL;
+ }
+ state = (altcp_proxyconnect_state_t *)conn->state;
+ if (state == NULL) {
+ return ERR_VAL;
+ }
+ if (state->flags & ALTCP_PROXYCONNECT_FLAGS_CONNECT_STARTED) {
+ return ERR_VAL;
+ }
+ state->flags |= ALTCP_PROXYCONNECT_FLAGS_CONNECT_STARTED;
+
+ conn->connected = connected;
+ /* connect to our proxy instead, but store the requested address and port */
+ ip_addr_copy(state->outer_addr, *ipaddr);
+ state->outer_port = port;
+
+ return altcp_connect(conn->inner_conn, &state->conf->proxy_addr, state->conf->proxy_port, altcp_proxyconnect_lower_connected);
+}
+
+static struct altcp_pcb *
+altcp_proxyconnect_listen(struct altcp_pcb *conn, u8_t backlog, err_t *err)
+{
+ LWIP_UNUSED_ARG(conn);
+ LWIP_UNUSED_ARG(backlog);
+ LWIP_UNUSED_ARG(err);
+ /* listen not supported! */
+ return NULL;
+}
+
+static void
+altcp_proxyconnect_abort(struct altcp_pcb *conn)
+{
+ if (conn != NULL) {
+ if (conn->inner_conn != NULL) {
+ altcp_abort(conn->inner_conn);
+ }
+ altcp_free(conn);
+ }
+}
+
+static err_t
+altcp_proxyconnect_close(struct altcp_pcb *conn)
+{
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+ if (conn->inner_conn != NULL) {
+ err_t err = altcp_close(conn->inner_conn);
+ if (err != ERR_OK) {
+ /* closing inner conn failed, return the error */
+ return err;
+ }
+ }
+ /* no inner conn or closing it succeeded, deallocate myself */
+ altcp_free(conn);
+ return ERR_OK;
+}
+
+static err_t
+altcp_proxyconnect_write(struct altcp_pcb *conn, const void *dataptr, u16_t len, u8_t apiflags)
+{
+ altcp_proxyconnect_state_t *state;
+
+ LWIP_UNUSED_ARG(apiflags);
+
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+
+ state = (altcp_proxyconnect_state_t *)conn->state;
+ if (state == NULL) {
+ /* @todo: which error? */
+ return ERR_CLSD;
+ }
+ if (!(state->flags & ALTCP_PROXYCONNECT_FLAGS_HANDSHAKE_DONE)) {
+ /* @todo: which error? */
+ return ERR_VAL;
+ }
+ return altcp_write(conn->inner_conn, dataptr, len, apiflags);
+}
+
+static void
+altcp_proxyconnect_dealloc(struct altcp_pcb *conn)
+{
+ /* clean up and free tls state */
+ if (conn) {
+ altcp_proxyconnect_state_t *state = (altcp_proxyconnect_state_t *)conn->state;
+ if (state) {
+ altcp_proxyconnect_state_free(state);
+ conn->state = NULL;
+ }
+ }
+}
+const struct altcp_functions altcp_proxyconnect_functions = {
+ altcp_proxyconnect_set_poll,
+ altcp_proxyconnect_recved,
+ altcp_default_bind,
+ altcp_proxyconnect_connect,
+ altcp_proxyconnect_listen,
+ altcp_proxyconnect_abort,
+ altcp_proxyconnect_close,
+ altcp_default_shutdown,
+ altcp_proxyconnect_write,
+ altcp_default_output,
+ altcp_default_mss,
+ altcp_default_sndbuf,
+ altcp_default_sndqueuelen,
+ altcp_default_nagle_disable,
+ altcp_default_nagle_enable,
+ altcp_default_nagle_disabled,
+ altcp_default_setprio,
+ altcp_proxyconnect_dealloc,
+ altcp_default_get_tcp_addrinfo,
+ altcp_default_get_ip,
+ altcp_default_get_port
+#ifdef LWIP_DEBUG
+ , altcp_default_dbg_get_tcp_state
+#endif
+};
+
+#endif /* LWIP_ALTCP */
diff --git a/resources/ethernet/LwIP/apps/http/fs.c b/resources/ethernet/LwIP/apps/http/fs.c
new file mode 100644
index 00000000..c89b2ff1
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/http/fs.c
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ */
+
+#include "lwip/apps/httpd_opts.h"
+#include "lwip/def.h"
+#include "lwip/apps/fs.h"
+#include
+
+
+#include HTTPD_FSDATA_FILE
+
+/*-----------------------------------------------------------------------------------*/
+
+#if LWIP_HTTPD_CUSTOM_FILES
+int fs_open_custom(struct fs_file *file, const char *name);
+void fs_close_custom(struct fs_file *file);
+#if LWIP_HTTPD_FS_ASYNC_READ
+u8_t fs_canread_custom(struct fs_file *file);
+u8_t fs_wait_read_custom(struct fs_file *file, fs_wait_cb callback_fn, void *callback_arg);
+int fs_read_async_custom(struct fs_file *file, char *buffer, int count, fs_wait_cb callback_fn, void *callback_arg);
+#else /* LWIP_HTTPD_FS_ASYNC_READ */
+int fs_read_custom(struct fs_file *file, char *buffer, int count);
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+#endif /* LWIP_HTTPD_CUSTOM_FILES */
+
+/*-----------------------------------------------------------------------------------*/
+err_t
+fs_open(struct fs_file *file, const char *name)
+{
+ const struct fsdata_file *f;
+
+ if ((file == NULL) || (name == NULL)) {
+ return ERR_ARG;
+ }
+
+#if LWIP_HTTPD_CUSTOM_FILES
+ if (fs_open_custom(file, name)) {
+ file->is_custom_file = 1;
+ return ERR_OK;
+ }
+ file->is_custom_file = 0;
+#endif /* LWIP_HTTPD_CUSTOM_FILES */
+
+ for (f = FS_ROOT; f != NULL; f = f->next) {
+ if (!strcmp(name, (const char *)f->name)) {
+ file->data = (const char *)f->data;
+ file->len = f->len;
+ file->index = f->len;
+ file->pextension = NULL;
+ file->flags = f->flags;
+#if HTTPD_PRECALCULATED_CHECKSUM
+ file->chksum_count = f->chksum_count;
+ file->chksum = f->chksum;
+#endif /* HTTPD_PRECALCULATED_CHECKSUM */
+#if LWIP_HTTPD_FILE_STATE
+ file->state = fs_state_init(file, name);
+#endif /* #if LWIP_HTTPD_FILE_STATE */
+ return ERR_OK;
+ }
+ }
+ /* file not found */
+ return ERR_VAL;
+}
+
+/*-----------------------------------------------------------------------------------*/
+void
+fs_close(struct fs_file *file)
+{
+#if LWIP_HTTPD_CUSTOM_FILES
+ if (file->is_custom_file) {
+ fs_close_custom(file);
+ }
+#endif /* LWIP_HTTPD_CUSTOM_FILES */
+#if LWIP_HTTPD_FILE_STATE
+ fs_state_free(file, file->state);
+#endif /* #if LWIP_HTTPD_FILE_STATE */
+ LWIP_UNUSED_ARG(file);
+}
+/*-----------------------------------------------------------------------------------*/
+#if LWIP_HTTPD_DYNAMIC_FILE_READ
+#if LWIP_HTTPD_FS_ASYNC_READ
+int
+fs_read_async(struct fs_file *file, char *buffer, int count, fs_wait_cb callback_fn, void *callback_arg)
+#else /* LWIP_HTTPD_FS_ASYNC_READ */
+int
+fs_read(struct fs_file *file, char *buffer, int count)
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+{
+ int read;
+ if (file->index == file->len) {
+ return FS_READ_EOF;
+ }
+#if LWIP_HTTPD_FS_ASYNC_READ
+ LWIP_UNUSED_ARG(callback_fn);
+ LWIP_UNUSED_ARG(callback_arg);
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+#if LWIP_HTTPD_CUSTOM_FILES
+ if (file->is_custom_file) {
+#if LWIP_HTTPD_FS_ASYNC_READ
+ return fs_read_async_custom(file, buffer, count, callback_fn, callback_arg);
+#else /* LWIP_HTTPD_FS_ASYNC_READ */
+ return fs_read_custom(file, buffer, count);
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+ }
+#endif /* LWIP_HTTPD_CUSTOM_FILES */
+
+ read = file->len - file->index;
+ if (read > count) {
+ read = count;
+ }
+
+ MEMCPY(buffer, (file->data + file->index), read);
+ file->index += read;
+
+ return (read);
+}
+#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */
+/*-----------------------------------------------------------------------------------*/
+#if LWIP_HTTPD_FS_ASYNC_READ
+int
+fs_is_file_ready(struct fs_file *file, fs_wait_cb callback_fn, void *callback_arg)
+{
+ if (file != NULL) {
+#if LWIP_HTTPD_FS_ASYNC_READ
+#if LWIP_HTTPD_CUSTOM_FILES
+ if (!fs_canread_custom(file)) {
+ if (fs_wait_read_custom(file, callback_fn, callback_arg)) {
+ return 0;
+ }
+ }
+#else /* LWIP_HTTPD_CUSTOM_FILES */
+ LWIP_UNUSED_ARG(callback_fn);
+ LWIP_UNUSED_ARG(callback_arg);
+#endif /* LWIP_HTTPD_CUSTOM_FILES */
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+ }
+ return 1;
+}
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+/*-----------------------------------------------------------------------------------*/
+int
+fs_bytes_left(struct fs_file *file)
+{
+ return file->len - file->index;
+}
diff --git a/resources/ethernet/LwIP/apps/http/fs/img/sics.gif b/resources/ethernet/LwIP/apps/http/fs/img/sics.gif
new file mode 100644
index 00000000..0a4fc7bb
Binary files /dev/null and b/resources/ethernet/LwIP/apps/http/fs/img/sics.gif differ
diff --git a/resources/ethernet/LwIP/apps/http/fsdata.c b/resources/ethernet/LwIP/apps/http/fsdata.c
new file mode 100644
index 00000000..ba2a2cb9
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/http/fsdata.c
@@ -0,0 +1,337 @@
+#include "lwip/apps/fs.h"
+#include "lwip/def.h"
+
+
+#define file_NULL (struct fsdata_file *) NULL
+
+
+#ifndef FS_FILE_FLAGS_HEADER_INCLUDED
+#define FS_FILE_FLAGS_HEADER_INCLUDED 1
+#endif
+#ifndef FS_FILE_FLAGS_HEADER_PERSISTENT
+#define FS_FILE_FLAGS_HEADER_PERSISTENT 0
+#endif
+/* FSDATA_FILE_ALIGNMENT: 0=off, 1=by variable, 2=by include */
+#ifndef FSDATA_FILE_ALIGNMENT
+#define FSDATA_FILE_ALIGNMENT 0
+#endif
+#ifndef FSDATA_ALIGN_PRE
+#define FSDATA_ALIGN_PRE
+#endif
+#ifndef FSDATA_ALIGN_POST
+#define FSDATA_ALIGN_POST
+#endif
+#if FSDATA_FILE_ALIGNMENT==2
+#include "fsdata_alignment.h"
+#endif
+#if FSDATA_FILE_ALIGNMENT==1
+static const unsigned int dummy_align__img_sics_gif = 0;
+#endif
+static const unsigned char FSDATA_ALIGN_PRE data__img_sics_gif[] FSDATA_ALIGN_POST = {
+/* /img/sics.gif (14 chars) */
+0x2f,0x69,0x6d,0x67,0x2f,0x73,0x69,0x63,0x73,0x2e,0x67,0x69,0x66,0x00,0x00,0x00,
+
+/* HTTP header */
+/* "HTTP/1.0 200 OK
+" (17 bytes) */
+0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x30,0x20,0x32,0x30,0x30,0x20,0x4f,0x4b,0x0d,
+0x0a,
+/* "Server: lwIP/2.0.3d (http://savannah.nongnu.org/projects/lwip)
+" (64 bytes) */
+0x53,0x65,0x72,0x76,0x65,0x72,0x3a,0x20,0x6c,0x77,0x49,0x50,0x2f,0x32,0x2e,0x30,
+0x2e,0x33,0x64,0x20,0x28,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x73,0x61,0x76,0x61,
+0x6e,0x6e,0x61,0x68,0x2e,0x6e,0x6f,0x6e,0x67,0x6e,0x75,0x2e,0x6f,0x72,0x67,0x2f,
+0x70,0x72,0x6f,0x6a,0x65,0x63,0x74,0x73,0x2f,0x6c,0x77,0x69,0x70,0x29,0x0d,0x0a,
+
+/* "Content-Length: 724
+" (18+ bytes) */
+0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x4c,0x65,0x6e,0x67,0x74,0x68,0x3a,0x20,
+0x37,0x32,0x34,0x0d,0x0a,
+/* "Content-Type: image/gif
+
+" (27 bytes) */
+0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x54,0x79,0x70,0x65,0x3a,0x20,0x69,0x6d,
+0x61,0x67,0x65,0x2f,0x67,0x69,0x66,0x0d,0x0a,0x0d,0x0a,
+/* raw file data (724 bytes) */
+0x47,0x49,0x46,0x38,0x39,0x61,0x46,0x00,0x22,0x00,0xa5,0x00,0x00,0xd9,0x2b,0x39,
+0x6a,0x6a,0x6a,0xbf,0xbf,0xbf,0x93,0x93,0x93,0x0f,0x0f,0x0f,0xb0,0xb0,0xb0,0xa6,
+0xa6,0xa6,0x80,0x80,0x80,0x76,0x76,0x76,0x1e,0x1e,0x1e,0x9d,0x9d,0x9d,0x2e,0x2e,
+0x2e,0x49,0x49,0x49,0x54,0x54,0x54,0x8a,0x8a,0x8a,0x60,0x60,0x60,0xc6,0xa6,0x99,
+0xbd,0xb5,0xb2,0xc2,0xab,0xa1,0xd9,0x41,0x40,0xd5,0x67,0x55,0xc0,0xb0,0xaa,0xd5,
+0x5e,0x4e,0xd6,0x50,0x45,0xcc,0x93,0x7d,0xc8,0xa1,0x90,0xce,0x8b,0x76,0xd2,0x7b,
+0x65,0xd1,0x84,0x6d,0xc9,0x99,0x86,0x3a,0x3a,0x3a,0x00,0x00,0x00,0xb8,0xb8,0xb8,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x2c,0x00,0x00,
+0x00,0x00,0x46,0x00,0x22,0x00,0x00,0x06,0xfe,0x40,0x90,0x70,0x48,0x2c,0x1a,0x8f,
+0xc8,0xa4,0x72,0xc9,0x6c,0x3a,0x9f,0xd0,0xa8,0x74,0x4a,0xad,0x5a,0xaf,0xd8,0xac,
+0x76,0xa9,0x40,0x04,0xbe,0x83,0xe2,0x60,0x3c,0x50,0x20,0x0d,0x8e,0x6f,0x00,0x31,
+0x28,0x1c,0x0d,0x07,0xb5,0xc3,0x60,0x75,0x24,0x3e,0xf8,0xfc,0x87,0x11,0x06,0xe9,
+0x3d,0x46,0x07,0x0b,0x7a,0x7a,0x7c,0x43,0x06,0x1e,0x84,0x78,0x0b,0x07,0x6e,0x51,
+0x01,0x8a,0x84,0x08,0x7e,0x79,0x80,0x87,0x89,0x91,0x7a,0x93,0x0a,0x04,0x99,0x78,
+0x96,0x4f,0x03,0x9e,0x79,0x01,0x94,0x9f,0x43,0x9c,0xa3,0xa4,0x05,0x77,0xa3,0xa0,
+0x4e,0x98,0x79,0x0b,0x1e,0x83,0xa4,0xa6,0x1f,0x96,0x05,0x9d,0xaa,0x78,0x01,0x07,
+0x84,0x04,0x1e,0x1e,0xbb,0xb8,0x51,0x84,0x0e,0x43,0x05,0x07,0x77,0xa5,0x7f,0x42,
+0xb1,0xb2,0x01,0x63,0x08,0x0d,0xbb,0x01,0x0c,0x7a,0x0d,0x44,0x0e,0xd8,0xaf,0x4c,
+0x05,0x7a,0x04,0x47,0x07,0x07,0xb7,0x80,0xa2,0xe1,0x7d,0x44,0x05,0x01,0x04,0x01,
+0xd0,0xea,0x87,0x93,0x4f,0xe0,0x9a,0x49,0xce,0xd8,0x79,0x04,0x66,0x20,0x15,0x10,
+0x10,0x11,0x92,0x29,0x80,0xb6,0xc0,0x91,0x15,0x45,0x1e,0x90,0x19,0x71,0x46,0xa8,
+0x5c,0x04,0x0e,0x00,0x22,0x4e,0xe8,0x40,0x24,0x9f,0x3e,0x04,0x06,0xa7,0x58,0xd4,
+0x93,0xa0,0x1c,0x91,0x3f,0xe8,0xf0,0x88,0x03,0xb1,0x21,0xa2,0x49,0x00,0x19,0x86,
+0xfc,0x52,0x44,0xe0,0x01,0x9d,0x29,0x21,0x15,0x25,0x50,0xf7,0x67,0x25,0x1e,0x06,
+0xfd,0x4e,0x9a,0xb4,0x90,0xac,0x15,0xfa,0xcb,0x52,0x53,0x1e,0x8c,0xf2,0xf8,0x07,
+0x92,0x2d,0x08,0x3a,0x4d,0x12,0x49,0x95,0x49,0xdb,0x14,0x04,0xc4,0x14,0x85,0x29,
+0xaa,0xe7,0x01,0x08,0xa4,0x49,0x01,0x14,0x51,0xe0,0x53,0x91,0xd5,0x29,0x06,0x1a,
+0x64,0x02,0xf4,0xc7,0x81,0x9e,0x05,0x20,0x22,0x64,0xa5,0x30,0xae,0xab,0x9e,0x97,
+0x53,0xd8,0xb9,0xfd,0x50,0xef,0x93,0x02,0x42,0x74,0x34,0xe8,0x9c,0x20,0x21,0xc9,
+0x01,0x68,0x78,0xe6,0x55,0x29,0x20,0x56,0x4f,0x4c,0x40,0x51,0x71,0x82,0xc0,0x70,
+0x21,0x22,0x85,0xbe,0x4b,0x1c,0x44,0x05,0xea,0xa4,0x01,0xbf,0x22,0xb5,0xf0,0x1c,
+0x06,0x51,0x38,0x8f,0xe0,0x22,0xec,0x18,0xac,0x39,0x22,0xd4,0xd6,0x93,0x44,0x01,
+0x32,0x82,0xc8,0xfc,0x61,0xb3,0x01,0x45,0x0c,0x2e,0x83,0x30,0xd0,0x0e,0x17,0x24,
+0x0f,0x70,0x85,0x94,0xee,0x05,0x05,0x53,0x4b,0x32,0x1b,0x3f,0x98,0xd3,0x1d,0x29,
+0x81,0xb0,0xae,0x1e,0x8c,0x7e,0x68,0xe0,0x60,0x5a,0x54,0x8f,0xb0,0x78,0x69,0x73,
+0x06,0xa2,0x00,0x6b,0x57,0xca,0x3d,0x11,0x50,0xbd,0x04,0x30,0x4b,0x3a,0xd4,0xab,
+0x5f,0x1f,0x9b,0x3d,0x13,0x74,0x27,0x88,0x3c,0x25,0xe0,0x17,0xbe,0x7a,0x79,0x45,
+0x0d,0x0c,0xb0,0x8b,0xda,0x90,0xca,0x80,0x06,0x5d,0x17,0x60,0x1c,0x22,0x4c,0xd8,
+0x57,0x22,0x06,0x20,0x00,0x98,0x07,0x08,0xe4,0x56,0x80,0x80,0x1c,0xc5,0xb7,0xc5,
+0x82,0x0c,0x36,0xe8,0xe0,0x83,0x10,0x46,0x28,0xe1,0x84,0x14,0x56,0x68,0xa1,0x10,
+0x41,0x00,0x00,0x3b,};
+
+#if FSDATA_FILE_ALIGNMENT==1
+static const unsigned int dummy_align__404_html = 1;
+#endif
+static const unsigned char FSDATA_ALIGN_PRE data__404_html[] FSDATA_ALIGN_POST = {
+/* /404.html (10 chars) */
+0x2f,0x34,0x30,0x34,0x2e,0x68,0x74,0x6d,0x6c,0x00,0x00,0x00,
+
+/* HTTP header */
+/* "HTTP/1.0 404 File not found
+" (29 bytes) */
+0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x30,0x20,0x34,0x30,0x34,0x20,0x46,0x69,0x6c,
+0x65,0x20,0x6e,0x6f,0x74,0x20,0x66,0x6f,0x75,0x6e,0x64,0x0d,0x0a,
+/* "Server: lwIP/2.0.3d (http://savannah.nongnu.org/projects/lwip)
+" (64 bytes) */
+0x53,0x65,0x72,0x76,0x65,0x72,0x3a,0x20,0x6c,0x77,0x49,0x50,0x2f,0x32,0x2e,0x30,
+0x2e,0x33,0x64,0x20,0x28,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x73,0x61,0x76,0x61,
+0x6e,0x6e,0x61,0x68,0x2e,0x6e,0x6f,0x6e,0x67,0x6e,0x75,0x2e,0x6f,0x72,0x67,0x2f,
+0x70,0x72,0x6f,0x6a,0x65,0x63,0x74,0x73,0x2f,0x6c,0x77,0x69,0x70,0x29,0x0d,0x0a,
+
+/* "Content-Length: 565
+" (18+ bytes) */
+0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x4c,0x65,0x6e,0x67,0x74,0x68,0x3a,0x20,
+0x35,0x36,0x35,0x0d,0x0a,
+/* "Content-Type: text/html
+
+" (27 bytes) */
+0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x54,0x79,0x70,0x65,0x3a,0x20,0x74,0x65,
+0x78,0x74,0x2f,0x68,0x74,0x6d,0x6c,0x0d,0x0a,0x0d,0x0a,
+/* raw file data (565 bytes) */
+0x3c,0x68,0x74,0x6d,0x6c,0x3e,0x0d,0x0a,0x3c,0x68,0x65,0x61,0x64,0x3e,0x3c,0x74,
+0x69,0x74,0x6c,0x65,0x3e,0x6c,0x77,0x49,0x50,0x20,0x2d,0x20,0x41,0x20,0x4c,0x69,
+0x67,0x68,0x74,0x77,0x65,0x69,0x67,0x68,0x74,0x20,0x54,0x43,0x50,0x2f,0x49,0x50,
+0x20,0x53,0x74,0x61,0x63,0x6b,0x3c,0x2f,0x74,0x69,0x74,0x6c,0x65,0x3e,0x3c,0x2f,
+0x68,0x65,0x61,0x64,0x3e,0x0d,0x0a,0x3c,0x62,0x6f,0x64,0x79,0x20,0x62,0x67,0x63,
+0x6f,0x6c,0x6f,0x72,0x3d,0x22,0x77,0x68,0x69,0x74,0x65,0x22,0x20,0x74,0x65,0x78,
+0x74,0x3d,0x22,0x62,0x6c,0x61,0x63,0x6b,0x22,0x3e,0x0d,0x0a,0x0d,0x0a,0x20,0x20,
+0x20,0x20,0x3c,0x74,0x61,0x62,0x6c,0x65,0x20,0x77,0x69,0x64,0x74,0x68,0x3d,0x22,
+0x31,0x30,0x30,0x25,0x22,0x3e,0x0d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x74,
+0x72,0x20,0x76,0x61,0x6c,0x69,0x67,0x6e,0x3d,0x22,0x74,0x6f,0x70,0x22,0x3e,0x3c,
+0x74,0x64,0x20,0x77,0x69,0x64,0x74,0x68,0x3d,0x22,0x38,0x30,0x22,0x3e,0x09,0x20,
+0x20,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x61,0x20,0x68,0x72,0x65,0x66,0x3d,0x22,0x68,
+0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x73,0x69,0x63,0x73,0x2e,0x73,
+0x65,0x2f,0x22,0x3e,0x3c,0x69,0x6d,0x67,0x20,0x73,0x72,0x63,0x3d,0x22,0x2f,0x69,
+0x6d,0x67,0x2f,0x73,0x69,0x63,0x73,0x2e,0x67,0x69,0x66,0x22,0x0d,0x0a,0x09,0x20,
+0x20,0x62,0x6f,0x72,0x64,0x65,0x72,0x3d,0x22,0x30,0x22,0x20,0x61,0x6c,0x74,0x3d,
+0x22,0x53,0x49,0x43,0x53,0x20,0x6c,0x6f,0x67,0x6f,0x22,0x20,0x74,0x69,0x74,0x6c,
+0x65,0x3d,0x22,0x53,0x49,0x43,0x53,0x20,0x6c,0x6f,0x67,0x6f,0x22,0x3e,0x3c,0x2f,
+0x61,0x3e,0x0d,0x0a,0x09,0x3c,0x2f,0x74,0x64,0x3e,0x3c,0x74,0x64,0x20,0x77,0x69,
+0x64,0x74,0x68,0x3d,0x22,0x35,0x30,0x30,0x22,0x3e,0x09,0x20,0x20,0x0d,0x0a,0x09,
+0x20,0x20,0x3c,0x68,0x31,0x3e,0x6c,0x77,0x49,0x50,0x20,0x2d,0x20,0x41,0x20,0x4c,
+0x69,0x67,0x68,0x74,0x77,0x65,0x69,0x67,0x68,0x74,0x20,0x54,0x43,0x50,0x2f,0x49,
+0x50,0x20,0x53,0x74,0x61,0x63,0x6b,0x3c,0x2f,0x68,0x31,0x3e,0x0d,0x0a,0x09,0x20,
+0x20,0x3c,0x68,0x32,0x3e,0x34,0x30,0x34,0x20,0x2d,0x20,0x50,0x61,0x67,0x65,0x20,
+0x6e,0x6f,0x74,0x20,0x66,0x6f,0x75,0x6e,0x64,0x3c,0x2f,0x68,0x32,0x3e,0x0d,0x0a,
+0x09,0x20,0x20,0x3c,0x70,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x53,0x6f,0x72,
+0x72,0x79,0x2c,0x20,0x74,0x68,0x65,0x20,0x70,0x61,0x67,0x65,0x20,0x79,0x6f,0x75,
+0x20,0x61,0x72,0x65,0x20,0x72,0x65,0x71,0x75,0x65,0x73,0x74,0x69,0x6e,0x67,0x20,
+0x77,0x61,0x73,0x20,0x6e,0x6f,0x74,0x20,0x66,0x6f,0x75,0x6e,0x64,0x20,0x6f,0x6e,
+0x20,0x74,0x68,0x69,0x73,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x73,0x65,0x72,0x76,
+0x65,0x72,0x2e,0x20,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x2f,0x70,0x3e,0x0d,0x0a,0x09,
+0x3c,0x2f,0x74,0x64,0x3e,0x3c,0x74,0x64,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x26,0x6e,
+0x62,0x73,0x70,0x3b,0x0d,0x0a,0x09,0x3c,0x2f,0x74,0x64,0x3e,0x3c,0x2f,0x74,0x72,
+0x3e,0x0d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x2f,0x74,0x61,0x62,0x6c,0x65,
+0x3e,0x0d,0x0a,0x3c,0x2f,0x62,0x6f,0x64,0x79,0x3e,0x0d,0x0a,0x3c,0x2f,0x68,0x74,
+0x6d,0x6c,0x3e,0x0d,0x0a,};
+
+#if FSDATA_FILE_ALIGNMENT==1
+static const unsigned int dummy_align__index_html = 2;
+#endif
+static const unsigned char FSDATA_ALIGN_PRE data__index_html[] FSDATA_ALIGN_POST = {
+/* /index.html (12 chars) */
+0x2f,0x69,0x6e,0x64,0x65,0x78,0x2e,0x68,0x74,0x6d,0x6c,0x00,
+
+/* HTTP header */
+/* "HTTP/1.0 200 OK
+" (17 bytes) */
+0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x30,0x20,0x32,0x30,0x30,0x20,0x4f,0x4b,0x0d,
+0x0a,
+/* "Server: lwIP/2.0.3d (http://savannah.nongnu.org/projects/lwip)
+" (64 bytes) */
+0x53,0x65,0x72,0x76,0x65,0x72,0x3a,0x20,0x6c,0x77,0x49,0x50,0x2f,0x32,0x2e,0x30,
+0x2e,0x33,0x64,0x20,0x28,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x73,0x61,0x76,0x61,
+0x6e,0x6e,0x61,0x68,0x2e,0x6e,0x6f,0x6e,0x67,0x6e,0x75,0x2e,0x6f,0x72,0x67,0x2f,
+0x70,0x72,0x6f,0x6a,0x65,0x63,0x74,0x73,0x2f,0x6c,0x77,0x69,0x70,0x29,0x0d,0x0a,
+
+/* "Content-Length: 1751
+" (18+ bytes) */
+0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x4c,0x65,0x6e,0x67,0x74,0x68,0x3a,0x20,
+0x31,0x37,0x35,0x31,0x0d,0x0a,
+/* "Content-Type: text/html
+
+" (27 bytes) */
+0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x54,0x79,0x70,0x65,0x3a,0x20,0x74,0x65,
+0x78,0x74,0x2f,0x68,0x74,0x6d,0x6c,0x0d,0x0a,0x0d,0x0a,
+/* raw file data (1751 bytes) */
+0x3c,0x68,0x74,0x6d,0x6c,0x3e,0x0d,0x0a,0x3c,0x68,0x65,0x61,0x64,0x3e,0x3c,0x74,
+0x69,0x74,0x6c,0x65,0x3e,0x6c,0x77,0x49,0x50,0x20,0x2d,0x20,0x41,0x20,0x4c,0x69,
+0x67,0x68,0x74,0x77,0x65,0x69,0x67,0x68,0x74,0x20,0x54,0x43,0x50,0x2f,0x49,0x50,
+0x20,0x53,0x74,0x61,0x63,0x6b,0x3c,0x2f,0x74,0x69,0x74,0x6c,0x65,0x3e,0x3c,0x2f,
+0x68,0x65,0x61,0x64,0x3e,0x0d,0x0a,0x3c,0x62,0x6f,0x64,0x79,0x20,0x62,0x67,0x63,
+0x6f,0x6c,0x6f,0x72,0x3d,0x22,0x77,0x68,0x69,0x74,0x65,0x22,0x20,0x74,0x65,0x78,
+0x74,0x3d,0x22,0x62,0x6c,0x61,0x63,0x6b,0x22,0x3e,0x0d,0x0a,0x0d,0x0a,0x20,0x20,
+0x20,0x20,0x3c,0x74,0x61,0x62,0x6c,0x65,0x20,0x77,0x69,0x64,0x74,0x68,0x3d,0x22,
+0x31,0x30,0x30,0x25,0x22,0x3e,0x0d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x74,
+0x72,0x20,0x76,0x61,0x6c,0x69,0x67,0x6e,0x3d,0x22,0x74,0x6f,0x70,0x22,0x3e,0x3c,
+0x74,0x64,0x20,0x77,0x69,0x64,0x74,0x68,0x3d,0x22,0x38,0x30,0x22,0x3e,0x09,0x20,
+0x20,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x61,0x20,0x68,0x72,0x65,0x66,0x3d,0x22,0x68,
+0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x73,0x69,0x63,0x73,0x2e,0x73,
+0x65,0x2f,0x22,0x3e,0x3c,0x69,0x6d,0x67,0x20,0x73,0x72,0x63,0x3d,0x22,0x2f,0x69,
+0x6d,0x67,0x2f,0x73,0x69,0x63,0x73,0x2e,0x67,0x69,0x66,0x22,0x0d,0x0a,0x09,0x20,
+0x20,0x62,0x6f,0x72,0x64,0x65,0x72,0x3d,0x22,0x30,0x22,0x20,0x61,0x6c,0x74,0x3d,
+0x22,0x53,0x49,0x43,0x53,0x20,0x6c,0x6f,0x67,0x6f,0x22,0x20,0x74,0x69,0x74,0x6c,
+0x65,0x3d,0x22,0x53,0x49,0x43,0x53,0x20,0x6c,0x6f,0x67,0x6f,0x22,0x3e,0x3c,0x2f,
+0x61,0x3e,0x0d,0x0a,0x09,0x3c,0x2f,0x74,0x64,0x3e,0x3c,0x74,0x64,0x20,0x77,0x69,
+0x64,0x74,0x68,0x3d,0x22,0x35,0x30,0x30,0x22,0x3e,0x09,0x20,0x20,0x0d,0x0a,0x09,
+0x20,0x20,0x3c,0x68,0x31,0x3e,0x6c,0x77,0x49,0x50,0x20,0x2d,0x20,0x41,0x20,0x4c,
+0x69,0x67,0x68,0x74,0x77,0x65,0x69,0x67,0x68,0x74,0x20,0x54,0x43,0x50,0x2f,0x49,
+0x50,0x20,0x53,0x74,0x61,0x63,0x6b,0x3c,0x2f,0x68,0x31,0x3e,0x0d,0x0a,0x09,0x20,
+0x20,0x3c,0x70,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x54,0x68,0x65,0x20,0x77,
+0x65,0x62,0x20,0x70,0x61,0x67,0x65,0x20,0x79,0x6f,0x75,0x20,0x61,0x72,0x65,0x20,
+0x77,0x61,0x74,0x63,0x68,0x69,0x6e,0x67,0x20,0x77,0x61,0x73,0x20,0x73,0x65,0x72,
+0x76,0x65,0x64,0x20,0x62,0x79,0x20,0x61,0x20,0x73,0x69,0x6d,0x70,0x6c,0x65,0x20,
+0x77,0x65,0x62,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x73,0x65,0x72,0x76,0x65,0x72,
+0x20,0x72,0x75,0x6e,0x6e,0x69,0x6e,0x67,0x20,0x6f,0x6e,0x20,0x74,0x6f,0x70,0x20,
+0x6f,0x66,0x20,0x74,0x68,0x65,0x20,0x6c,0x69,0x67,0x68,0x74,0x77,0x65,0x69,0x67,
+0x68,0x74,0x20,0x54,0x43,0x50,0x2f,0x49,0x50,0x20,0x73,0x74,0x61,0x63,0x6b,0x20,
+0x3c,0x61,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x68,0x72,0x65,0x66,0x3d,0x22,0x68,
+0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x73,0x69,0x63,0x73,0x2e,0x73,
+0x65,0x2f,0x7e,0x61,0x64,0x61,0x6d,0x2f,0x6c,0x77,0x69,0x70,0x2f,0x22,0x3e,0x6c,
+0x77,0x49,0x50,0x3c,0x2f,0x61,0x3e,0x2e,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x2f,0x70,
+0x3e,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x70,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,
+0x6c,0x77,0x49,0x50,0x20,0x69,0x73,0x20,0x61,0x6e,0x20,0x6f,0x70,0x65,0x6e,0x20,
+0x73,0x6f,0x75,0x72,0x63,0x65,0x20,0x69,0x6d,0x70,0x6c,0x65,0x6d,0x65,0x6e,0x74,
+0x61,0x74,0x69,0x6f,0x6e,0x20,0x6f,0x66,0x20,0x74,0x68,0x65,0x20,0x54,0x43,0x50,
+0x2f,0x49,0x50,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x70,0x72,0x6f,0x74,0x6f,0x63,
+0x6f,0x6c,0x20,0x73,0x75,0x69,0x74,0x65,0x20,0x74,0x68,0x61,0x74,0x20,0x77,0x61,
+0x73,0x20,0x6f,0x72,0x69,0x67,0x69,0x6e,0x61,0x6c,0x6c,0x79,0x20,0x77,0x72,0x69,
+0x74,0x74,0x65,0x6e,0x20,0x62,0x79,0x20,0x3c,0x61,0x0d,0x0a,0x09,0x20,0x20,0x20,
+0x20,0x68,0x72,0x65,0x66,0x3d,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,
+0x77,0x2e,0x73,0x69,0x63,0x73,0x2e,0x73,0x65,0x2f,0x7e,0x61,0x64,0x61,0x6d,0x2f,
+0x6c,0x77,0x69,0x70,0x2f,0x22,0x3e,0x41,0x64,0x61,0x6d,0x20,0x44,0x75,0x6e,0x6b,
+0x65,0x6c,0x73,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x6f,0x66,0x20,0x74,0x68,0x65,
+0x20,0x53,0x77,0x65,0x64,0x69,0x73,0x68,0x20,0x49,0x6e,0x73,0x74,0x69,0x74,0x75,
+0x74,0x65,0x20,0x6f,0x66,0x20,0x43,0x6f,0x6d,0x70,0x75,0x74,0x65,0x72,0x20,0x53,
+0x63,0x69,0x65,0x6e,0x63,0x65,0x3c,0x2f,0x61,0x3e,0x20,0x62,0x75,0x74,0x20,0x6e,
+0x6f,0x77,0x20,0x69,0x73,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x62,0x65,0x69,0x6e,
+0x67,0x20,0x61,0x63,0x74,0x69,0x76,0x65,0x6c,0x79,0x20,0x64,0x65,0x76,0x65,0x6c,
+0x6f,0x70,0x65,0x64,0x20,0x62,0x79,0x20,0x61,0x20,0x74,0x65,0x61,0x6d,0x20,0x6f,
+0x66,0x20,0x64,0x65,0x76,0x65,0x6c,0x6f,0x70,0x65,0x72,0x73,0x0d,0x0a,0x09,0x20,
+0x20,0x20,0x20,0x64,0x69,0x73,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x64,0x20,0x77,
+0x6f,0x72,0x6c,0x64,0x2d,0x77,0x69,0x64,0x65,0x2e,0x20,0x53,0x69,0x6e,0x63,0x65,
+0x20,0x69,0x74,0x27,0x73,0x20,0x72,0x65,0x6c,0x65,0x61,0x73,0x65,0x2c,0x20,0x6c,
+0x77,0x49,0x50,0x20,0x68,0x61,0x73,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x73,0x70,
+0x75,0x72,0x72,0x65,0x64,0x20,0x61,0x20,0x6c,0x6f,0x74,0x20,0x6f,0x66,0x20,0x69,
+0x6e,0x74,0x65,0x72,0x65,0x73,0x74,0x20,0x61,0x6e,0x64,0x20,0x68,0x61,0x73,0x20,
+0x62,0x65,0x65,0x6e,0x20,0x70,0x6f,0x72,0x74,0x65,0x64,0x20,0x74,0x6f,0x20,0x73,
+0x65,0x76,0x65,0x72,0x61,0x6c,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x70,0x6c,0x61,
+0x74,0x66,0x6f,0x72,0x6d,0x73,0x20,0x61,0x6e,0x64,0x20,0x6f,0x70,0x65,0x72,0x61,
+0x74,0x69,0x6e,0x67,0x20,0x73,0x79,0x73,0x74,0x65,0x6d,0x73,0x2e,0x20,0x6c,0x77,
+0x49,0x50,0x20,0x63,0x61,0x6e,0x20,0x62,0x65,0x20,0x75,0x73,0x65,0x64,0x20,0x65,
+0x69,0x74,0x68,0x65,0x72,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x77,0x69,0x74,0x68,
+0x20,0x6f,0x72,0x20,0x77,0x69,0x74,0x68,0x6f,0x75,0x74,0x20,0x61,0x6e,0x20,0x75,
+0x6e,0x64,0x65,0x72,0x6c,0x79,0x69,0x6e,0x67,0x20,0x4f,0x53,0x2e,0x0d,0x0a,0x09,
+0x20,0x20,0x3c,0x2f,0x70,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x70,0x3e,0x0d,0x0a,
+0x09,0x20,0x20,0x20,0x20,0x54,0x68,0x65,0x20,0x66,0x6f,0x63,0x75,0x73,0x20,0x6f,
+0x66,0x20,0x74,0x68,0x65,0x20,0x6c,0x77,0x49,0x50,0x20,0x54,0x43,0x50,0x2f,0x49,
+0x50,0x20,0x69,0x6d,0x70,0x6c,0x65,0x6d,0x65,0x6e,0x74,0x61,0x74,0x69,0x6f,0x6e,
+0x20,0x69,0x73,0x20,0x74,0x6f,0x20,0x72,0x65,0x64,0x75,0x63,0x65,0x0d,0x0a,0x09,
+0x20,0x20,0x20,0x20,0x74,0x68,0x65,0x20,0x52,0x41,0x4d,0x20,0x75,0x73,0x61,0x67,
+0x65,0x20,0x77,0x68,0x69,0x6c,0x65,0x20,0x73,0x74,0x69,0x6c,0x6c,0x20,0x68,0x61,
+0x76,0x69,0x6e,0x67,0x20,0x61,0x20,0x66,0x75,0x6c,0x6c,0x20,0x73,0x63,0x61,0x6c,
+0x65,0x20,0x54,0x43,0x50,0x2e,0x20,0x54,0x68,0x69,0x73,0x0d,0x0a,0x09,0x20,0x20,
+0x20,0x20,0x6d,0x61,0x6b,0x65,0x73,0x20,0x6c,0x77,0x49,0x50,0x20,0x73,0x75,0x69,
+0x74,0x61,0x62,0x6c,0x65,0x20,0x66,0x6f,0x72,0x20,0x75,0x73,0x65,0x20,0x69,0x6e,
+0x20,0x65,0x6d,0x62,0x65,0x64,0x64,0x65,0x64,0x20,0x73,0x79,0x73,0x74,0x65,0x6d,
+0x73,0x20,0x77,0x69,0x74,0x68,0x20,0x74,0x65,0x6e,0x73,0x0d,0x0a,0x09,0x20,0x20,
+0x20,0x20,0x6f,0x66,0x20,0x6b,0x69,0x6c,0x6f,0x62,0x79,0x74,0x65,0x73,0x20,0x6f,
+0x66,0x20,0x66,0x72,0x65,0x65,0x20,0x52,0x41,0x4d,0x20,0x61,0x6e,0x64,0x20,0x72,
+0x6f,0x6f,0x6d,0x20,0x66,0x6f,0x72,0x20,0x61,0x72,0x6f,0x75,0x6e,0x64,0x20,0x34,
+0x30,0x20,0x6b,0x69,0x6c,0x6f,0x62,0x79,0x74,0x65,0x73,0x0d,0x0a,0x09,0x20,0x20,
+0x20,0x20,0x6f,0x66,0x20,0x63,0x6f,0x64,0x65,0x20,0x52,0x4f,0x4d,0x2e,0x0d,0x0a,
+0x09,0x20,0x20,0x3c,0x2f,0x70,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x70,0x3e,0x0d,
+0x0a,0x09,0x20,0x20,0x20,0x20,0x4d,0x6f,0x72,0x65,0x20,0x69,0x6e,0x66,0x6f,0x72,
+0x6d,0x61,0x74,0x69,0x6f,0x6e,0x20,0x61,0x62,0x6f,0x75,0x74,0x20,0x6c,0x77,0x49,
+0x50,0x20,0x63,0x61,0x6e,0x20,0x62,0x65,0x20,0x66,0x6f,0x75,0x6e,0x64,0x20,0x61,
+0x74,0x20,0x74,0x68,0x65,0x20,0x6c,0x77,0x49,0x50,0x0d,0x0a,0x09,0x20,0x20,0x20,
+0x20,0x68,0x6f,0x6d,0x65,0x70,0x61,0x67,0x65,0x20,0x61,0x74,0x20,0x3c,0x61,0x0d,
+0x0a,0x09,0x20,0x20,0x20,0x20,0x68,0x72,0x65,0x66,0x3d,0x22,0x68,0x74,0x74,0x70,
+0x3a,0x2f,0x2f,0x73,0x61,0x76,0x61,0x6e,0x6e,0x61,0x68,0x2e,0x6e,0x6f,0x6e,0x67,
+0x6e,0x75,0x2e,0x6f,0x72,0x67,0x2f,0x70,0x72,0x6f,0x6a,0x65,0x63,0x74,0x73,0x2f,
+0x6c,0x77,0x69,0x70,0x2f,0x22,0x3e,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x73,0x61,
+0x76,0x61,0x6e,0x6e,0x61,0x68,0x2e,0x6e,0x6f,0x6e,0x67,0x6e,0x75,0x2e,0x6f,0x72,
+0x67,0x2f,0x70,0x72,0x6f,0x6a,0x65,0x63,0x74,0x73,0x2f,0x6c,0x77,0x69,0x70,0x2f,
+0x3c,0x2f,0x61,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x6f,0x72,0x20,0x61,0x74,
+0x20,0x74,0x68,0x65,0x20,0x6c,0x77,0x49,0x50,0x20,0x77,0x69,0x6b,0x69,0x20,0x61,
+0x74,0x20,0x3c,0x61,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x68,0x72,0x65,0x66,0x3d,
+0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x6c,0x77,0x69,0x70,0x2e,0x77,0x69,0x6b,
+0x69,0x61,0x2e,0x63,0x6f,0x6d,0x2f,0x22,0x3e,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,
+0x6c,0x77,0x69,0x70,0x2e,0x77,0x69,0x6b,0x69,0x61,0x2e,0x63,0x6f,0x6d,0x2f,0x3c,
+0x2f,0x61,0x3e,0x2e,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x2f,0x70,0x3e,0x0d,0x0a,0x09,
+0x3c,0x2f,0x74,0x64,0x3e,0x3c,0x74,0x64,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x26,0x6e,
+0x62,0x73,0x70,0x3b,0x0d,0x0a,0x09,0x3c,0x2f,0x74,0x64,0x3e,0x3c,0x2f,0x74,0x72,
+0x3e,0x0d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x2f,0x74,0x61,0x62,0x6c,0x65,
+0x3e,0x0d,0x0a,0x3c,0x2f,0x62,0x6f,0x64,0x79,0x3e,0x0d,0x0a,0x3c,0x2f,0x68,0x74,
+0x6d,0x6c,0x3e,0x0d,0x0a,0x0d,0x0a,};
+
+
+
+const struct fsdata_file file__img_sics_gif[] = { {
+file_NULL,
+data__img_sics_gif,
+data__img_sics_gif + 16,
+sizeof(data__img_sics_gif) - 16,
+FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT,
+}};
+
+const struct fsdata_file file__404_html[] = { {
+file__img_sics_gif,
+data__404_html,
+data__404_html + 12,
+sizeof(data__404_html) - 12,
+FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT,
+}};
+
+const struct fsdata_file file__index_html[] = { {
+file__404_html,
+data__index_html,
+data__index_html + 12,
+sizeof(data__index_html) - 12,
+FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT,
+}};
+
+#define FS_ROOT file__index_html
+#define FS_NUMFILES 3
+
diff --git a/resources/ethernet/LwIP/apps/http/fsdata.h b/resources/ethernet/LwIP/apps/http/fsdata.h
new file mode 100644
index 00000000..bd73419d
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/http/fsdata.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ */
+#ifndef LWIP_FSDATA_H
+#define LWIP_FSDATA_H
+
+#include "lwip/apps/httpd_opts.h"
+#include "lwip/apps/fs.h"
+
+/* THIS FILE IS DEPRECATED AND WILL BE REMOVED IN THE FUTURE */
+/* content was moved to fs.h to simplify #include structure */
+
+#endif /* LWIP_FSDATA_H */
diff --git a/resources/ethernet/LwIP/apps/http/http_client.c b/resources/ethernet/LwIP/apps/http/http_client.c
new file mode 100644
index 00000000..6129cd5c
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/http/http_client.c
@@ -0,0 +1,909 @@
+/**
+ * @file
+ * HTTP client
+ */
+
+/*
+ * Copyright (c) 2018 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ */
+
+/**
+ * @defgroup httpc HTTP client
+ * @ingroup apps
+ * @todo:
+ * - persistent connections
+ * - select outgoing http version
+ * - optionally follow redirect
+ * - check request uri for invalid characters? (e.g. encode spaces)
+ * - IPv6 support
+ */
+
+#include "lwip/apps/http_client.h"
+
+#include "lwip/altcp_tcp.h"
+#include "lwip/dns.h"
+#include "lwip/debug.h"
+#include "lwip/mem.h"
+#include "lwip/altcp_tls.h"
+#include "lwip/init.h"
+
+#include
+#include
+#include
+
+#if LWIP_TCP && LWIP_CALLBACK_API
+
+/**
+ * HTTPC_DEBUG: Enable debugging for HTTP client.
+ */
+#ifndef HTTPC_DEBUG
+#define HTTPC_DEBUG LWIP_DBG_OFF
+#endif
+
+/** Set this to 1 to keep server name and uri in request state */
+#ifndef HTTPC_DEBUG_REQUEST
+#define HTTPC_DEBUG_REQUEST 0
+#endif
+
+/** This string is passed in the HTTP header as "User-Agent: " */
+#ifndef HTTPC_CLIENT_AGENT
+#define HTTPC_CLIENT_AGENT "lwIP/" LWIP_VERSION_STRING " (http://savannah.nongnu.org/projects/lwip)"
+#endif
+
+/* the various debug levels for this file */
+#define HTTPC_DEBUG_TRACE (HTTPC_DEBUG | LWIP_DBG_TRACE)
+#define HTTPC_DEBUG_STATE (HTTPC_DEBUG | LWIP_DBG_STATE)
+#define HTTPC_DEBUG_WARN (HTTPC_DEBUG | LWIP_DBG_LEVEL_WARNING)
+#define HTTPC_DEBUG_WARN_STATE (HTTPC_DEBUG | LWIP_DBG_LEVEL_WARNING | LWIP_DBG_STATE)
+#define HTTPC_DEBUG_SERIOUS (HTTPC_DEBUG | LWIP_DBG_LEVEL_SERIOUS)
+
+#define HTTPC_POLL_INTERVAL 1
+#define HTTPC_POLL_TIMEOUT 30 /* 15 seconds */
+
+#define HTTPC_CONTENT_LEN_INVALID 0xFFFFFFFF
+
+/* GET request basic */
+#define HTTPC_REQ_11 "GET %s HTTP/1.1\r\n" /* URI */\
+ "User-Agent: %s\r\n" /* User-Agent */ \
+ "Accept: */*\r\n" \
+ "Connection: Close\r\n" /* we don't support persistent connections, yet */ \
+ "\r\n"
+#define HTTPC_REQ_11_FORMAT(uri) HTTPC_REQ_11, uri, HTTPC_CLIENT_AGENT
+
+/* GET request with host */
+#define HTTPC_REQ_11_HOST "GET %s HTTP/1.1\r\n" /* URI */\
+ "User-Agent: %s\r\n" /* User-Agent */ \
+ "Accept: */*\r\n" \
+ "Host: %s\r\n" /* server name */ \
+ "Connection: Close\r\n" /* we don't support persistent connections, yet */ \
+ "\r\n"
+#define HTTPC_REQ_11_HOST_FORMAT(uri, srv_name) HTTPC_REQ_11_HOST, uri, HTTPC_CLIENT_AGENT, srv_name
+
+/* GET request with proxy */
+#define HTTPC_REQ_11_PROXY "GET http://%s%s HTTP/1.1\r\n" /* HOST, URI */\
+ "User-Agent: %s\r\n" /* User-Agent */ \
+ "Accept: */*\r\n" \
+ "Host: %s\r\n" /* server name */ \
+ "Connection: Close\r\n" /* we don't support persistent connections, yet */ \
+ "\r\n"
+#define HTTPC_REQ_11_PROXY_FORMAT(host, uri, srv_name) HTTPC_REQ_11_PROXY, host, uri, HTTPC_CLIENT_AGENT, srv_name
+
+/* GET request with proxy (non-default server port) */
+#define HTTPC_REQ_11_PROXY_PORT "GET http://%s:%d%s HTTP/1.1\r\n" /* HOST, host-port, URI */\
+ "User-Agent: %s\r\n" /* User-Agent */ \
+ "Accept: */*\r\n" \
+ "Host: %s\r\n" /* server name */ \
+ "Connection: Close\r\n" /* we don't support persistent connections, yet */ \
+ "\r\n"
+#define HTTPC_REQ_11_PROXY_PORT_FORMAT(host, host_port, uri, srv_name) HTTPC_REQ_11_PROXY_PORT, host, host_port, uri, HTTPC_CLIENT_AGENT, srv_name
+
+typedef enum ehttpc_parse_state {
+ HTTPC_PARSE_WAIT_FIRST_LINE = 0,
+ HTTPC_PARSE_WAIT_HEADERS,
+ HTTPC_PARSE_RX_DATA
+} httpc_parse_state_t;
+
+typedef struct _httpc_state
+{
+ struct altcp_pcb* pcb;
+ ip_addr_t remote_addr;
+ u16_t remote_port;
+ int timeout_ticks;
+ struct pbuf *request;
+ struct pbuf *rx_hdrs;
+ u16_t rx_http_version;
+ u16_t rx_status;
+ altcp_recv_fn recv_fn;
+ const httpc_connection_t *conn_settings;
+ void* callback_arg;
+ u32_t rx_content_len;
+ u32_t hdr_content_len;
+ httpc_parse_state_t parse_state;
+#if HTTPC_DEBUG_REQUEST
+ char* server_name;
+ char* uri;
+#endif
+} httpc_state_t;
+
+/** Free http client state and deallocate all resources within */
+static err_t
+httpc_free_state(httpc_state_t* req)
+{
+ struct altcp_pcb* tpcb;
+
+ if (req->request != NULL) {
+ pbuf_free(req->request);
+ req->request = NULL;
+ }
+ if (req->rx_hdrs != NULL) {
+ pbuf_free(req->rx_hdrs);
+ req->rx_hdrs = NULL;
+ }
+
+ tpcb = req->pcb;
+ mem_free(req);
+ req = NULL;
+
+ if (tpcb != NULL) {
+ err_t r;
+ altcp_arg(tpcb, NULL);
+ altcp_recv(tpcb, NULL);
+ altcp_err(tpcb, NULL);
+ altcp_poll(tpcb, NULL, 0);
+ altcp_sent(tpcb, NULL);
+ r = altcp_close(tpcb);
+ if (r != ERR_OK) {
+ altcp_abort(tpcb);
+ return ERR_ABRT;
+ }
+ }
+ return ERR_OK;
+}
+
+/** Close the connection: call finished callback and free the state */
+static err_t
+httpc_close(httpc_state_t* req, httpc_result_t result, u32_t server_response, err_t err)
+{
+ if (req != NULL) {
+ if (req->conn_settings != NULL) {
+ if (req->conn_settings->result_fn != NULL) {
+ req->conn_settings->result_fn(req->callback_arg, result, req->rx_content_len, server_response, err);
+ }
+ }
+ return httpc_free_state(req);
+ }
+ return ERR_OK;
+}
+
+/** Parse http header response line 1 */
+static err_t
+http_parse_response_status(struct pbuf *p, u16_t *http_version, u16_t *http_status, u16_t *http_status_str_offset)
+{
+ u16_t end1 = pbuf_memfind(p, "\r\n", 2, 0);
+ if (end1 != 0xFFFF) {
+ /* get parts of first line */
+ u16_t space1, space2;
+ space1 = pbuf_memfind(p, " ", 1, 0);
+ if (space1 != 0xFFFF) {
+ if ((pbuf_memcmp(p, 0, "HTTP/", 5) == 0) && (pbuf_get_at(p, 6) == '.')) {
+ char status_num[10];
+ size_t status_num_len;
+ /* parse http version */
+ u16_t version = pbuf_get_at(p, 5) - '0';
+ version <<= 8;
+ version |= pbuf_get_at(p, 7) - '0';
+ *http_version = version;
+
+ /* parse http status number */
+ space2 = pbuf_memfind(p, " ", 1, space1 + 1);
+ if (space2 != 0xFFFF) {
+ *http_status_str_offset = space2 + 1;
+ status_num_len = space2 - space1 - 1;
+ } else {
+ status_num_len = end1 - space1 - 1;
+ }
+ memset(status_num, 0, sizeof(status_num));
+ if (pbuf_copy_partial(p, status_num, (u16_t)status_num_len, space1 + 1) == status_num_len) {
+ int status = atoi(status_num);
+ if ((status > 0) && (status <= 0xFFFF)) {
+ *http_status = (u16_t)status;
+ return ERR_OK;
+ }
+ }
+ }
+ }
+ }
+ return ERR_VAL;
+}
+
+/** Wait for all headers to be received, return its length and content-length (if available) */
+static err_t
+http_wait_headers(struct pbuf *p, u32_t *content_length, u16_t *total_header_len)
+{
+ u16_t end1 = pbuf_memfind(p, "\r\n\r\n", 4, 0);
+ if (end1 < (0xFFFF - 2)) {
+ /* all headers received */
+ /* check if we have a content length (@todo: case insensitive?) */
+ u16_t content_len_hdr;
+ *content_length = HTTPC_CONTENT_LEN_INVALID;
+ *total_header_len = end1 + 4;
+
+ content_len_hdr = pbuf_memfind(p, "Content-Length: ", 16, 0);
+ if (content_len_hdr != 0xFFFF) {
+ u16_t content_len_line_end = pbuf_memfind(p, "\r\n", 2, content_len_hdr);
+ if (content_len_line_end != 0xFFFF) {
+ char content_len_num[16];
+ u16_t content_len_num_len = (u16_t)(content_len_line_end - content_len_hdr - 16);
+ memset(content_len_num, 0, sizeof(content_len_num));
+ if (pbuf_copy_partial(p, content_len_num, content_len_num_len, content_len_hdr + 16) == content_len_num_len) {
+ int len = atoi(content_len_num);
+ if ((len >= 0) && ((u32_t)len < HTTPC_CONTENT_LEN_INVALID)) {
+ *content_length = (u32_t)len;
+ }
+ }
+ }
+ }
+ return ERR_OK;
+ }
+ return ERR_VAL;
+}
+
+/** http client tcp recv callback */
+static err_t
+httpc_tcp_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t r)
+{
+ httpc_state_t* req = (httpc_state_t*)arg;
+ LWIP_UNUSED_ARG(r);
+
+ if (p == NULL) {
+ httpc_result_t result;
+ if (req->parse_state != HTTPC_PARSE_RX_DATA) {
+ /* did not get RX data yet */
+ result = HTTPC_RESULT_ERR_CLOSED;
+ } else if ((req->hdr_content_len != HTTPC_CONTENT_LEN_INVALID) &&
+ (req->hdr_content_len != req->rx_content_len)) {
+ /* header has been received with content length but not all data received */
+ result = HTTPC_RESULT_ERR_CONTENT_LEN;
+ } else {
+ /* receiving data and either all data received or no content length header */
+ result = HTTPC_RESULT_OK;
+ }
+ return httpc_close(req, result, req->rx_status, ERR_OK);
+ }
+ if (req->parse_state != HTTPC_PARSE_RX_DATA) {
+ if (req->rx_hdrs == NULL) {
+ req->rx_hdrs = p;
+ } else {
+ pbuf_cat(req->rx_hdrs, p);
+ }
+ if (req->parse_state == HTTPC_PARSE_WAIT_FIRST_LINE) {
+ u16_t status_str_off;
+ err_t err = http_parse_response_status(req->rx_hdrs, &req->rx_http_version, &req->rx_status, &status_str_off);
+ if (err == ERR_OK) {
+ /* don't care status string */
+ req->parse_state = HTTPC_PARSE_WAIT_HEADERS;
+ }
+ }
+ if (req->parse_state == HTTPC_PARSE_WAIT_HEADERS) {
+ u16_t total_header_len;
+ err_t err = http_wait_headers(req->rx_hdrs, &req->hdr_content_len, &total_header_len);
+ if (err == ERR_OK) {
+ struct pbuf *q;
+ /* full header received, send window update for header bytes and call into client callback */
+ altcp_recved(pcb, total_header_len);
+ if (req->conn_settings) {
+ if (req->conn_settings->headers_done_fn) {
+ err = req->conn_settings->headers_done_fn(req, req->callback_arg, req->rx_hdrs, total_header_len, req->hdr_content_len);
+ if (err != ERR_OK) {
+ return httpc_close(req, HTTPC_RESULT_LOCAL_ABORT, req->rx_status, err);
+ }
+ }
+ }
+ /* hide header bytes in pbuf */
+ q = pbuf_free_header(req->rx_hdrs, total_header_len);
+ p = q;
+ req->rx_hdrs = NULL;
+ /* go on with data */
+ req->parse_state = HTTPC_PARSE_RX_DATA;
+ }
+ }
+ }
+ if ((p != NULL) && (req->parse_state == HTTPC_PARSE_RX_DATA)) {
+ req->rx_content_len += p->tot_len;
+ if (req->recv_fn != NULL) {
+ /* directly return here: the connection migth already be aborted from the callback! */
+ return req->recv_fn(req->callback_arg, pcb, p, r);
+ } else {
+ altcp_recved(pcb, p->tot_len);
+ pbuf_free(p);
+ }
+ }
+ return ERR_OK;
+}
+
+/** http client tcp err callback */
+static void
+httpc_tcp_err(void *arg, err_t err)
+{
+ httpc_state_t* req = (httpc_state_t*)arg;
+ if (req != NULL) {
+ /* pcb has already been deallocated */
+ req->pcb = NULL;
+ httpc_close(req, HTTPC_RESULT_ERR_CLOSED, 0, err);
+ }
+}
+
+/** http client tcp poll callback */
+static err_t
+httpc_tcp_poll(void *arg, struct altcp_pcb *pcb)
+{
+ /* implement timeout */
+ httpc_state_t* req = (httpc_state_t*)arg;
+ LWIP_UNUSED_ARG(pcb);
+ if (req != NULL) {
+ if (req->timeout_ticks) {
+ req->timeout_ticks--;
+ }
+ if (!req->timeout_ticks) {
+ return httpc_close(req, HTTPC_RESULT_ERR_TIMEOUT, 0, ERR_OK);
+ }
+ }
+ return ERR_OK;
+}
+
+/** http client tcp sent callback */
+static err_t
+httpc_tcp_sent(void *arg, struct altcp_pcb *pcb, u16_t len)
+{
+ /* nothing to do here for now */
+ LWIP_UNUSED_ARG(arg);
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_UNUSED_ARG(len);
+ return ERR_OK;
+}
+
+/** http client tcp connected callback */
+static err_t
+httpc_tcp_connected(void *arg, struct altcp_pcb *pcb, err_t err)
+{
+ err_t r;
+ httpc_state_t* req = (httpc_state_t*)arg;
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_UNUSED_ARG(err);
+
+ /* send request; last char is zero termination */
+ r = altcp_write(req->pcb, req->request->payload, req->request->len - 1, TCP_WRITE_FLAG_COPY);
+ if (r != ERR_OK) {
+ /* could not write the single small request -> fail, don't retry */
+ return httpc_close(req, HTTPC_RESULT_ERR_MEM, 0, r);
+ }
+ /* everything written, we can free the request */
+ pbuf_free(req->request);
+ req->request = NULL;
+
+ altcp_output(req->pcb);
+ return ERR_OK;
+}
+
+/** Start the http request when the server IP addr is known */
+static err_t
+httpc_get_internal_addr(httpc_state_t* req, const ip_addr_t *ipaddr)
+{
+ err_t err;
+ LWIP_ASSERT("req != NULL", req != NULL);
+
+ if (&req->remote_addr != ipaddr) {
+ /* fill in remote addr if called externally */
+ req->remote_addr = *ipaddr;
+ }
+
+ err = altcp_connect(req->pcb, &req->remote_addr, req->remote_port, httpc_tcp_connected);
+ if (err == ERR_OK) {
+ return ERR_OK;
+ }
+ LWIP_DEBUGF(HTTPC_DEBUG_WARN_STATE, ("tcp_connect failed: %d\n", (int)err));
+ return err;
+}
+
+#if LWIP_DNS
+/** DNS callback
+ * If ipaddr is non-NULL, resolving succeeded and the request can be sent, otherwise it failed.
+ */
+static void
+httpc_dns_found(const char* hostname, const ip_addr_t *ipaddr, void *arg)
+{
+ httpc_state_t* req = (httpc_state_t*)arg;
+ err_t err;
+ httpc_result_t result;
+
+ LWIP_UNUSED_ARG(hostname);
+
+ if (ipaddr != NULL) {
+ err = httpc_get_internal_addr(req, ipaddr);
+ if (err == ERR_OK) {
+ return;
+ }
+ result = HTTPC_RESULT_ERR_CONNECT;
+ } else {
+ LWIP_DEBUGF(HTTPC_DEBUG_WARN_STATE, ("httpc_dns_found: failed to resolve hostname: %s\n",
+ hostname));
+ result = HTTPC_RESULT_ERR_HOSTNAME;
+ err = ERR_ARG;
+ }
+ httpc_close(req, result, 0, err);
+}
+#endif /* LWIP_DNS */
+
+/** Start the http request after converting 'server_name' to ip address (DNS or address string) */
+static err_t
+httpc_get_internal_dns(httpc_state_t* req, const char* server_name)
+{
+ err_t err;
+ LWIP_ASSERT("req != NULL", req != NULL);
+
+#if LWIP_DNS
+ err = dns_gethostbyname(server_name, &req->remote_addr, httpc_dns_found, req);
+#else
+ err = ipaddr_aton(server_name, &req->remote_addr) ? ERR_OK : ERR_ARG;
+#endif
+
+ if (err == ERR_OK) {
+ /* cached or IP-string */
+ err = httpc_get_internal_addr(req, &req->remote_addr);
+ } else if (err == ERR_INPROGRESS) {
+ return ERR_OK;
+ }
+ return err;
+}
+
+static int
+httpc_create_request_string(const httpc_connection_t *settings, const char* server_name, int server_port, const char* uri,
+ int use_host, char *buffer, size_t buffer_size)
+{
+ if (settings->use_proxy) {
+ LWIP_ASSERT("server_name != NULL", server_name != NULL);
+ if (server_port != HTTP_DEFAULT_PORT) {
+ return snprintf(buffer, buffer_size, HTTPC_REQ_11_PROXY_PORT_FORMAT(server_name, server_port, uri, server_name));
+ } else {
+ return snprintf(buffer, buffer_size, HTTPC_REQ_11_PROXY_FORMAT(server_name, uri, server_name));
+ }
+ } else if (use_host) {
+ LWIP_ASSERT("server_name != NULL", server_name != NULL);
+ return snprintf(buffer, buffer_size, HTTPC_REQ_11_HOST_FORMAT(uri, server_name));
+ } else {
+ return snprintf(buffer, buffer_size, HTTPC_REQ_11_FORMAT(uri));
+ }
+}
+
+/** Initialize the connection struct */
+static err_t
+httpc_init_connection_common(httpc_state_t **connection, const httpc_connection_t *settings, const char* server_name,
+ u16_t server_port, const char* uri, altcp_recv_fn recv_fn, void* callback_arg, int use_host)
+{
+ size_t alloc_len;
+ mem_size_t mem_alloc_len;
+ int req_len, req_len2;
+ httpc_state_t *req;
+#if HTTPC_DEBUG_REQUEST
+ size_t server_name_len, uri_len;
+#endif
+
+ LWIP_ASSERT("uri != NULL", uri != NULL);
+
+ /* get request len */
+ req_len = httpc_create_request_string(settings, server_name, server_port, uri, use_host, NULL, 0);
+ if ((req_len < 0) || (req_len > 0xFFFF)) {
+ return ERR_VAL;
+ }
+ /* alloc state and request in one block */
+ alloc_len = sizeof(httpc_state_t);
+#if HTTPC_DEBUG_REQUEST
+ server_name_len = server_name ? strlen(server_name) : 0;
+ uri_len = strlen(uri);
+ alloc_len += server_name_len + 1 + uri_len + 1;
+#endif
+ mem_alloc_len = (mem_size_t)alloc_len;
+ if ((mem_alloc_len < alloc_len) || (req_len + 1 > 0xFFFF)) {
+ return ERR_VAL;
+ }
+
+ req = (httpc_state_t*)mem_malloc((mem_size_t)alloc_len);
+ if(req == NULL) {
+ return ERR_MEM;
+ }
+ memset(req, 0, sizeof(httpc_state_t));
+ req->timeout_ticks = HTTPC_POLL_TIMEOUT;
+ req->request = pbuf_alloc(PBUF_RAW, (u16_t)(req_len + 1), PBUF_RAM);
+ if (req->request == NULL) {
+ httpc_free_state(req);
+ return ERR_MEM;
+ }
+ if (req->request->next != NULL) {
+ /* need a pbuf in one piece */
+ httpc_free_state(req);
+ return ERR_MEM;
+ }
+ req->hdr_content_len = HTTPC_CONTENT_LEN_INVALID;
+#if HTTPC_DEBUG_REQUEST
+ req->server_name = (char*)(req + 1);
+ if (server_name) {
+ memcpy(req->server_name, server_name, server_name_len + 1);
+ }
+ req->uri = req->server_name + server_name_len + 1;
+ memcpy(req->uri, uri, uri_len + 1);
+#endif
+ req->pcb = altcp_new(settings->altcp_allocator);
+ if(req->pcb == NULL) {
+ httpc_free_state(req);
+ return ERR_MEM;
+ }
+ req->remote_port = settings->use_proxy ? settings->proxy_port : server_port;
+ altcp_arg(req->pcb, req);
+ altcp_recv(req->pcb, httpc_tcp_recv);
+ altcp_err(req->pcb, httpc_tcp_err);
+ altcp_poll(req->pcb, httpc_tcp_poll, HTTPC_POLL_INTERVAL);
+ altcp_sent(req->pcb, httpc_tcp_sent);
+
+ /* set up request buffer */
+ req_len2 = httpc_create_request_string(settings, server_name, server_port, uri, use_host,
+ (char *)req->request->payload, req_len + 1);
+ if (req_len2 != req_len) {
+ httpc_free_state(req);
+ return ERR_VAL;
+ }
+
+ req->recv_fn = recv_fn;
+ req->conn_settings = settings;
+ req->callback_arg = callback_arg;
+
+ *connection = req;
+ return ERR_OK;
+}
+
+/**
+ * Initialize the connection struct
+ */
+static err_t
+httpc_init_connection(httpc_state_t **connection, const httpc_connection_t *settings, const char* server_name,
+ u16_t server_port, const char* uri, altcp_recv_fn recv_fn, void* callback_arg)
+{
+ return httpc_init_connection_common(connection, settings, server_name, server_port, uri, recv_fn, callback_arg, 1);
+}
+
+
+/**
+ * Initialize the connection struct (from IP address)
+ */
+static err_t
+httpc_init_connection_addr(httpc_state_t **connection, const httpc_connection_t *settings,
+ const ip_addr_t* server_addr, u16_t server_port, const char* uri,
+ altcp_recv_fn recv_fn, void* callback_arg)
+{
+ char *server_addr_str = ipaddr_ntoa(server_addr);
+ if (server_addr_str == NULL) {
+ return ERR_VAL;
+ }
+ return httpc_init_connection_common(connection, settings, server_addr_str, server_port, uri,
+ recv_fn, callback_arg, 1);
+}
+
+/**
+ * @ingroup httpc
+ * HTTP client API: get a file by passing server IP address
+ *
+ * @param server_addr IP address of the server to connect
+ * @param port tcp port of the server
+ * @param uri uri to get from the server, remember leading "/"!
+ * @param settings connection settings (callbacks, proxy, etc.)
+ * @param recv_fn the http body (not the headers) are passed to this callback
+ * @param callback_arg argument passed to all the callbacks
+ * @param connection retreives the connection handle (to match in callbacks)
+ * @return ERR_OK if starting the request succeeds (callback_fn will be called later)
+ * or an error code
+ */
+err_t
+httpc_get_file(const ip_addr_t* server_addr, u16_t port, const char* uri, const httpc_connection_t *settings,
+ altcp_recv_fn recv_fn, void* callback_arg, httpc_state_t **connection)
+{
+ err_t err;
+ httpc_state_t* req;
+
+ LWIP_ERROR("invalid parameters", (server_addr != NULL) && (uri != NULL) && (recv_fn != NULL), return ERR_ARG;);
+
+ err = httpc_init_connection_addr(&req, settings, server_addr, port,
+ uri, recv_fn, callback_arg);
+ if (err != ERR_OK) {
+ return err;
+ }
+
+ if (settings->use_proxy) {
+ err = httpc_get_internal_addr(req, &settings->proxy_addr);
+ } else {
+ err = httpc_get_internal_addr(req, server_addr);
+ }
+ if(err != ERR_OK) {
+ httpc_free_state(req);
+ return err;
+ }
+
+ if (connection != NULL) {
+ *connection = req;
+ }
+ return ERR_OK;
+}
+
+/**
+ * @ingroup httpc
+ * HTTP client API: get a file by passing server name as string (DNS name or IP address string)
+ *
+ * @param server_name server name as string (DNS name or IP address string)
+ * @param port tcp port of the server
+ * @param uri uri to get from the server, remember leading "/"!
+ * @param settings connection settings (callbacks, proxy, etc.)
+ * @param recv_fn the http body (not the headers) are passed to this callback
+ * @param callback_arg argument passed to all the callbacks
+ * @param connection retreives the connection handle (to match in callbacks)
+ * @return ERR_OK if starting the request succeeds (callback_fn will be called later)
+ * or an error code
+ */
+err_t
+httpc_get_file_dns(const char* server_name, u16_t port, const char* uri, const httpc_connection_t *settings,
+ altcp_recv_fn recv_fn, void* callback_arg, httpc_state_t **connection)
+{
+ err_t err;
+ httpc_state_t* req;
+
+ LWIP_ERROR("invalid parameters", (server_name != NULL) && (uri != NULL) && (recv_fn != NULL), return ERR_ARG;);
+
+ err = httpc_init_connection(&req, settings, server_name, port, uri, recv_fn, callback_arg);
+ if (err != ERR_OK) {
+ return err;
+ }
+
+ if (settings->use_proxy) {
+ err = httpc_get_internal_addr(req, &settings->proxy_addr);
+ } else {
+ err = httpc_get_internal_dns(req, server_name);
+ }
+ if(err != ERR_OK) {
+ httpc_free_state(req);
+ return err;
+ }
+
+ if (connection != NULL) {
+ *connection = req;
+ }
+ return ERR_OK;
+}
+
+#if LWIP_HTTPC_HAVE_FILE_IO
+/* Implementation to disk via fopen/fwrite/fclose follows */
+
+typedef struct _httpc_filestate
+{
+ const char* local_file_name;
+ FILE *file;
+ httpc_connection_t settings;
+ const httpc_connection_t *client_settings;
+ void *callback_arg;
+} httpc_filestate_t;
+
+static void httpc_fs_result(void *arg, httpc_result_t httpc_result, u32_t rx_content_len,
+ u32_t srv_res, err_t err);
+
+/** Initalize http client state for download to file system */
+static err_t
+httpc_fs_init(httpc_filestate_t **filestate_out, const char* local_file_name,
+ const httpc_connection_t *settings, void* callback_arg)
+{
+ httpc_filestate_t *filestate;
+ size_t file_len, alloc_len;
+ FILE *f;
+
+ file_len = strlen(local_file_name);
+ alloc_len = sizeof(httpc_filestate_t) + file_len + 1;
+
+ filestate = (httpc_filestate_t *)mem_malloc((mem_size_t)alloc_len);
+ if (filestate == NULL) {
+ return ERR_MEM;
+ }
+ memset(filestate, 0, sizeof(httpc_filestate_t));
+ filestate->local_file_name = (const char *)(filestate + 1);
+ memcpy((char *)(filestate + 1), local_file_name, file_len + 1);
+ filestate->file = NULL;
+ filestate->client_settings = settings;
+ filestate->callback_arg = callback_arg;
+ /* copy client settings but override result callback */
+ memcpy(&filestate->settings, settings, sizeof(httpc_connection_t));
+ filestate->settings.result_fn = httpc_fs_result;
+
+ f = fopen(local_file_name, "wb");
+ if(f == NULL) {
+ /* could not open file */
+ mem_free(filestate);
+ return ERR_VAL;
+ }
+ filestate->file = f;
+ *filestate_out = filestate;
+ return ERR_OK;
+}
+
+/** Free http client state for download to file system */
+static void
+httpc_fs_free(httpc_filestate_t *filestate)
+{
+ if (filestate != NULL) {
+ if (filestate->file != NULL) {
+ fclose(filestate->file);
+ filestate->file = NULL;
+ }
+ mem_free(filestate);
+ }
+}
+
+/** Connection closed (success or error) */
+static void
+httpc_fs_result(void *arg, httpc_result_t httpc_result, u32_t rx_content_len,
+ u32_t srv_res, err_t err)
+{
+ httpc_filestate_t *filestate = (httpc_filestate_t *)arg;
+ if (filestate != NULL) {
+ if (filestate->client_settings->result_fn != NULL) {
+ filestate->client_settings->result_fn(filestate->callback_arg, httpc_result, rx_content_len,
+ srv_res, err);
+ }
+ httpc_fs_free(filestate);
+ }
+}
+
+/** tcp recv callback */
+static err_t
+httpc_fs_tcp_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err)
+{
+ httpc_filestate_t *filestate = (httpc_filestate_t*)arg;
+ struct pbuf* q;
+ LWIP_UNUSED_ARG(err);
+
+ LWIP_ASSERT("p != NULL", p != NULL);
+
+ for (q = p; q != NULL; q = q->next) {
+ fwrite(q->payload, 1, q->len, filestate->file);
+ }
+ altcp_recved(pcb, p->tot_len);
+ pbuf_free(p);
+ return ERR_OK;
+}
+
+/**
+ * @ingroup httpc
+ * HTTP client API: get a file to disk by passing server IP address
+ *
+ * @param server_addr IP address of the server to connect
+ * @param port tcp port of the server
+ * @param uri uri to get from the server, remember leading "/"!
+ * @param settings connection settings (callbacks, proxy, etc.)
+ * @param callback_arg argument passed to all the callbacks
+ * @param connection retreives the connection handle (to match in callbacks)
+ * @return ERR_OK if starting the request succeeds (callback_fn will be called later)
+ * or an error code
+ */
+err_t
+httpc_get_file_to_disk(const ip_addr_t* server_addr, u16_t port, const char* uri, const httpc_connection_t *settings,
+ void* callback_arg, const char* local_file_name, httpc_state_t **connection)
+{
+ err_t err;
+ httpc_state_t* req;
+ httpc_filestate_t *filestate;
+
+ LWIP_ERROR("invalid parameters", (server_addr != NULL) && (uri != NULL) && (local_file_name != NULL), return ERR_ARG;);
+
+ err = httpc_fs_init(&filestate, local_file_name, settings, callback_arg);
+ if (err != ERR_OK) {
+ return err;
+ }
+
+ err = httpc_init_connection_addr(&req, &filestate->settings, server_addr, port,
+ uri, httpc_fs_tcp_recv, filestate);
+ if (err != ERR_OK) {
+ httpc_fs_free(filestate);
+ return err;
+ }
+
+ if (settings->use_proxy) {
+ err = httpc_get_internal_addr(req, &settings->proxy_addr);
+ } else {
+ err = httpc_get_internal_addr(req, server_addr);
+ }
+ if(err != ERR_OK) {
+ httpc_fs_free(filestate);
+ httpc_free_state(req);
+ return err;
+ }
+
+ if (connection != NULL) {
+ *connection = req;
+ }
+ return ERR_OK;
+}
+
+/**
+ * @ingroup httpc
+ * HTTP client API: get a file to disk by passing server name as string (DNS name or IP address string)
+ *
+ * @param server_name server name as string (DNS name or IP address string)
+ * @param port tcp port of the server
+ * @param uri uri to get from the server, remember leading "/"!
+ * @param settings connection settings (callbacks, proxy, etc.)
+ * @param callback_arg argument passed to all the callbacks
+ * @param connection retreives the connection handle (to match in callbacks)
+ * @return ERR_OK if starting the request succeeds (callback_fn will be called later)
+ * or an error code
+ */
+err_t
+httpc_get_file_dns_to_disk(const char* server_name, u16_t port, const char* uri, const httpc_connection_t *settings,
+ void* callback_arg, const char* local_file_name, httpc_state_t **connection)
+{
+ err_t err;
+ httpc_state_t* req;
+ httpc_filestate_t *filestate;
+
+ LWIP_ERROR("invalid parameters", (server_name != NULL) && (uri != NULL) && (local_file_name != NULL), return ERR_ARG;);
+
+ err = httpc_fs_init(&filestate, local_file_name, settings, callback_arg);
+ if (err != ERR_OK) {
+ return err;
+ }
+
+ err = httpc_init_connection(&req, &filestate->settings, server_name, port,
+ uri, httpc_fs_tcp_recv, filestate);
+ if (err != ERR_OK) {
+ httpc_fs_free(filestate);
+ return err;
+ }
+
+ if (settings->use_proxy) {
+ err = httpc_get_internal_addr(req, &settings->proxy_addr);
+ } else {
+ err = httpc_get_internal_dns(req, server_name);
+ }
+ if(err != ERR_OK) {
+ httpc_fs_free(filestate);
+ httpc_free_state(req);
+ return err;
+ }
+
+ if (connection != NULL) {
+ *connection = req;
+ }
+ return ERR_OK;
+}
+#endif /* LWIP_HTTPC_HAVE_FILE_IO */
+
+#endif /* LWIP_TCP && LWIP_CALLBACK_API */
diff --git a/resources/ethernet/LwIP/apps/http/httpd.c b/resources/ethernet/LwIP/apps/http/httpd.c
new file mode 100644
index 00000000..cbb0387d
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/http/httpd.c
@@ -0,0 +1,2746 @@
+/**
+ * @file
+ * LWIP HTTP server implementation
+ */
+
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ * Simon Goldschmidt
+ *
+ */
+
+/**
+ * @defgroup httpd HTTP server
+ * @ingroup apps
+ *
+ * This httpd supports for a
+ * rudimentary server-side-include facility which will replace tags of the form
+ * in any file whose extension is .shtml, .shtm or .ssi with
+ * strings provided by an include handler whose pointer is provided to the
+ * module via function http_set_ssi_handler().
+ * Additionally, a simple common
+ * gateway interface (CGI) handling mechanism has been added to allow clients
+ * to hook functions to particular request URIs.
+ *
+ * To enable SSI support, define label LWIP_HTTPD_SSI in lwipopts.h.
+ * To enable CGI support, define label LWIP_HTTPD_CGI in lwipopts.h.
+ *
+ * By default, the server assumes that HTTP headers are already present in
+ * each file stored in the file system. By defining LWIP_HTTPD_DYNAMIC_HEADERS in
+ * lwipopts.h, this behavior can be changed such that the server inserts the
+ * headers automatically based on the extension of the file being served. If
+ * this mode is used, be careful to ensure that the file system image used
+ * does not already contain the header information.
+ *
+ * File system images without headers can be created using the makefsfile
+ * tool with the -h command line option.
+ *
+ *
+ * Notes about valid SSI tags
+ * --------------------------
+ *
+ * The following assumptions are made about tags used in SSI markers:
+ *
+ * 1. No tag may contain '-' or whitespace characters within the tag name.
+ * 2. Whitespace is allowed between the tag leadin "".
+ * 3. The maximum tag name length is LWIP_HTTPD_MAX_TAG_NAME_LEN, currently 8 characters.
+ *
+ * Notes on CGI usage
+ * ------------------
+ *
+ * The simple CGI support offered here works with GET method requests only
+ * and can handle up to 16 parameters encoded into the URI. The handler
+ * function may not write directly to the HTTP output but must return a
+ * filename that the HTTP server will send to the browser as a response to
+ * the incoming CGI request.
+ *
+ *
+ *
+ * The list of supported file types is quite short, so if makefsdata complains
+ * about an unknown extension, make sure to add it (and its doctype) to
+ * the 'g_psHTTPHeaders' list.
+ */
+#include "lwip/init.h"
+#include "lwip/apps/httpd.h"
+#include "lwip/debug.h"
+#include "lwip/stats.h"
+#include "lwip/apps/fs.h"
+#include "httpd_structs.h"
+#include "lwip/def.h"
+
+#include "lwip/altcp.h"
+#include "lwip/altcp_tcp.h"
+#if HTTPD_ENABLE_HTTPS
+#include "lwip/altcp_tls.h"
+#endif
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+#if LWIP_HTTPD_TIMING
+#include "lwip/sys.h"
+#endif /* LWIP_HTTPD_TIMING */
+
+#include /* memset */
+#include /* atoi */
+#include
+
+#if LWIP_TCP && LWIP_CALLBACK_API
+
+/** Minimum length for a valid HTTP/0.9 request: "GET /\r\n" -> 7 bytes */
+#define MIN_REQ_LEN 7
+
+#define CRLF "\r\n"
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+#define HTTP11_CONNECTIONKEEPALIVE "Connection: keep-alive"
+#define HTTP11_CONNECTIONKEEPALIVE2 "Connection: Keep-Alive"
+#endif
+
+#if LWIP_HTTPD_DYNAMIC_FILE_READ
+#define HTTP_IS_DYNAMIC_FILE(hs) ((hs)->buf != NULL)
+#else
+#define HTTP_IS_DYNAMIC_FILE(hs) 0
+#endif
+
+/* This defines checks whether tcp_write has to copy data or not */
+
+#ifndef HTTP_IS_DATA_VOLATILE
+/** tcp_write does not have to copy data when sent from rom-file-system directly */
+#define HTTP_IS_DATA_VOLATILE(hs) (HTTP_IS_DYNAMIC_FILE(hs) ? TCP_WRITE_FLAG_COPY : 0)
+#endif
+/** Default: dynamic headers are sent from ROM (non-dynamic headers are handled like file data) */
+#ifndef HTTP_IS_HDR_VOLATILE
+#define HTTP_IS_HDR_VOLATILE(hs, ptr) 0
+#endif
+
+/* Return values for http_send_*() */
+#define HTTP_DATA_TO_SEND_FREED 3
+#define HTTP_DATA_TO_SEND_BREAK 2
+#define HTTP_DATA_TO_SEND_CONTINUE 1
+#define HTTP_NO_DATA_TO_SEND 0
+
+typedef struct {
+ const char *name;
+ u8_t shtml;
+} default_filename;
+
+static const default_filename httpd_default_filenames[] = {
+ {"/index.shtml", 1 },
+ {"/index.ssi", 1 },
+ {"/index.shtm", 1 },
+ {"/index.html", 0 },
+ {"/index.htm", 0 }
+};
+
+#define NUM_DEFAULT_FILENAMES LWIP_ARRAYSIZE(httpd_default_filenames)
+
+#if LWIP_HTTPD_SUPPORT_REQUESTLIST
+/** HTTP request is copied here from pbufs for simple parsing */
+static char httpd_req_buf[LWIP_HTTPD_MAX_REQ_LENGTH + 1];
+#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+
+#if LWIP_HTTPD_SUPPORT_POST
+#if LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN > LWIP_HTTPD_MAX_REQUEST_URI_LEN
+#define LWIP_HTTPD_URI_BUF_LEN LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN
+#endif
+#endif
+#ifndef LWIP_HTTPD_URI_BUF_LEN
+#define LWIP_HTTPD_URI_BUF_LEN LWIP_HTTPD_MAX_REQUEST_URI_LEN
+#endif
+#if LWIP_HTTPD_URI_BUF_LEN
+/* Filename for response file to send when POST is finished or
+ * search for default files when a directory is requested. */
+static char http_uri_buf[LWIP_HTTPD_URI_BUF_LEN + 1];
+#endif
+
+#if LWIP_HTTPD_DYNAMIC_HEADERS
+/* The number of individual strings that comprise the headers sent before each
+ * requested file.
+ */
+#define NUM_FILE_HDR_STRINGS 5
+#define HDR_STRINGS_IDX_HTTP_STATUS 0 /* e.g. "HTTP/1.0 200 OK\r\n" */
+#define HDR_STRINGS_IDX_SERVER_NAME 1 /* e.g. "Server: "HTTPD_SERVER_AGENT"\r\n" */
+#define HDR_STRINGS_IDX_CONTENT_LEN_KEEPALIVE 2 /* e.g. "Content-Length: xy\r\n" and/or "Connection: keep-alive\r\n" */
+#define HDR_STRINGS_IDX_CONTENT_LEN_NR 3 /* the byte count, when content-length is used */
+#define HDR_STRINGS_IDX_CONTENT_TYPE 4 /* the content type (or default answer content type including default document) */
+
+/* The dynamically generated Content-Length buffer needs space for CRLF + NULL */
+#define LWIP_HTTPD_MAX_CONTENT_LEN_OFFSET 3
+#ifndef LWIP_HTTPD_MAX_CONTENT_LEN_SIZE
+/* The dynamically generated Content-Length buffer shall be able to work with
+ ~953 MB (9 digits) */
+#define LWIP_HTTPD_MAX_CONTENT_LEN_SIZE (9 + LWIP_HTTPD_MAX_CONTENT_LEN_OFFSET)
+#endif
+#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
+
+#if LWIP_HTTPD_SSI
+
+#define HTTPD_LAST_TAG_PART 0xFFFF
+
+enum tag_check_state {
+ TAG_NONE, /* Not processing an SSI tag */
+ TAG_LEADIN, /* Tag lead in "" being processed */
+ TAG_SENDING /* Sending tag replacement string */
+};
+
+struct http_ssi_state {
+ const char *parsed; /* Pointer to the first unparsed byte in buf. */
+#if !LWIP_HTTPD_SSI_INCLUDE_TAG
+ const char *tag_started;/* Pointer to the first opening '<' of the tag. */
+#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG */
+ const char *tag_end; /* Pointer to char after the closing '>' of the tag. */
+ u32_t parse_left; /* Number of unparsed bytes in buf. */
+ u16_t tag_index; /* Counter used by tag parsing state machine */
+ u16_t tag_insert_len; /* Length of insert in string tag_insert */
+#if LWIP_HTTPD_SSI_MULTIPART
+ u16_t tag_part; /* Counter passed to and changed by tag insertion function to insert multiple times */
+#endif /* LWIP_HTTPD_SSI_MULTIPART */
+ u8_t tag_type; /* index into http_ssi_tag_desc array */
+ u8_t tag_name_len; /* Length of the tag name in string tag_name */
+ char tag_name[LWIP_HTTPD_MAX_TAG_NAME_LEN + 1]; /* Last tag name extracted */
+ char tag_insert[LWIP_HTTPD_MAX_TAG_INSERT_LEN + 1]; /* Insert string for tag_name */
+ enum tag_check_state tag_state; /* State of the tag processor */
+};
+
+struct http_ssi_tag_description {
+ const char *lead_in;
+ const char *lead_out;
+};
+
+#endif /* LWIP_HTTPD_SSI */
+
+struct http_state {
+#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED
+ struct http_state *next;
+#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */
+ struct fs_file file_handle;
+ struct fs_file *handle;
+ const char *file; /* Pointer to first unsent byte in buf. */
+
+ struct altcp_pcb *pcb;
+#if LWIP_HTTPD_SUPPORT_REQUESTLIST
+ struct pbuf *req;
+#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+
+#if LWIP_HTTPD_DYNAMIC_FILE_READ
+ char *buf; /* File read buffer. */
+ int buf_len; /* Size of file read buffer, buf. */
+#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */
+ u32_t left; /* Number of unsent bytes in buf. */
+ u8_t retries;
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+ u8_t keepalive;
+#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */
+#if LWIP_HTTPD_SSI
+ struct http_ssi_state *ssi;
+#endif /* LWIP_HTTPD_SSI */
+#if LWIP_HTTPD_CGI
+ char *params[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Params extracted from the request URI */
+ char *param_vals[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Values for each extracted param */
+#endif /* LWIP_HTTPD_CGI */
+#if LWIP_HTTPD_DYNAMIC_HEADERS
+ const char *hdrs[NUM_FILE_HDR_STRINGS]; /* HTTP headers to be sent. */
+ char hdr_content_len[LWIP_HTTPD_MAX_CONTENT_LEN_SIZE];
+ u16_t hdr_pos; /* The position of the first unsent header byte in the
+ current string */
+ u16_t hdr_index; /* The index of the hdr string currently being sent. */
+#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
+#if LWIP_HTTPD_TIMING
+ u32_t time_started;
+#endif /* LWIP_HTTPD_TIMING */
+#if LWIP_HTTPD_SUPPORT_POST
+ u32_t post_content_len_left;
+#if LWIP_HTTPD_POST_MANUAL_WND
+ u32_t unrecved_bytes;
+ u8_t no_auto_wnd;
+ u8_t post_finished;
+#endif /* LWIP_HTTPD_POST_MANUAL_WND */
+#endif /* LWIP_HTTPD_SUPPORT_POST*/
+};
+
+#if HTTPD_USE_MEM_POOL
+LWIP_MEMPOOL_DECLARE(HTTPD_STATE, MEMP_NUM_PARALLEL_HTTPD_CONNS, sizeof(struct http_state), "HTTPD_STATE")
+#if LWIP_HTTPD_SSI
+LWIP_MEMPOOL_DECLARE(HTTPD_SSI_STATE, MEMP_NUM_PARALLEL_HTTPD_SSI_CONNS, sizeof(struct http_ssi_state), "HTTPD_SSI_STATE")
+#define HTTP_FREE_SSI_STATE(x) LWIP_MEMPOOL_FREE(HTTPD_SSI_STATE, (x))
+#define HTTP_ALLOC_SSI_STATE() (struct http_ssi_state *)LWIP_MEMPOOL_ALLOC(HTTPD_SSI_STATE)
+#endif /* LWIP_HTTPD_SSI */
+#define HTTP_ALLOC_HTTP_STATE() (struct http_state *)LWIP_MEMPOOL_ALLOC(HTTPD_STATE)
+#define HTTP_FREE_HTTP_STATE(x) LWIP_MEMPOOL_FREE(HTTPD_STATE, (x))
+#else /* HTTPD_USE_MEM_POOL */
+#define HTTP_ALLOC_HTTP_STATE() (struct http_state *)mem_malloc(sizeof(struct http_state))
+#define HTTP_FREE_HTTP_STATE(x) mem_free(x)
+#if LWIP_HTTPD_SSI
+#define HTTP_ALLOC_SSI_STATE() (struct http_ssi_state *)mem_malloc(sizeof(struct http_ssi_state))
+#define HTTP_FREE_SSI_STATE(x) mem_free(x)
+#endif /* LWIP_HTTPD_SSI */
+#endif /* HTTPD_USE_MEM_POOL */
+
+static err_t http_close_conn(struct altcp_pcb *pcb, struct http_state *hs);
+static err_t http_close_or_abort_conn(struct altcp_pcb *pcb, struct http_state *hs, u8_t abort_conn);
+static err_t http_find_file(struct http_state *hs, const char *uri, int is_09);
+static err_t http_init_file(struct http_state *hs, struct fs_file *file, int is_09, const char *uri, u8_t tag_check, char *params);
+static err_t http_poll(void *arg, struct altcp_pcb *pcb);
+static u8_t http_check_eof(struct altcp_pcb *pcb, struct http_state *hs);
+#if LWIP_HTTPD_FS_ASYNC_READ
+static void http_continue(void *connection);
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+
+#if LWIP_HTTPD_SSI
+/* SSI insert handler function pointer. */
+static tSSIHandler httpd_ssi_handler;
+#if !LWIP_HTTPD_SSI_RAW
+static int httpd_num_tags;
+static const char **httpd_tags;
+#endif /* !LWIP_HTTPD_SSI_RAW */
+
+/* Define the available tag lead-ins and corresponding lead-outs.
+ * ATTENTION: for the algorithm below using this array, it is essential
+ * that the lead in differs in the first character! */
+const struct http_ssi_tag_description http_ssi_tag_desc[] = {
+ {""},
+ {"/*#", "*/"}
+};
+
+#endif /* LWIP_HTTPD_SSI */
+
+#if LWIP_HTTPD_CGI
+/* CGI handler information */
+static const tCGI *httpd_cgis;
+static int httpd_num_cgis;
+static int http_cgi_paramcount;
+#define http_cgi_params hs->params
+#define http_cgi_param_vals hs->param_vals
+#elif LWIP_HTTPD_CGI_SSI
+static char *http_cgi_params[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Params extracted from the request URI */
+static char *http_cgi_param_vals[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Values for each extracted param */
+#endif /* LWIP_HTTPD_CGI */
+
+#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED
+/** global list of active HTTP connections, use to kill the oldest when
+ running out of memory */
+static struct http_state *http_connections;
+
+static void
+http_add_connection(struct http_state *hs)
+{
+ /* add the connection to the list */
+ hs->next = http_connections;
+ http_connections = hs;
+}
+
+static void
+http_remove_connection(struct http_state *hs)
+{
+ /* take the connection off the list */
+ if (http_connections) {
+ if (http_connections == hs) {
+ http_connections = hs->next;
+ } else {
+ struct http_state *last;
+ for (last = http_connections; last->next != NULL; last = last->next) {
+ if (last->next == hs) {
+ last->next = hs->next;
+ break;
+ }
+ }
+ }
+ }
+}
+
+static void
+http_kill_oldest_connection(u8_t ssi_required)
+{
+ struct http_state *hs = http_connections;
+ struct http_state *hs_free_next = NULL;
+ while (hs && hs->next) {
+#if LWIP_HTTPD_SSI
+ if (ssi_required) {
+ if (hs->next->ssi != NULL) {
+ hs_free_next = hs;
+ }
+ } else
+#else /* LWIP_HTTPD_SSI */
+ LWIP_UNUSED_ARG(ssi_required);
+#endif /* LWIP_HTTPD_SSI */
+ {
+ hs_free_next = hs;
+ }
+ LWIP_ASSERT("broken list", hs != hs->next);
+ hs = hs->next;
+ }
+ if (hs_free_next != NULL) {
+ LWIP_ASSERT("hs_free_next->next != NULL", hs_free_next->next != NULL);
+ LWIP_ASSERT("hs_free_next->next->pcb != NULL", hs_free_next->next->pcb != NULL);
+ /* send RST when killing a connection because of memory shortage */
+ http_close_or_abort_conn(hs_free_next->next->pcb, hs_free_next->next, 1); /* this also unlinks the http_state from the list */
+ }
+}
+#else /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */
+
+#define http_add_connection(hs)
+#define http_remove_connection(hs)
+
+#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */
+
+#if LWIP_HTTPD_SSI
+/** Allocate as struct http_ssi_state. */
+static struct http_ssi_state *
+http_ssi_state_alloc(void)
+{
+ struct http_ssi_state *ret = HTTP_ALLOC_SSI_STATE();
+#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED
+ if (ret == NULL) {
+ http_kill_oldest_connection(1);
+ ret = HTTP_ALLOC_SSI_STATE();
+ }
+#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */
+ if (ret != NULL) {
+ memset(ret, 0, sizeof(struct http_ssi_state));
+ }
+ return ret;
+}
+
+/** Free a struct http_ssi_state. */
+static void
+http_ssi_state_free(struct http_ssi_state *ssi)
+{
+ if (ssi != NULL) {
+ HTTP_FREE_SSI_STATE(ssi);
+ }
+}
+#endif /* LWIP_HTTPD_SSI */
+
+/** Initialize a struct http_state.
+ */
+static void
+http_state_init(struct http_state *hs)
+{
+ /* Initialize the structure. */
+ memset(hs, 0, sizeof(struct http_state));
+#if LWIP_HTTPD_DYNAMIC_HEADERS
+ /* Indicate that the headers are not yet valid */
+ hs->hdr_index = NUM_FILE_HDR_STRINGS;
+#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
+}
+
+/** Allocate a struct http_state. */
+static struct http_state *
+http_state_alloc(void)
+{
+ struct http_state *ret = HTTP_ALLOC_HTTP_STATE();
+#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED
+ if (ret == NULL) {
+ http_kill_oldest_connection(0);
+ ret = HTTP_ALLOC_HTTP_STATE();
+ }
+#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */
+ if (ret != NULL) {
+ http_state_init(ret);
+ http_add_connection(ret);
+ }
+ return ret;
+}
+
+/** Free a struct http_state.
+ * Also frees the file data if dynamic.
+ */
+static void
+http_state_eof(struct http_state *hs)
+{
+ if (hs->handle) {
+#if LWIP_HTTPD_TIMING
+ u32_t ms_needed = sys_now() - hs->time_started;
+ u32_t needed = LWIP_MAX(1, (ms_needed / 100));
+ LWIP_DEBUGF(HTTPD_DEBUG_TIMING, ("httpd: needed %"U32_F" ms to send file of %d bytes -> %"U32_F" bytes/sec\n",
+ ms_needed, hs->handle->len, ((((u32_t)hs->handle->len) * 10) / needed)));
+#endif /* LWIP_HTTPD_TIMING */
+ fs_close(hs->handle);
+ hs->handle = NULL;
+ }
+#if LWIP_HTTPD_DYNAMIC_FILE_READ
+ if (hs->buf != NULL) {
+ mem_free(hs->buf);
+ hs->buf = NULL;
+ }
+#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */
+#if LWIP_HTTPD_SSI
+ if (hs->ssi) {
+ http_ssi_state_free(hs->ssi);
+ hs->ssi = NULL;
+ }
+#endif /* LWIP_HTTPD_SSI */
+#if LWIP_HTTPD_SUPPORT_REQUESTLIST
+ if (hs->req) {
+ pbuf_free(hs->req);
+ hs->req = NULL;
+ }
+#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+}
+
+/** Free a struct http_state.
+ * Also frees the file data if dynamic.
+ */
+static void
+http_state_free(struct http_state *hs)
+{
+ if (hs != NULL) {
+ http_state_eof(hs);
+ http_remove_connection(hs);
+ HTTP_FREE_HTTP_STATE(hs);
+ }
+}
+
+/** Call tcp_write() in a loop trying smaller and smaller length
+ *
+ * @param pcb altcp_pcb to send
+ * @param ptr Data to send
+ * @param length Length of data to send (in/out: on return, contains the
+ * amount of data sent)
+ * @param apiflags directly passed to tcp_write
+ * @return the return value of tcp_write
+ */
+static err_t
+http_write(struct altcp_pcb *pcb, const void *ptr, u16_t *length, u8_t apiflags)
+{
+ u16_t len, max_len;
+ err_t err;
+ LWIP_ASSERT("length != NULL", length != NULL);
+ len = *length;
+ if (len == 0) {
+ return ERR_OK;
+ }
+ /* We cannot send more data than space available in the send buffer. */
+ max_len = altcp_sndbuf(pcb);
+ if (max_len < len) {
+ len = max_len;
+ }
+#ifdef HTTPD_MAX_WRITE_LEN
+ /* Additional limitation: e.g. don't enqueue more than 2*mss at once */
+ max_len = HTTPD_MAX_WRITE_LEN(pcb);
+ if (len > max_len) {
+ len = max_len;
+ }
+#endif /* HTTPD_MAX_WRITE_LEN */
+ do {
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Trying to send %d bytes\n", len));
+ err = altcp_write(pcb, ptr, len, apiflags);
+ if (err == ERR_MEM) {
+ if ((altcp_sndbuf(pcb) == 0) ||
+ (altcp_sndqueuelen(pcb) >= TCP_SND_QUEUELEN)) {
+ /* no need to try smaller sizes */
+ len = 1;
+ } else {
+ len /= 2;
+ }
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE,
+ ("Send failed, trying less (%d bytes)\n", len));
+ }
+ } while ((err == ERR_MEM) && (len > 1));
+
+ if (err == ERR_OK) {
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Sent %d bytes\n", len));
+ *length = len;
+ } else {
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Send failed with err %d (\"%s\")\n", err, lwip_strerr(err)));
+ *length = 0;
+ }
+
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+ /* ensure nagle is normally enabled (only disabled for persistent connections
+ when all data has been enqueued but the connection stays open for the next
+ request */
+ altcp_nagle_enable(pcb);
+#endif
+
+ return err;
+}
+
+/**
+ * The connection shall be actively closed (using RST to close from fault states).
+ * Reset the sent- and recv-callbacks.
+ *
+ * @param pcb the tcp pcb to reset callbacks
+ * @param hs connection state to free
+ */
+static err_t
+http_close_or_abort_conn(struct altcp_pcb *pcb, struct http_state *hs, u8_t abort_conn)
+{
+ err_t err;
+ LWIP_DEBUGF(HTTPD_DEBUG, ("Closing connection %p\n", (void *)pcb));
+
+#if LWIP_HTTPD_SUPPORT_POST
+ if (hs != NULL) {
+ if ((hs->post_content_len_left != 0)
+#if LWIP_HTTPD_POST_MANUAL_WND
+ || ((hs->no_auto_wnd != 0) && (hs->unrecved_bytes != 0))
+#endif /* LWIP_HTTPD_POST_MANUAL_WND */
+ ) {
+ /* make sure the post code knows that the connection is closed */
+ http_uri_buf[0] = 0;
+ httpd_post_finished(hs, http_uri_buf, LWIP_HTTPD_URI_BUF_LEN);
+ }
+ }
+#endif /* LWIP_HTTPD_SUPPORT_POST*/
+
+
+ altcp_arg(pcb, NULL);
+ altcp_recv(pcb, NULL);
+ altcp_err(pcb, NULL);
+ altcp_poll(pcb, NULL, 0);
+ altcp_sent(pcb, NULL);
+ if (hs != NULL) {
+ http_state_free(hs);
+ }
+
+ if (abort_conn) {
+ altcp_abort(pcb);
+ return ERR_OK;
+ }
+ err = altcp_close(pcb);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(HTTPD_DEBUG, ("Error %d closing %p\n", err, (void *)pcb));
+ /* error closing, try again later in poll */
+ altcp_poll(pcb, http_poll, HTTPD_POLL_INTERVAL);
+ }
+ return err;
+}
+
+/**
+ * The connection shall be actively closed.
+ * Reset the sent- and recv-callbacks.
+ *
+ * @param pcb the tcp pcb to reset callbacks
+ * @param hs connection state to free
+ */
+static err_t
+http_close_conn(struct altcp_pcb *pcb, struct http_state *hs)
+{
+ return http_close_or_abort_conn(pcb, hs, 0);
+}
+
+/** End of file: either close the connection (Connection: close) or
+ * close the file (Connection: keep-alive)
+ */
+static void
+http_eof(struct altcp_pcb *pcb, struct http_state *hs)
+{
+ /* HTTP/1.1 persistent connection? (Not supported for SSI) */
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+ if (hs->keepalive) {
+ http_remove_connection(hs);
+
+ http_state_eof(hs);
+ http_state_init(hs);
+ /* restore state: */
+ hs->pcb = pcb;
+ hs->keepalive = 1;
+ http_add_connection(hs);
+ /* ensure nagle doesn't interfere with sending all data as fast as possible: */
+ altcp_nagle_disable(pcb);
+ } else
+#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */
+ {
+ http_close_conn(pcb, hs);
+ }
+}
+
+#if LWIP_HTTPD_CGI || LWIP_HTTPD_CGI_SSI
+/**
+ * Extract URI parameters from the parameter-part of an URI in the form
+ * "test.cgi?x=y" @todo: better explanation!
+ * Pointers to the parameters are stored in hs->param_vals.
+ *
+ * @param hs http connection state
+ * @param params pointer to the NULL-terminated parameter string from the URI
+ * @return number of parameters extracted
+ */
+static int
+extract_uri_parameters(struct http_state *hs, char *params)
+{
+ char *pair;
+ char *equals;
+ int loop;
+
+ LWIP_UNUSED_ARG(hs);
+
+ /* If we have no parameters at all, return immediately. */
+ if (!params || (params[0] == '\0')) {
+ return (0);
+ }
+
+ /* Get a pointer to our first parameter */
+ pair = params;
+
+ /* Parse up to LWIP_HTTPD_MAX_CGI_PARAMETERS from the passed string and ignore the
+ * remainder (if any) */
+ for (loop = 0; (loop < LWIP_HTTPD_MAX_CGI_PARAMETERS) && pair; loop++) {
+
+ /* Save the name of the parameter */
+ http_cgi_params[loop] = pair;
+
+ /* Remember the start of this name=value pair */
+ equals = pair;
+
+ /* Find the start of the next name=value pair and replace the delimiter
+ * with a 0 to terminate the previous pair string. */
+ pair = strchr(pair, '&');
+ if (pair) {
+ *pair = '\0';
+ pair++;
+ } else {
+ /* We didn't find a new parameter so find the end of the URI and
+ * replace the space with a '\0' */
+ pair = strchr(equals, ' ');
+ if (pair) {
+ *pair = '\0';
+ }
+
+ /* Revert to NULL so that we exit the loop as expected. */
+ pair = NULL;
+ }
+
+ /* Now find the '=' in the previous pair, replace it with '\0' and save
+ * the parameter value string. */
+ equals = strchr(equals, '=');
+ if (equals) {
+ *equals = '\0';
+ http_cgi_param_vals[loop] = equals + 1;
+ } else {
+ http_cgi_param_vals[loop] = NULL;
+ }
+ }
+
+ return loop;
+}
+#endif /* LWIP_HTTPD_CGI || LWIP_HTTPD_CGI_SSI */
+
+#if LWIP_HTTPD_SSI
+/**
+ * Insert a tag (found in an shtml in the form of "" into the file.
+ * The tag's name is stored in ssi->tag_name (NULL-terminated), the replacement
+ * should be written to hs->tag_insert (up to a length of LWIP_HTTPD_MAX_TAG_INSERT_LEN).
+ * The amount of data written is stored to ssi->tag_insert_len.
+ *
+ * @todo: return tag_insert_len - maybe it can be removed from struct http_state?
+ *
+ * @param hs http connection state
+ */
+static void
+get_tag_insert(struct http_state *hs)
+{
+#if LWIP_HTTPD_SSI_RAW
+ const char *tag;
+#else /* LWIP_HTTPD_SSI_RAW */
+ int tag;
+#endif /* LWIP_HTTPD_SSI_RAW */
+ size_t len;
+ struct http_ssi_state *ssi;
+#if LWIP_HTTPD_SSI_MULTIPART
+ u16_t current_tag_part;
+#endif /* LWIP_HTTPD_SSI_MULTIPART */
+
+ LWIP_ASSERT("hs != NULL", hs != NULL);
+ ssi = hs->ssi;
+ LWIP_ASSERT("ssi != NULL", ssi != NULL);
+#if LWIP_HTTPD_SSI_MULTIPART
+ current_tag_part = ssi->tag_part;
+ ssi->tag_part = HTTPD_LAST_TAG_PART;
+#endif /* LWIP_HTTPD_SSI_MULTIPART */
+#if LWIP_HTTPD_SSI_RAW
+ tag = ssi->tag_name;
+#endif
+
+ if (httpd_ssi_handler
+#if !LWIP_HTTPD_SSI_RAW
+ && httpd_tags && httpd_num_tags
+#endif /* !LWIP_HTTPD_SSI_RAW */
+ ) {
+
+ /* Find this tag in the list we have been provided. */
+#if LWIP_HTTPD_SSI_RAW
+ {
+#else /* LWIP_HTTPD_SSI_RAW */
+ for (tag = 0; tag < httpd_num_tags; tag++) {
+ if (strcmp(ssi->tag_name, httpd_tags[tag]) == 0)
+#endif /* LWIP_HTTPD_SSI_RAW */
+ {
+ ssi->tag_insert_len = httpd_ssi_handler(tag, ssi->tag_insert,
+ LWIP_HTTPD_MAX_TAG_INSERT_LEN
+#if LWIP_HTTPD_SSI_MULTIPART
+ , current_tag_part, &ssi->tag_part
+#endif /* LWIP_HTTPD_SSI_MULTIPART */
+#if LWIP_HTTPD_FILE_STATE
+ , (hs->handle ? hs->handle->state : NULL)
+#endif /* LWIP_HTTPD_FILE_STATE */
+ );
+#if LWIP_HTTPD_SSI_RAW
+ if (ssi->tag_insert_len != HTTPD_SSI_TAG_UNKNOWN)
+#endif /* LWIP_HTTPD_SSI_RAW */
+ {
+ return;
+ }
+ }
+ }
+ }
+
+ /* If we drop out, we were asked to serve a page which contains tags that
+ * we don't have a handler for. Merely echo back the tags with an error
+ * marker. */
+#define UNKNOWN_TAG1_TEXT "***UNKNOWN TAG "
+#define UNKNOWN_TAG1_LEN 18
+#define UNKNOWN_TAG2_TEXT "***"
+#define UNKNOWN_TAG2_LEN 7
+ len = LWIP_MIN(sizeof(ssi->tag_name), LWIP_MIN(strlen(ssi->tag_name),
+ LWIP_HTTPD_MAX_TAG_INSERT_LEN - (UNKNOWN_TAG1_LEN + UNKNOWN_TAG2_LEN)));
+ MEMCPY(ssi->tag_insert, UNKNOWN_TAG1_TEXT, UNKNOWN_TAG1_LEN);
+ MEMCPY(&ssi->tag_insert[UNKNOWN_TAG1_LEN], ssi->tag_name, len);
+ MEMCPY(&ssi->tag_insert[UNKNOWN_TAG1_LEN + len], UNKNOWN_TAG2_TEXT, UNKNOWN_TAG2_LEN);
+ ssi->tag_insert[UNKNOWN_TAG1_LEN + len + UNKNOWN_TAG2_LEN] = 0;
+
+ len = strlen(ssi->tag_insert);
+ LWIP_ASSERT("len <= 0xffff", len <= 0xffff);
+ ssi->tag_insert_len = (u16_t)len;
+}
+#endif /* LWIP_HTTPD_SSI */
+
+#if LWIP_HTTPD_DYNAMIC_HEADERS
+/**
+ * Generate the relevant HTTP headers for the given filename and write
+ * them into the supplied buffer.
+ */
+static void
+get_http_headers(struct http_state *hs, const char *uri)
+{
+ size_t content_type;
+ char *tmp;
+ char *ext;
+ char *vars;
+
+ /* In all cases, the second header we send is the server identification
+ so set it here. */
+ hs->hdrs[HDR_STRINGS_IDX_SERVER_NAME] = g_psHTTPHeaderStrings[HTTP_HDR_SERVER];
+ hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEEPALIVE] = NULL;
+ hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_NR] = NULL;
+
+ /* Is this a normal file or the special case we use to send back the
+ default "404: Page not found" response? */
+ if (uri == NULL) {
+ hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_FOUND];
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+ if (hs->keepalive) {
+ hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = g_psHTTPHeaderStrings[DEFAULT_404_HTML_PERSISTENT];
+ } else
+#endif
+ {
+ hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = g_psHTTPHeaderStrings[DEFAULT_404_HTML];
+ }
+
+ /* Set up to send the first header string. */
+ hs->hdr_index = 0;
+ hs->hdr_pos = 0;
+ return;
+ }
+ /* We are dealing with a particular filename. Look for one other
+ special case. We assume that any filename with "404" in it must be
+ indicative of a 404 server error whereas all other files require
+ the 200 OK header. */
+ if (strstr(uri, "404")) {
+ hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_FOUND];
+ } else if (strstr(uri, "400")) {
+ hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_BAD_REQUEST];
+ } else if (strstr(uri, "501")) {
+ hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_IMPL];
+ } else {
+ hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_OK];
+ }
+
+ /* Determine if the URI has any variables and, if so, temporarily remove
+ them. */
+ vars = strchr(uri, '?');
+ if (vars) {
+ *vars = '\0';
+ }
+
+ /* Get a pointer to the file extension. We find this by looking for the
+ last occurrence of "." in the filename passed. */
+ ext = NULL;
+ tmp = strchr(uri, '.');
+ while (tmp) {
+ ext = tmp + 1;
+ tmp = strchr(ext, '.');
+ }
+ if (ext != NULL) {
+ /* Now determine the content type and add the relevant header for that. */
+ for (content_type = 0; content_type < NUM_HTTP_HEADERS; content_type++) {
+ /* Have we found a matching extension? */
+ if (!lwip_stricmp(g_psHTTPHeaders[content_type].extension, ext)) {
+ break;
+ }
+ }
+ } else {
+ content_type = NUM_HTTP_HEADERS;
+ }
+
+ /* Reinstate the parameter marker if there was one in the original URI. */
+ if (vars) {
+ *vars = '?';
+ }
+
+#if LWIP_HTTPD_OMIT_HEADER_FOR_EXTENSIONLESS_URI
+ /* Does the URL passed have any file extension? If not, we assume it
+ is a special-case URL used for control state notification and we do
+ not send any HTTP headers with the response. */
+ if (!ext) {
+ /* Force the header index to a value indicating that all headers
+ have already been sent. */
+ hs->hdr_index = NUM_FILE_HDR_STRINGS;
+ return;
+ }
+#endif /* LWIP_HTTPD_OMIT_HEADER_FOR_EXTENSIONLESS_URI */
+ /* Did we find a matching extension? */
+ if (content_type < NUM_HTTP_HEADERS) {
+ /* yes, store it */
+ hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = g_psHTTPHeaders[content_type].content_type;
+ } else if (!ext) {
+ /* no, no extension found -> use binary transfer to prevent the browser adding '.txt' on save */
+ hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = HTTP_HDR_APP;
+ } else {
+ /* No - use the default, plain text file type. */
+ hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = HTTP_HDR_DEFAULT_TYPE;
+ }
+ /* Set up to send the first header string. */
+ hs->hdr_index = 0;
+ hs->hdr_pos = 0;
+}
+
+/* Add content-length header? */
+static void
+get_http_content_length(struct http_state *hs)
+{
+ u8_t add_content_len = 0;
+
+ LWIP_ASSERT("already been here?", hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEEPALIVE] == NULL);
+
+ add_content_len = 0;
+#if LWIP_HTTPD_SSI
+ if (hs->ssi == NULL) /* @todo: get maximum file length from SSI */
+#endif /* LWIP_HTTPD_SSI */
+ {
+ if ((hs->handle != NULL) && (hs->handle->flags & FS_FILE_FLAGS_HEADER_PERSISTENT)) {
+ add_content_len = 1;
+ }
+ }
+ if (add_content_len) {
+ size_t len;
+ lwip_itoa(hs->hdr_content_len, (size_t)LWIP_HTTPD_MAX_CONTENT_LEN_SIZE,
+ hs->handle->len);
+ len = strlen(hs->hdr_content_len);
+ if (len <= LWIP_HTTPD_MAX_CONTENT_LEN_SIZE - LWIP_HTTPD_MAX_CONTENT_LEN_OFFSET) {
+ SMEMCPY(&hs->hdr_content_len[len], CRLF, 3);
+ hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_NR] = hs->hdr_content_len;
+ } else {
+ add_content_len = 0;
+ }
+ }
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+ if (add_content_len) {
+ hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEEPALIVE] = g_psHTTPHeaderStrings[HTTP_HDR_KEEPALIVE_LEN];
+ } else {
+ hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEEPALIVE] = g_psHTTPHeaderStrings[HTTP_HDR_CONN_CLOSE];
+ hs->keepalive = 0;
+ }
+#else /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */
+ if (add_content_len) {
+ hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEEPALIVE] = g_psHTTPHeaderStrings[HTTP_HDR_CONTENT_LENGTH];
+ }
+#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */
+}
+
+/** Sub-function of http_send(): send dynamic headers
+ *
+ * @returns: - HTTP_NO_DATA_TO_SEND: no new data has been enqueued
+ * - HTTP_DATA_TO_SEND_CONTINUE: continue with sending HTTP body
+ * - HTTP_DATA_TO_SEND_BREAK: data has been enqueued, headers pending,
+ * so don't send HTTP body yet
+ * - HTTP_DATA_TO_SEND_FREED: http_state and pcb are already freed
+ */
+static u8_t
+http_send_headers(struct altcp_pcb *pcb, struct http_state *hs)
+{
+ err_t err;
+ u16_t len;
+ u8_t data_to_send = HTTP_NO_DATA_TO_SEND;
+ u16_t hdrlen, sendlen;
+
+ if (hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEEPALIVE] == NULL) {
+ /* set up "content-length" and "connection:" headers */
+ get_http_content_length(hs);
+ }
+
+ /* How much data can we send? */
+ len = altcp_sndbuf(pcb);
+ sendlen = len;
+
+ while (len && (hs->hdr_index < NUM_FILE_HDR_STRINGS) && sendlen) {
+ const void *ptr;
+ u16_t old_sendlen;
+ u8_t apiflags;
+ /* How much do we have to send from the current header? */
+ hdrlen = (u16_t)strlen(hs->hdrs[hs->hdr_index]);
+
+ /* How much of this can we send? */
+ sendlen = (len < (hdrlen - hs->hdr_pos)) ? len : (hdrlen - hs->hdr_pos);
+
+ /* Send this amount of data or as much as we can given memory
+ * constraints. */
+ ptr = (const void *)(hs->hdrs[hs->hdr_index] + hs->hdr_pos);
+ old_sendlen = sendlen;
+ apiflags = HTTP_IS_HDR_VOLATILE(hs, ptr);
+ if (hs->hdr_index == HDR_STRINGS_IDX_CONTENT_LEN_NR) {
+ /* content-length is always volatile */
+ apiflags |= TCP_WRITE_FLAG_COPY;
+ }
+ if (hs->hdr_index < NUM_FILE_HDR_STRINGS - 1) {
+ apiflags |= TCP_WRITE_FLAG_MORE;
+ }
+ err = http_write(pcb, ptr, &sendlen, apiflags);
+ if ((err == ERR_OK) && (old_sendlen != sendlen)) {
+ /* Remember that we added some more data to be transmitted. */
+ data_to_send = HTTP_DATA_TO_SEND_CONTINUE;
+ } else if (err != ERR_OK) {
+ /* special case: http_write does not try to send 1 byte */
+ sendlen = 0;
+ }
+
+ /* Fix up the header position for the next time round. */
+ hs->hdr_pos += sendlen;
+ len -= sendlen;
+
+ /* Have we finished sending this string? */
+ if (hs->hdr_pos == hdrlen) {
+ /* Yes - move on to the next one */
+ hs->hdr_index++;
+ /* skip headers that are NULL (not all headers are required) */
+ while ((hs->hdr_index < NUM_FILE_HDR_STRINGS) &&
+ (hs->hdrs[hs->hdr_index] == NULL)) {
+ hs->hdr_index++;
+ }
+ hs->hdr_pos = 0;
+ }
+ }
+
+ if ((hs->hdr_index >= NUM_FILE_HDR_STRINGS) && (hs->file == NULL)) {
+ /* When we are at the end of the headers, check for data to send
+ * instead of waiting for ACK from remote side to continue
+ * (which would happen when sending files from async read). */
+ if (http_check_eof(pcb, hs)) {
+ data_to_send = HTTP_DATA_TO_SEND_BREAK;
+ } else {
+ /* At this point, for non-keepalive connections, hs is deallocated an
+ pcb is closed. */
+ return HTTP_DATA_TO_SEND_FREED;
+ }
+ }
+ /* If we get here and there are still header bytes to send, we send
+ * the header information we just wrote immediately. If there are no
+ * more headers to send, but we do have file data to send, drop through
+ * to try to send some file data too. */
+ if ((hs->hdr_index < NUM_FILE_HDR_STRINGS) || !hs->file) {
+ LWIP_DEBUGF(HTTPD_DEBUG, ("tcp_output\n"));
+ return HTTP_DATA_TO_SEND_BREAK;
+ }
+ return data_to_send;
+}
+#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
+
+/** Sub-function of http_send(): end-of-file (or block) is reached,
+ * either close the file or read the next block (if supported).
+ *
+ * @returns: 0 if the file is finished or no data has been read
+ * 1 if the file is not finished and data has been read
+ */
+static u8_t
+http_check_eof(struct altcp_pcb *pcb, struct http_state *hs)
+{
+ int bytes_left;
+#if LWIP_HTTPD_DYNAMIC_FILE_READ
+ int count;
+#ifdef HTTPD_MAX_WRITE_LEN
+ int max_write_len;
+#endif /* HTTPD_MAX_WRITE_LEN */
+#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */
+
+ /* Do we have a valid file handle? */
+ if (hs->handle == NULL) {
+ /* No - close the connection. */
+ http_eof(pcb, hs);
+ return 0;
+ }
+ bytes_left = fs_bytes_left(hs->handle);
+ if (bytes_left <= 0) {
+ /* We reached the end of the file so this request is done. */
+ LWIP_DEBUGF(HTTPD_DEBUG, ("End of file.\n"));
+ http_eof(pcb, hs);
+ return 0;
+ }
+#if LWIP_HTTPD_DYNAMIC_FILE_READ
+ /* Do we already have a send buffer allocated? */
+ if (hs->buf) {
+ /* Yes - get the length of the buffer */
+ count = LWIP_MIN(hs->buf_len, bytes_left);
+ } else {
+ /* We don't have a send buffer so allocate one now */
+ count = altcp_sndbuf(pcb);
+ if (bytes_left < count) {
+ count = bytes_left;
+ }
+#ifdef HTTPD_MAX_WRITE_LEN
+ /* Additional limitation: e.g. don't enqueue more than 2*mss at once */
+ max_write_len = HTTPD_MAX_WRITE_LEN(pcb);
+ if (count > max_write_len) {
+ count = max_write_len;
+ }
+#endif /* HTTPD_MAX_WRITE_LEN */
+ do {
+ hs->buf = (char *)mem_malloc((mem_size_t)count);
+ if (hs->buf != NULL) {
+ hs->buf_len = count;
+ break;
+ }
+ count = count / 2;
+ } while (count > 100);
+
+ /* Did we get a send buffer? If not, return immediately. */
+ if (hs->buf == NULL) {
+ LWIP_DEBUGF(HTTPD_DEBUG, ("No buff\n"));
+ return 0;
+ }
+ }
+
+ /* Read a block of data from the file. */
+ LWIP_DEBUGF(HTTPD_DEBUG, ("Trying to read %d bytes.\n", count));
+
+#if LWIP_HTTPD_FS_ASYNC_READ
+ count = fs_read_async(hs->handle, hs->buf, count, http_continue, hs);
+#else /* LWIP_HTTPD_FS_ASYNC_READ */
+ count = fs_read(hs->handle, hs->buf, count);
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+ if (count < 0) {
+ if (count == FS_READ_DELAYED) {
+ /* Delayed read, wait for FS to unblock us */
+ return 0;
+ }
+ /* We reached the end of the file so this request is done.
+ * @todo: close here for HTTP/1.1 when reading file fails */
+ LWIP_DEBUGF(HTTPD_DEBUG, ("End of file.\n"));
+ http_eof(pcb, hs);
+ return 0;
+ }
+
+ /* Set up to send the block of data we just read */
+ LWIP_DEBUGF(HTTPD_DEBUG, ("Read %d bytes.\n", count));
+ hs->left = count;
+ hs->file = hs->buf;
+#if LWIP_HTTPD_SSI
+ if (hs->ssi) {
+ hs->ssi->parse_left = count;
+ hs->ssi->parsed = hs->buf;
+ }
+#endif /* LWIP_HTTPD_SSI */
+#else /* LWIP_HTTPD_DYNAMIC_FILE_READ */
+ LWIP_ASSERT("SSI and DYNAMIC_HEADERS turned off but eof not reached", 0);
+#endif /* LWIP_HTTPD_SSI || LWIP_HTTPD_DYNAMIC_HEADERS */
+ return 1;
+}
+
+/** Sub-function of http_send(): This is the normal send-routine for non-ssi files
+ *
+ * @returns: - 1: data has been written (so call tcp_ouput)
+ * - 0: no data has been written (no need to call tcp_output)
+ */
+static u8_t
+http_send_data_nonssi(struct altcp_pcb *pcb, struct http_state *hs)
+{
+ err_t err;
+ u16_t len;
+ u8_t data_to_send = 0;
+
+ /* We are not processing an SHTML file so no tag checking is necessary.
+ * Just send the data as we received it from the file. */
+ len = (u16_t)LWIP_MIN(hs->left, 0xffff);
+
+ err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs));
+ if (err == ERR_OK) {
+ data_to_send = 1;
+ hs->file += len;
+ hs->left -= len;
+ }
+
+ return data_to_send;
+}
+
+#if LWIP_HTTPD_SSI
+/** Sub-function of http_send(): This is the send-routine for ssi files
+ *
+ * @returns: - 1: data has been written (so call tcp_ouput)
+ * - 0: no data has been written (no need to call tcp_output)
+ */
+static u8_t
+http_send_data_ssi(struct altcp_pcb *pcb, struct http_state *hs)
+{
+ err_t err = ERR_OK;
+ u16_t len;
+ u8_t data_to_send = 0;
+ u8_t tag_type;
+
+ struct http_ssi_state *ssi = hs->ssi;
+ LWIP_ASSERT("ssi != NULL", ssi != NULL);
+ /* We are processing an SHTML file so need to scan for tags and replace
+ * them with insert strings. We need to be careful here since a tag may
+ * straddle the boundary of two blocks read from the file and we may also
+ * have to split the insert string between two tcp_write operations. */
+
+ /* How much data could we send? */
+ len = altcp_sndbuf(pcb);
+
+ /* Do we have remaining data to send before parsing more? */
+ if (ssi->parsed > hs->file) {
+ len = (u16_t)LWIP_MIN(ssi->parsed - hs->file, 0xffff);
+
+ err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs));
+ if (err == ERR_OK) {
+ data_to_send = 1;
+ hs->file += len;
+ hs->left -= len;
+ }
+
+ /* If the send buffer is full, return now. */
+ if (altcp_sndbuf(pcb) == 0) {
+ return data_to_send;
+ }
+ }
+
+ LWIP_DEBUGF(HTTPD_DEBUG, ("State %d, %d left\n", ssi->tag_state, (int)ssi->parse_left));
+
+ /* We have sent all the data that was already parsed so continue parsing
+ * the buffer contents looking for SSI tags. */
+ while (((ssi->tag_state == TAG_SENDING) || ssi->parse_left) && (err == ERR_OK)) {
+ if (len == 0) {
+ return data_to_send;
+ }
+ switch (ssi->tag_state) {
+ case TAG_NONE:
+ /* We are not currently processing an SSI tag so scan for the
+ * start of the lead-in marker. */
+ for (tag_type = 0; tag_type < LWIP_ARRAYSIZE(http_ssi_tag_desc); tag_type++) {
+ if (*ssi->parsed == http_ssi_tag_desc[tag_type].lead_in[0]) {
+ /* We found what could be the lead-in for a new tag so change
+ * state appropriately. */
+ ssi->tag_type = tag_type;
+ ssi->tag_state = TAG_LEADIN;
+ ssi->tag_index = 1;
+ #if !LWIP_HTTPD_SSI_INCLUDE_TAG
+ ssi->tag_started = ssi->parsed;
+ #endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG */
+ break;
+ }
+ }
+
+ /* Move on to the next character in the buffer */
+ ssi->parse_left--;
+ ssi->parsed++;
+ break;
+
+ case TAG_LEADIN:
+ /* We are processing the lead-in marker, looking for the start of
+ * the tag name. */
+
+ /* Have we reached the end of the leadin? */
+ if (http_ssi_tag_desc[ssi->tag_type].lead_in[ssi->tag_index] == 0) {
+ ssi->tag_index = 0;
+ ssi->tag_state = TAG_FOUND;
+ } else {
+ /* Have we found the next character we expect for the tag leadin? */
+ if (*ssi->parsed == http_ssi_tag_desc[ssi->tag_type].lead_in[ssi->tag_index]) {
+ /* Yes - move to the next one unless we have found the complete
+ * leadin, in which case we start looking for the tag itself */
+ ssi->tag_index++;
+ } else {
+ /* We found an unexpected character so this is not a tag. Move
+ * back to idle state. */
+ ssi->tag_state = TAG_NONE;
+ }
+
+ /* Move on to the next character in the buffer */
+ ssi->parse_left--;
+ ssi->parsed++;
+ }
+ break;
+
+ case TAG_FOUND:
+ /* We are reading the tag name, looking for the start of the
+ * lead-out marker and removing any whitespace found. */
+
+ /* Remove leading whitespace between the tag leading and the first
+ * tag name character. */
+ if ((ssi->tag_index == 0) && ((*ssi->parsed == ' ') ||
+ (*ssi->parsed == '\t') || (*ssi->parsed == '\n') ||
+ (*ssi->parsed == '\r'))) {
+ /* Move on to the next character in the buffer */
+ ssi->parse_left--;
+ ssi->parsed++;
+ break;
+ }
+
+ /* Have we found the end of the tag name? This is signalled by
+ * us finding the first leadout character or whitespace */
+ if ((*ssi->parsed == http_ssi_tag_desc[ssi->tag_type].lead_out[0]) ||
+ (*ssi->parsed == ' ') || (*ssi->parsed == '\t') ||
+ (*ssi->parsed == '\n') || (*ssi->parsed == '\r')) {
+
+ if (ssi->tag_index == 0) {
+ /* We read a zero length tag so ignore it. */
+ ssi->tag_state = TAG_NONE;
+ } else {
+ /* We read a non-empty tag so go ahead and look for the
+ * leadout string. */
+ ssi->tag_state = TAG_LEADOUT;
+ LWIP_ASSERT("ssi->tag_index <= 0xff", ssi->tag_index <= 0xff);
+ ssi->tag_name_len = (u8_t)ssi->tag_index;
+ ssi->tag_name[ssi->tag_index] = '\0';
+ if (*ssi->parsed == http_ssi_tag_desc[ssi->tag_type].lead_out[0]) {
+ ssi->tag_index = 1;
+ } else {
+ ssi->tag_index = 0;
+ }
+ }
+ } else {
+ /* This character is part of the tag name so save it */
+ if (ssi->tag_index < LWIP_HTTPD_MAX_TAG_NAME_LEN) {
+ ssi->tag_name[ssi->tag_index++] = *ssi->parsed;
+ } else {
+ /* The tag was too long so ignore it. */
+ ssi->tag_state = TAG_NONE;
+ }
+ }
+
+ /* Move on to the next character in the buffer */
+ ssi->parse_left--;
+ ssi->parsed++;
+
+ break;
+
+ /* We are looking for the end of the lead-out marker. */
+ case TAG_LEADOUT:
+ /* Remove leading whitespace between the tag leading and the first
+ * tag leadout character. */
+ if ((ssi->tag_index == 0) && ((*ssi->parsed == ' ') ||
+ (*ssi->parsed == '\t') || (*ssi->parsed == '\n') ||
+ (*ssi->parsed == '\r'))) {
+ /* Move on to the next character in the buffer */
+ ssi->parse_left--;
+ ssi->parsed++;
+ break;
+ }
+
+ /* Have we found the next character we expect for the tag leadout? */
+ if (*ssi->parsed == http_ssi_tag_desc[ssi->tag_type].lead_out[ssi->tag_index]) {
+ /* Yes - move to the next one unless we have found the complete
+ * leadout, in which case we need to call the client to process
+ * the tag. */
+
+ /* Move on to the next character in the buffer */
+ ssi->parse_left--;
+ ssi->parsed++;
+ ssi->tag_index++;
+
+ if (http_ssi_tag_desc[ssi->tag_type].lead_out[ssi->tag_index] == 0) {
+ /* Call the client to ask for the insert string for the
+ * tag we just found. */
+#if LWIP_HTTPD_SSI_MULTIPART
+ ssi->tag_part = 0; /* start with tag part 0 */
+#endif /* LWIP_HTTPD_SSI_MULTIPART */
+ get_tag_insert(hs);
+
+ /* Next time through, we are going to be sending data
+ * immediately, either the end of the block we start
+ * sending here or the insert string. */
+ ssi->tag_index = 0;
+ ssi->tag_state = TAG_SENDING;
+ ssi->tag_end = ssi->parsed;
+#if !LWIP_HTTPD_SSI_INCLUDE_TAG
+ ssi->parsed = ssi->tag_started;
+#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/
+
+ /* If there is any unsent data in the buffer prior to the
+ * tag, we need to send it now. */
+ if (ssi->tag_end > hs->file) {
+ /* How much of the data can we send? */
+#if LWIP_HTTPD_SSI_INCLUDE_TAG
+ len = (u16_t)LWIP_MIN(ssi->tag_end - hs->file, 0xffff);
+#else /* LWIP_HTTPD_SSI_INCLUDE_TAG*/
+ /* we would include the tag in sending */
+ len = (u16_t)LWIP_MIN(ssi->tag_started - hs->file, 0xffff);
+#endif /* LWIP_HTTPD_SSI_INCLUDE_TAG*/
+
+ err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs));
+ if (err == ERR_OK) {
+ data_to_send = 1;
+#if !LWIP_HTTPD_SSI_INCLUDE_TAG
+ if (ssi->tag_started <= hs->file) {
+ /* pretend to have sent the tag, too */
+ len += (u16_t)(ssi->tag_end - ssi->tag_started);
+ }
+#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/
+ hs->file += len;
+ hs->left -= len;
+ }
+ }
+ }
+ } else {
+ /* We found an unexpected character so this is not a tag. Move
+ * back to idle state. */
+ ssi->parse_left--;
+ ssi->parsed++;
+ ssi->tag_state = TAG_NONE;
+ }
+ break;
+
+ /*
+ * We have found a valid tag and are in the process of sending
+ * data as a result of that discovery. We send either remaining data
+ * from the file prior to the insert point or the insert string itself.
+ */
+ case TAG_SENDING:
+ /* Do we have any remaining file data to send from the buffer prior
+ * to the tag? */
+ if (ssi->tag_end > hs->file) {
+ /* How much of the data can we send? */
+#if LWIP_HTTPD_SSI_INCLUDE_TAG
+ len = (u16_t)LWIP_MIN(ssi->tag_end - hs->file, 0xffff);
+#else /* LWIP_HTTPD_SSI_INCLUDE_TAG*/
+ LWIP_ASSERT("hs->started >= hs->file", ssi->tag_started >= hs->file);
+ /* we would include the tag in sending */
+ len = (u16_t)LWIP_MIN(ssi->tag_started - hs->file, 0xffff);
+#endif /* LWIP_HTTPD_SSI_INCLUDE_TAG*/
+ if (len != 0) {
+ err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs));
+ } else {
+ err = ERR_OK;
+ }
+ if (err == ERR_OK) {
+ data_to_send = 1;
+#if !LWIP_HTTPD_SSI_INCLUDE_TAG
+ if (ssi->tag_started <= hs->file) {
+ /* pretend to have sent the tag, too */
+ len += (u16_t)(ssi->tag_end - ssi->tag_started);
+ }
+#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/
+ hs->file += len;
+ hs->left -= len;
+ }
+ } else {
+#if LWIP_HTTPD_SSI_MULTIPART
+ if (ssi->tag_index >= ssi->tag_insert_len) {
+ /* Did the last SSIHandler have more to send? */
+ if (ssi->tag_part != HTTPD_LAST_TAG_PART) {
+ /* If so, call it again */
+ ssi->tag_index = 0;
+ get_tag_insert(hs);
+ }
+ }
+#endif /* LWIP_HTTPD_SSI_MULTIPART */
+
+ /* Do we still have insert data left to send? */
+ if (ssi->tag_index < ssi->tag_insert_len) {
+ /* We are sending the insert string itself. How much of the
+ * insert can we send? */
+ len = (ssi->tag_insert_len - ssi->tag_index);
+
+ /* Note that we set the copy flag here since we only have a
+ * single tag insert buffer per connection. If we don't do
+ * this, insert corruption can occur if more than one insert
+ * is processed before we call tcp_output. */
+ err = http_write(pcb, &(ssi->tag_insert[ssi->tag_index]), &len,
+ HTTP_IS_TAG_VOLATILE(hs));
+ if (err == ERR_OK) {
+ data_to_send = 1;
+ ssi->tag_index += len;
+ /* Don't return here: keep on sending data */
+ }
+ } else {
+#if LWIP_HTTPD_SSI_MULTIPART
+ if (ssi->tag_part == HTTPD_LAST_TAG_PART)
+#endif /* LWIP_HTTPD_SSI_MULTIPART */
+ {
+ /* We have sent all the insert data so go back to looking for
+ * a new tag. */
+ LWIP_DEBUGF(HTTPD_DEBUG, ("Everything sent.\n"));
+ ssi->tag_index = 0;
+ ssi->tag_state = TAG_NONE;
+#if !LWIP_HTTPD_SSI_INCLUDE_TAG
+ ssi->parsed = ssi->tag_end;
+#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ /* If we drop out of the end of the for loop, this implies we must have
+ * file data to send so send it now. In TAG_SENDING state, we've already
+ * handled this so skip the send if that's the case. */
+ if ((ssi->tag_state != TAG_SENDING) && (ssi->parsed > hs->file)) {
+#if LWIP_HTTPD_DYNAMIC_FILE_READ && !LWIP_HTTPD_SSI_INCLUDE_TAG
+ if ((ssi->tag_state != TAG_NONE) && (ssi->tag_started > ssi->tag_end)) {
+ /* If we found tag on the edge of the read buffer: just throw away the first part
+ (we have copied/saved everything required for parsing on later). */
+ len = (u16_t)(ssi->tag_started - hs->file);
+ hs->left -= (ssi->parsed - ssi->tag_started);
+ ssi->parsed = ssi->tag_started;
+ ssi->tag_started = hs->buf;
+ } else
+#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ && !LWIP_HTTPD_SSI_INCLUDE_TAG */
+ {
+ len = (u16_t)LWIP_MIN(ssi->parsed - hs->file, 0xffff);
+ }
+
+ err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs));
+ if (err == ERR_OK) {
+ data_to_send = 1;
+ hs->file += len;
+ hs->left -= len;
+ }
+ }
+ return data_to_send;
+}
+#endif /* LWIP_HTTPD_SSI */
+
+/**
+ * Try to send more data on this pcb.
+ *
+ * @param pcb the pcb to send data
+ * @param hs connection state
+ */
+static u8_t
+http_send(struct altcp_pcb *pcb, struct http_state *hs)
+{
+ u8_t data_to_send = HTTP_NO_DATA_TO_SEND;
+
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_send: pcb=%p hs=%p left=%d\n", (void *)pcb,
+ (void *)hs, hs != NULL ? (int)hs->left : 0));
+
+#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND
+ if (hs->unrecved_bytes != 0) {
+ return 0;
+ }
+#endif /* LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND */
+
+ /* If we were passed a NULL state structure pointer, ignore the call. */
+ if (hs == NULL) {
+ return 0;
+ }
+
+#if LWIP_HTTPD_FS_ASYNC_READ
+ /* Check if we are allowed to read from this file.
+ (e.g. SSI might want to delay sending until data is available) */
+ if (!fs_is_file_ready(hs->handle, http_continue, hs)) {
+ return 0;
+ }
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+
+#if LWIP_HTTPD_DYNAMIC_HEADERS
+ /* Do we have any more header data to send for this file? */
+ if (hs->hdr_index < NUM_FILE_HDR_STRINGS) {
+ data_to_send = http_send_headers(pcb, hs);
+ if ((data_to_send == HTTP_DATA_TO_SEND_FREED) ||
+ ((data_to_send != HTTP_DATA_TO_SEND_CONTINUE) &&
+ (hs->hdr_index < NUM_FILE_HDR_STRINGS))) {
+ return data_to_send;
+ }
+ }
+#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
+
+ /* Have we run out of file data to send? If so, we need to read the next
+ * block from the file. */
+ if (hs->left == 0) {
+ if (!http_check_eof(pcb, hs)) {
+ return 0;
+ }
+ }
+
+#if LWIP_HTTPD_SSI
+ if (hs->ssi) {
+ data_to_send = http_send_data_ssi(pcb, hs);
+ } else
+#endif /* LWIP_HTTPD_SSI */
+ {
+ data_to_send = http_send_data_nonssi(pcb, hs);
+ }
+
+ if ((hs->left == 0) && (fs_bytes_left(hs->handle) <= 0)) {
+ /* We reached the end of the file so this request is done.
+ * This adds the FIN flag right into the last data segment. */
+ LWIP_DEBUGF(HTTPD_DEBUG, ("End of file.\n"));
+ http_eof(pcb, hs);
+ return 0;
+ }
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("send_data end.\n"));
+ return data_to_send;
+}
+
+#if LWIP_HTTPD_SUPPORT_EXTSTATUS
+/** Initialize a http connection with a file to send for an error message
+ *
+ * @param hs http connection state
+ * @param error_nr HTTP error number
+ * @return ERR_OK if file was found and hs has been initialized correctly
+ * another err_t otherwise
+ */
+static err_t
+http_find_error_file(struct http_state *hs, u16_t error_nr)
+{
+ const char *uri, *uri1, *uri2, *uri3;
+
+ if (error_nr == 501) {
+ uri1 = "/501.html";
+ uri2 = "/501.htm";
+ uri3 = "/501.shtml";
+ } else {
+ /* 400 (bad request is the default) */
+ uri1 = "/400.html";
+ uri2 = "/400.htm";
+ uri3 = "/400.shtml";
+ }
+ if (fs_open(&hs->file_handle, uri1) == ERR_OK) {
+ uri = uri1;
+ } else if (fs_open(&hs->file_handle, uri2) == ERR_OK) {
+ uri = uri2;
+ } else if (fs_open(&hs->file_handle, uri3) == ERR_OK) {
+ uri = uri3;
+ } else {
+ LWIP_DEBUGF(HTTPD_DEBUG, ("Error page for error %"U16_F" not found\n",
+ error_nr));
+ return ERR_ARG;
+ }
+ return http_init_file(hs, &hs->file_handle, 0, uri, 0, NULL);
+}
+#else /* LWIP_HTTPD_SUPPORT_EXTSTATUS */
+#define http_find_error_file(hs, error_nr) ERR_ARG
+#endif /* LWIP_HTTPD_SUPPORT_EXTSTATUS */
+
+/**
+ * Get the file struct for a 404 error page.
+ * Tries some file names and returns NULL if none found.
+ *
+ * @param uri pointer that receives the actual file name URI
+ * @return file struct for the error page or NULL no matching file was found
+ */
+static struct fs_file *
+http_get_404_file(struct http_state *hs, const char **uri)
+{
+ err_t err;
+
+ *uri = "/404.html";
+ err = fs_open(&hs->file_handle, *uri);
+ if (err != ERR_OK) {
+ /* 404.html doesn't exist. Try 404.htm instead. */
+ *uri = "/404.htm";
+ err = fs_open(&hs->file_handle, *uri);
+ if (err != ERR_OK) {
+ /* 404.htm doesn't exist either. Try 404.shtml instead. */
+ *uri = "/404.shtml";
+ err = fs_open(&hs->file_handle, *uri);
+ if (err != ERR_OK) {
+ /* 404.htm doesn't exist either. Indicate to the caller that it should
+ * send back a default 404 page.
+ */
+ *uri = NULL;
+ return NULL;
+ }
+ }
+ }
+
+ return &hs->file_handle;
+}
+
+#if LWIP_HTTPD_SUPPORT_POST
+static err_t
+http_handle_post_finished(struct http_state *hs)
+{
+#if LWIP_HTTPD_POST_MANUAL_WND
+ /* Prevent multiple calls to httpd_post_finished, since it might have already
+ been called before from httpd_post_data_recved(). */
+ if (hs->post_finished) {
+ return ERR_OK;
+ }
+ hs->post_finished = 1;
+#endif /* LWIP_HTTPD_POST_MANUAL_WND */
+ /* application error or POST finished */
+ /* NULL-terminate the buffer */
+ http_uri_buf[0] = 0;
+ httpd_post_finished(hs, http_uri_buf, LWIP_HTTPD_URI_BUF_LEN);
+ return http_find_file(hs, http_uri_buf, 0);
+}
+
+/** Pass received POST body data to the application and correctly handle
+ * returning a response document or closing the connection.
+ * ATTENTION: The application is responsible for the pbuf now, so don't free it!
+ *
+ * @param hs http connection state
+ * @param p pbuf to pass to the application
+ * @return ERR_OK if passed successfully, another err_t if the response file
+ * hasn't been found (after POST finished)
+ */
+static err_t
+http_post_rxpbuf(struct http_state *hs, struct pbuf *p)
+{
+ err_t err;
+
+ if (p != NULL) {
+ /* adjust remaining Content-Length */
+ if (hs->post_content_len_left < p->tot_len) {
+ hs->post_content_len_left = 0;
+ } else {
+ hs->post_content_len_left -= p->tot_len;
+ }
+ }
+#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND
+ /* prevent connection being closed if httpd_post_data_recved() is called nested */
+ hs->unrecved_bytes++;
+#endif
+ if (p != NULL) {
+ err = httpd_post_receive_data(hs, p);
+ } else {
+ err = ERR_OK;
+ }
+#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND
+ hs->unrecved_bytes--;
+#endif
+ if (err != ERR_OK) {
+ /* Ignore remaining content in case of application error */
+ hs->post_content_len_left = 0;
+ }
+ if (hs->post_content_len_left == 0) {
+#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND
+ if (hs->unrecved_bytes != 0) {
+ return ERR_OK;
+ }
+#endif /* LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND */
+ /* application error or POST finished */
+ return http_handle_post_finished(hs);
+ }
+
+ return ERR_OK;
+}
+
+/** Handle a post request. Called from http_parse_request when method 'POST'
+ * is found.
+ *
+ * @param p The input pbuf (containing the POST header and body).
+ * @param hs The http connection state.
+ * @param data HTTP request (header and part of body) from input pbuf(s).
+ * @param data_len Size of 'data'.
+ * @param uri The HTTP URI parsed from input pbuf(s).
+ * @param uri_end Pointer to the end of 'uri' (here, the rest of the HTTP
+ * header starts).
+ * @return ERR_OK: POST correctly parsed and accepted by the application.
+ * ERR_INPROGRESS: POST not completely parsed (no error yet)
+ * another err_t: Error parsing POST or denied by the application
+ */
+static err_t
+http_post_request(struct pbuf *inp, struct http_state *hs,
+ char *data, u16_t data_len, char *uri, char *uri_end)
+{
+ err_t err;
+ /* search for end-of-header (first double-CRLF) */
+ char *crlfcrlf = lwip_strnstr(uri_end + 1, CRLF CRLF, data_len - (uri_end + 1 - data));
+
+ if (crlfcrlf != NULL) {
+ /* search for "Content-Length: " */
+#define HTTP_HDR_CONTENT_LEN "Content-Length: "
+#define HTTP_HDR_CONTENT_LEN_LEN 16
+#define HTTP_HDR_CONTENT_LEN_DIGIT_MAX_LEN 10
+ char *scontent_len = lwip_strnstr(uri_end + 1, HTTP_HDR_CONTENT_LEN, crlfcrlf - (uri_end + 1));
+ if (scontent_len != NULL) {
+ char *scontent_len_end = lwip_strnstr(scontent_len + HTTP_HDR_CONTENT_LEN_LEN, CRLF, HTTP_HDR_CONTENT_LEN_DIGIT_MAX_LEN);
+ if (scontent_len_end != NULL) {
+ int content_len;
+ char *content_len_num = scontent_len + HTTP_HDR_CONTENT_LEN_LEN;
+ content_len = atoi(content_len_num);
+ if (content_len == 0) {
+ /* if atoi returns 0 on error, fix this */
+ if ((content_len_num[0] != '0') || (content_len_num[1] != '\r')) {
+ content_len = -1;
+ }
+ }
+ if (content_len >= 0) {
+ /* adjust length of HTTP header passed to application */
+ const char *hdr_start_after_uri = uri_end + 1;
+ u16_t hdr_len = (u16_t)LWIP_MIN(data_len, crlfcrlf + 4 - data);
+ u16_t hdr_data_len = (u16_t)LWIP_MIN(data_len, crlfcrlf + 4 - hdr_start_after_uri);
+ u8_t post_auto_wnd = 1;
+ http_uri_buf[0] = 0;
+ /* trim http header */
+ *crlfcrlf = 0;
+ err = httpd_post_begin(hs, uri, hdr_start_after_uri, hdr_data_len, content_len,
+ http_uri_buf, LWIP_HTTPD_URI_BUF_LEN, &post_auto_wnd);
+ if (err == ERR_OK) {
+ /* try to pass in data of the first pbuf(s) */
+ struct pbuf *q = inp;
+ u16_t start_offset = hdr_len;
+#if LWIP_HTTPD_POST_MANUAL_WND
+ hs->no_auto_wnd = !post_auto_wnd;
+#endif /* LWIP_HTTPD_POST_MANUAL_WND */
+ /* set the Content-Length to be received for this POST */
+ hs->post_content_len_left = (u32_t)content_len;
+
+ /* get to the pbuf where the body starts */
+ while ((q != NULL) && (q->len <= start_offset)) {
+ start_offset -= q->len;
+ q = q->next;
+ }
+ if (q != NULL) {
+ /* hide the remaining HTTP header */
+ pbuf_remove_header(q, start_offset);
+#if LWIP_HTTPD_POST_MANUAL_WND
+ if (!post_auto_wnd) {
+ /* already tcp_recved() this data... */
+ hs->unrecved_bytes = q->tot_len;
+ }
+#endif /* LWIP_HTTPD_POST_MANUAL_WND */
+ pbuf_ref(q);
+ return http_post_rxpbuf(hs, q);
+ } else if (hs->post_content_len_left == 0) {
+ q = pbuf_alloc(PBUF_RAW, 0, PBUF_REF);
+ return http_post_rxpbuf(hs, q);
+ } else {
+ return ERR_OK;
+ }
+ } else {
+ /* return file passed from application */
+ return http_find_file(hs, http_uri_buf, 0);
+ }
+ } else {
+ LWIP_DEBUGF(HTTPD_DEBUG, ("POST received invalid Content-Length: %s\n",
+ content_len_num));
+ return ERR_ARG;
+ }
+ }
+ }
+ /* If we come here, headers are fully received (double-crlf), but Content-Length
+ was not included. Since this is currently the only supported method, we have
+ to fail in this case! */
+ LWIP_DEBUGF(HTTPD_DEBUG, ("Error when parsing Content-Length\n"));
+ return ERR_ARG;
+ }
+ /* if we come here, the POST is incomplete */
+#if LWIP_HTTPD_SUPPORT_REQUESTLIST
+ return ERR_INPROGRESS;
+#else /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+ return ERR_ARG;
+#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+}
+
+#if LWIP_HTTPD_POST_MANUAL_WND
+/**
+ * @ingroup httpd
+ * A POST implementation can call this function to update the TCP window.
+ * This can be used to throttle data reception (e.g. when received data is
+ * programmed to flash and data is received faster than programmed).
+ *
+ * @param connection A connection handle passed to httpd_post_begin for which
+ * httpd_post_finished has *NOT* been called yet!
+ * @param recved_len Length of data received (for window update)
+ */
+void httpd_post_data_recved(void *connection, u16_t recved_len)
+{
+ struct http_state *hs = (struct http_state *)connection;
+ if (hs != NULL) {
+ if (hs->no_auto_wnd) {
+ u16_t len = recved_len;
+ if (hs->unrecved_bytes >= recved_len) {
+ hs->unrecved_bytes -= recved_len;
+ } else {
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_LEVEL_WARNING, ("httpd_post_data_recved: recved_len too big\n"));
+ len = (u16_t)hs->unrecved_bytes;
+ hs->unrecved_bytes = 0;
+ }
+ if (hs->pcb != NULL) {
+ if (len != 0) {
+ altcp_recved(hs->pcb, len);
+ }
+ if ((hs->post_content_len_left == 0) && (hs->unrecved_bytes == 0)) {
+ /* finished handling POST */
+ http_handle_post_finished(hs);
+ http_send(hs->pcb, hs);
+ }
+ }
+ }
+ }
+}
+#endif /* LWIP_HTTPD_POST_MANUAL_WND */
+
+#endif /* LWIP_HTTPD_SUPPORT_POST */
+
+#if LWIP_HTTPD_FS_ASYNC_READ
+/** Try to send more data if file has been blocked before
+ * This is a callback function passed to fs_read_async().
+ */
+static void
+http_continue(void *connection)
+{
+ struct http_state *hs = (struct http_state *)connection;
+ LWIP_ASSERT_CORE_LOCKED();
+ if (hs && (hs->pcb) && (hs->handle)) {
+ LWIP_ASSERT("hs->pcb != NULL", hs->pcb != NULL);
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("httpd_continue: try to send more data\n"));
+ if (http_send(hs->pcb, hs)) {
+ /* If we wrote anything to be sent, go ahead and send it now. */
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("tcp_output\n"));
+ altcp_output(hs->pcb);
+ }
+ }
+}
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+
+/**
+ * When data has been received in the correct state, try to parse it
+ * as a HTTP request.
+ *
+ * @param inp the received pbuf
+ * @param hs the connection state
+ * @param pcb the altcp_pcb which received this packet
+ * @return ERR_OK if request was OK and hs has been initialized correctly
+ * ERR_INPROGRESS if request was OK so far but not fully received
+ * another err_t otherwise
+ */
+static err_t
+http_parse_request(struct pbuf *inp, struct http_state *hs, struct altcp_pcb *pcb)
+{
+ char *data;
+ char *crlf;
+ u16_t data_len;
+ struct pbuf *p = inp;
+#if LWIP_HTTPD_SUPPORT_REQUESTLIST
+ u16_t clen;
+#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+#if LWIP_HTTPD_SUPPORT_POST
+ err_t err;
+#endif /* LWIP_HTTPD_SUPPORT_POST */
+
+ LWIP_UNUSED_ARG(pcb); /* only used for post */
+ LWIP_ASSERT("p != NULL", p != NULL);
+ LWIP_ASSERT("hs != NULL", hs != NULL);
+
+ if ((hs->handle != NULL) || (hs->file != NULL)) {
+ LWIP_DEBUGF(HTTPD_DEBUG, ("Received data while sending a file\n"));
+ /* already sending a file */
+ /* @todo: abort? */
+ return ERR_USE;
+ }
+
+#if LWIP_HTTPD_SUPPORT_REQUESTLIST
+
+ LWIP_DEBUGF(HTTPD_DEBUG, ("Received %"U16_F" bytes\n", p->tot_len));
+
+ /* first check allowed characters in this pbuf? */
+
+ /* enqueue the pbuf */
+ if (hs->req == NULL) {
+ LWIP_DEBUGF(HTTPD_DEBUG, ("First pbuf\n"));
+ hs->req = p;
+ } else {
+ LWIP_DEBUGF(HTTPD_DEBUG, ("pbuf enqueued\n"));
+ pbuf_cat(hs->req, p);
+ }
+ /* increase pbuf ref counter as it is freed when we return but we want to
+ keep it on the req list */
+ pbuf_ref(p);
+
+ if (hs->req->next != NULL) {
+ data_len = LWIP_MIN(hs->req->tot_len, LWIP_HTTPD_MAX_REQ_LENGTH);
+ pbuf_copy_partial(hs->req, httpd_req_buf, data_len, 0);
+ data = httpd_req_buf;
+ } else
+#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+ {
+ data = (char *)p->payload;
+ data_len = p->len;
+ if (p->len != p->tot_len) {
+ LWIP_DEBUGF(HTTPD_DEBUG, ("Warning: incomplete header due to chained pbufs\n"));
+ }
+ }
+
+ /* received enough data for minimal request? */
+ if (data_len >= MIN_REQ_LEN) {
+ /* wait for CRLF before parsing anything */
+ crlf = lwip_strnstr(data, CRLF, data_len);
+ if (crlf != NULL) {
+#if LWIP_HTTPD_SUPPORT_POST
+ int is_post = 0;
+#endif /* LWIP_HTTPD_SUPPORT_POST */
+ int is_09 = 0;
+ char *sp1, *sp2;
+ u16_t left_len, uri_len;
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("CRLF received, parsing request\n"));
+ /* parse method */
+ if (!strncmp(data, "GET ", 4)) {
+ sp1 = data + 3;
+ /* received GET request */
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Received GET request\"\n"));
+#if LWIP_HTTPD_SUPPORT_POST
+ } else if (!strncmp(data, "POST ", 5)) {
+ /* store request type */
+ is_post = 1;
+ sp1 = data + 4;
+ /* received GET request */
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Received POST request\n"));
+#endif /* LWIP_HTTPD_SUPPORT_POST */
+ } else {
+ /* null-terminate the METHOD (pbuf is freed anyway wen returning) */
+ data[4] = 0;
+ /* unsupported method! */
+ LWIP_DEBUGF(HTTPD_DEBUG, ("Unsupported request method (not implemented): \"%s\"\n",
+ data));
+ return http_find_error_file(hs, 501);
+ }
+ /* if we come here, method is OK, parse URI */
+ left_len = (u16_t)(data_len - ((sp1 + 1) - data));
+ sp2 = lwip_strnstr(sp1 + 1, " ", left_len);
+#if LWIP_HTTPD_SUPPORT_V09
+ if (sp2 == NULL) {
+ /* HTTP 0.9: respond with correct protocol version */
+ sp2 = lwip_strnstr(sp1 + 1, CRLF, left_len);
+ is_09 = 1;
+#if LWIP_HTTPD_SUPPORT_POST
+ if (is_post) {
+ /* HTTP/0.9 does not support POST */
+ goto badrequest;
+ }
+#endif /* LWIP_HTTPD_SUPPORT_POST */
+ }
+#endif /* LWIP_HTTPD_SUPPORT_V09 */
+ uri_len = (u16_t)(sp2 - (sp1 + 1));
+ if ((sp2 != 0) && (sp2 > sp1)) {
+ /* wait for CRLFCRLF (indicating end of HTTP headers) before parsing anything */
+ if (lwip_strnstr(data, CRLF CRLF, data_len) != NULL) {
+ char *uri = sp1 + 1;
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+ /* This is HTTP/1.0 compatible: for strict 1.1, a connection
+ would always be persistent unless "close" was specified. */
+ if (!is_09 && (lwip_strnstr(data, HTTP11_CONNECTIONKEEPALIVE, data_len) ||
+ lwip_strnstr(data, HTTP11_CONNECTIONKEEPALIVE2, data_len))) {
+ hs->keepalive = 1;
+ } else {
+ hs->keepalive = 0;
+ }
+#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */
+ /* null-terminate the METHOD (pbuf is freed anyway wen returning) */
+ *sp1 = 0;
+ uri[uri_len] = 0;
+ LWIP_DEBUGF(HTTPD_DEBUG, ("Received \"%s\" request for URI: \"%s\"\n",
+ data, uri));
+#if LWIP_HTTPD_SUPPORT_POST
+ if (is_post) {
+#if LWIP_HTTPD_SUPPORT_REQUESTLIST
+ struct pbuf *q = hs->req;
+#else /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+ struct pbuf *q = inp;
+#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+ err = http_post_request(q, hs, data, data_len, uri, sp2);
+ if (err != ERR_OK) {
+ /* restore header for next try */
+ *sp1 = ' ';
+ *sp2 = ' ';
+ uri[uri_len] = ' ';
+ }
+ if (err == ERR_ARG) {
+ goto badrequest;
+ }
+ return err;
+ } else
+#endif /* LWIP_HTTPD_SUPPORT_POST */
+ {
+ return http_find_file(hs, uri, is_09);
+ }
+ }
+ } else {
+ LWIP_DEBUGF(HTTPD_DEBUG, ("invalid URI\n"));
+ }
+ }
+ }
+
+#if LWIP_HTTPD_SUPPORT_REQUESTLIST
+ clen = pbuf_clen(hs->req);
+ if ((hs->req->tot_len <= LWIP_HTTPD_REQ_BUFSIZE) &&
+ (clen <= LWIP_HTTPD_REQ_QUEUELEN)) {
+ /* request not fully received (too short or CRLF is missing) */
+ return ERR_INPROGRESS;
+ } else
+#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+ {
+#if LWIP_HTTPD_SUPPORT_POST
+badrequest:
+#endif /* LWIP_HTTPD_SUPPORT_POST */
+ LWIP_DEBUGF(HTTPD_DEBUG, ("bad request\n"));
+ /* could not parse request */
+ return http_find_error_file(hs, 400);
+ }
+}
+
+#if LWIP_HTTPD_SSI && (LWIP_HTTPD_SSI_BY_FILE_EXTENSION == 1)
+/* Check if SSI should be parsed for this file/URL
+ * (With LWIP_HTTPD_SSI_BY_FILE_EXTENSION == 2, this function can be
+ * overridden by an external implementation.)
+ *
+ * @return 1 for SSI, 0 for standard files
+ */
+static u8_t
+http_uri_is_ssi(struct fs_file *file, const char *uri)
+{
+ size_t loop;
+ u8_t tag_check = 0;
+ if (file != NULL) {
+ /* See if we have been asked for an shtml file and, if so,
+ enable tag checking. */
+ const char *ext = NULL, *sub;
+ char *param = (char *)strstr(uri, "?");
+ if (param != NULL) {
+ /* separate uri from parameters for now, set back later */
+ *param = 0;
+ }
+ sub = uri;
+ ext = uri;
+ for (sub = strstr(sub, "."); sub != NULL; sub = strstr(sub, ".")) {
+ ext = sub;
+ sub++;
+ }
+ for (loop = 0; loop < NUM_SHTML_EXTENSIONS; loop++) {
+ if (!lwip_stricmp(ext, g_pcSSIExtensions[loop])) {
+ tag_check = 1;
+ break;
+ }
+ }
+ if (param != NULL) {
+ *param = '?';
+ }
+ }
+ return tag_check;
+}
+#endif /* LWIP_HTTPD_SSI */
+
+/** Try to find the file specified by uri and, if found, initialize hs
+ * accordingly.
+ *
+ * @param hs the connection state
+ * @param uri the HTTP header URI
+ * @param is_09 1 if the request is HTTP/0.9 (no HTTP headers in response)
+ * @return ERR_OK if file was found and hs has been initialized correctly
+ * another err_t otherwise
+ */
+static err_t
+http_find_file(struct http_state *hs, const char *uri, int is_09)
+{
+ size_t loop;
+ struct fs_file *file = NULL;
+ char *params = NULL;
+ err_t err;
+#if LWIP_HTTPD_CGI
+ int i;
+#endif /* LWIP_HTTPD_CGI */
+#if !LWIP_HTTPD_SSI
+ const
+#endif /* !LWIP_HTTPD_SSI */
+ /* By default, assume we will not be processing server-side-includes tags */
+ u8_t tag_check = 0;
+
+ /* Have we been asked for the default file (in root or a directory) ? */
+#if LWIP_HTTPD_MAX_REQUEST_URI_LEN
+ size_t uri_len = strlen(uri);
+ if ((uri_len > 0) && (uri[uri_len - 1] == '/') &&
+ ((uri != http_uri_buf) || (uri_len == 1))) {
+ size_t copy_len = LWIP_MIN(sizeof(http_uri_buf) - 1, uri_len - 1);
+ if (copy_len > 0) {
+ MEMCPY(http_uri_buf, uri, copy_len);
+ http_uri_buf[copy_len] = 0;
+ }
+#else /* LWIP_HTTPD_MAX_REQUEST_URI_LEN */
+ if ((uri[0] == '/') && (uri[1] == 0)) {
+#endif /* LWIP_HTTPD_MAX_REQUEST_URI_LEN */
+ /* Try each of the configured default filenames until we find one
+ that exists. */
+ for (loop = 0; loop < NUM_DEFAULT_FILENAMES; loop++) {
+ const char *file_name;
+#if LWIP_HTTPD_MAX_REQUEST_URI_LEN
+ if (copy_len > 0) {
+ size_t len_left = sizeof(http_uri_buf) - copy_len - 1;
+ if (len_left > 0) {
+ size_t name_len = strlen(httpd_default_filenames[loop].name);
+ size_t name_copy_len = LWIP_MIN(len_left, name_len);
+ MEMCPY(&http_uri_buf[copy_len], httpd_default_filenames[loop].name, name_copy_len);
+ http_uri_buf[copy_len + name_copy_len] = 0;
+ }
+ file_name = http_uri_buf;
+ } else
+#endif /* LWIP_HTTPD_MAX_REQUEST_URI_LEN */
+ {
+ file_name = httpd_default_filenames[loop].name;
+ }
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Looking for %s...\n", file_name));
+ err = fs_open(&hs->file_handle, file_name);
+ if (err == ERR_OK) {
+ uri = file_name;
+ file = &hs->file_handle;
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Opened.\n"));
+#if LWIP_HTTPD_SSI
+ tag_check = httpd_default_filenames[loop].shtml;
+#endif /* LWIP_HTTPD_SSI */
+ break;
+ }
+ }
+ }
+ if (file == NULL) {
+ /* No - we've been asked for a specific file. */
+ /* First, isolate the base URI (without any parameters) */
+ params = (char *)strchr(uri, '?');
+ if (params != NULL) {
+ /* URI contains parameters. NULL-terminate the base URI */
+ *params = '\0';
+ params++;
+ }
+
+#if LWIP_HTTPD_CGI
+ http_cgi_paramcount = -1;
+ /* Does the base URI we have isolated correspond to a CGI handler? */
+ if (httpd_num_cgis && httpd_cgis) {
+ for (i = 0; i < httpd_num_cgis; i++) {
+ if (strcmp(uri, httpd_cgis[i].pcCGIName) == 0) {
+ /*
+ * We found a CGI that handles this URI so extract the
+ * parameters and call the handler.
+ */
+ http_cgi_paramcount = extract_uri_parameters(hs, params);
+ uri = httpd_cgis[i].pfnCGIHandler(i, http_cgi_paramcount, hs->params,
+ hs->param_vals);
+ break;
+ }
+ }
+ }
+#endif /* LWIP_HTTPD_CGI */
+
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Opening %s\n", uri));
+
+ err = fs_open(&hs->file_handle, uri);
+ if (err == ERR_OK) {
+ file = &hs->file_handle;
+ } else {
+ file = http_get_404_file(hs, &uri);
+ }
+#if LWIP_HTTPD_SSI
+ if (file != NULL) {
+ if (file->flags & FS_FILE_FLAGS_SSI) {
+ tag_check = 1;
+ } else {
+#if LWIP_HTTPD_SSI_BY_FILE_EXTENSION
+ tag_check = http_uri_is_ssi(file, uri);
+#endif /* LWIP_HTTPD_SSI_BY_FILE_EXTENSION */
+ }
+ }
+#endif /* LWIP_HTTPD_SSI */
+ }
+ if (file == NULL) {
+ /* None of the default filenames exist so send back a 404 page */
+ file = http_get_404_file(hs, &uri);
+ }
+ return http_init_file(hs, file, is_09, uri, tag_check, params);
+}
+
+/** Initialize a http connection with a file to send (if found).
+ * Called by http_find_file and http_find_error_file.
+ *
+ * @param hs http connection state
+ * @param file file structure to send (or NULL if not found)
+ * @param is_09 1 if the request is HTTP/0.9 (no HTTP headers in response)
+ * @param uri the HTTP header URI
+ * @param tag_check enable SSI tag checking
+ * @param params != NULL if URI has parameters (separated by '?')
+ * @return ERR_OK if file was found and hs has been initialized correctly
+ * another err_t otherwise
+ */
+static err_t
+http_init_file(struct http_state *hs, struct fs_file *file, int is_09, const char *uri,
+ u8_t tag_check, char *params)
+{
+#if !LWIP_HTTPD_SUPPORT_V09
+ LWIP_UNUSED_ARG(is_09);
+#endif
+ if (file != NULL) {
+ /* file opened, initialise struct http_state */
+#if !LWIP_HTTPD_DYNAMIC_FILE_READ
+ /* If dynamic read is disabled, file data must be in one piece and available now */
+ LWIP_ASSERT("file->data != NULL", file->data != NULL);
+#endif
+
+#if LWIP_HTTPD_SSI
+ if (tag_check) {
+ struct http_ssi_state *ssi = http_ssi_state_alloc();
+ if (ssi != NULL) {
+ ssi->tag_index = 0;
+ ssi->tag_state = TAG_NONE;
+ ssi->parsed = file->data;
+ ssi->parse_left = file->len;
+ ssi->tag_end = file->data;
+ hs->ssi = ssi;
+ }
+ }
+#else /* LWIP_HTTPD_SSI */
+ LWIP_UNUSED_ARG(tag_check);
+#endif /* LWIP_HTTPD_SSI */
+ hs->handle = file;
+#if LWIP_HTTPD_CGI_SSI
+ if (params != NULL) {
+ /* URI contains parameters, call generic CGI handler */
+ int count;
+#if LWIP_HTTPD_CGI
+ if (http_cgi_paramcount >= 0) {
+ count = http_cgi_paramcount;
+ } else
+#endif
+ {
+ count = extract_uri_parameters(hs, params);
+ }
+ httpd_cgi_handler(file, uri, count, http_cgi_params, http_cgi_param_vals
+#if defined(LWIP_HTTPD_FILE_STATE) && LWIP_HTTPD_FILE_STATE
+ , file->state
+#endif /* LWIP_HTTPD_FILE_STATE */
+ );
+ }
+#else /* LWIP_HTTPD_CGI_SSI */
+ LWIP_UNUSED_ARG(params);
+#endif /* LWIP_HTTPD_CGI_SSI */
+ hs->file = file->data;
+ LWIP_ASSERT("File length must be positive!", (file->len >= 0));
+#if LWIP_HTTPD_CUSTOM_FILES
+ if (file->is_custom_file && (file->data == NULL)) {
+ /* custom file, need to read data first (via fs_read_custom) */
+ hs->left = 0;
+ } else
+#endif /* LWIP_HTTPD_CUSTOM_FILES */
+ {
+ hs->left = (u32_t)file->len;
+ }
+ hs->retries = 0;
+#if LWIP_HTTPD_TIMING
+ hs->time_started = sys_now();
+#endif /* LWIP_HTTPD_TIMING */
+#if !LWIP_HTTPD_DYNAMIC_HEADERS
+ LWIP_ASSERT("HTTP headers not included in file system",
+ (hs->handle->flags & FS_FILE_FLAGS_HEADER_INCLUDED) != 0);
+#endif /* !LWIP_HTTPD_DYNAMIC_HEADERS */
+#if LWIP_HTTPD_SUPPORT_V09
+ if (is_09 && ((hs->handle->flags & FS_FILE_FLAGS_HEADER_INCLUDED) != 0)) {
+ /* HTTP/0.9 responses are sent without HTTP header,
+ search for the end of the header. */
+ char *file_start = lwip_strnstr(hs->file, CRLF CRLF, hs->left);
+ if (file_start != NULL) {
+ int diff = file_start + 4 - hs->file;
+ hs->file += diff;
+ hs->left -= (u32_t)diff;
+ }
+ }
+#endif /* LWIP_HTTPD_SUPPORT_V09*/
+ } else {
+ hs->handle = NULL;
+ hs->file = NULL;
+ hs->left = 0;
+ hs->retries = 0;
+ }
+#if LWIP_HTTPD_DYNAMIC_HEADERS
+ /* Determine the HTTP headers to send based on the file extension of
+ * the requested URI. */
+ if ((hs->handle == NULL) || ((hs->handle->flags & FS_FILE_FLAGS_HEADER_INCLUDED) == 0)) {
+ get_http_headers(hs, uri);
+ }
+#else /* LWIP_HTTPD_DYNAMIC_HEADERS */
+ LWIP_UNUSED_ARG(uri);
+#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+ if (hs->keepalive) {
+#if LWIP_HTTPD_SSI
+ if (hs->ssi != NULL) {
+ hs->keepalive = 0;
+ } else
+#endif /* LWIP_HTTPD_SSI */
+ {
+ if ((hs->handle != NULL) &&
+ ((hs->handle->flags & (FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT)) == FS_FILE_FLAGS_HEADER_INCLUDED)) {
+ hs->keepalive = 0;
+ }
+ }
+ }
+#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */
+ return ERR_OK;
+}
+
+/**
+ * The pcb had an error and is already deallocated.
+ * The argument might still be valid (if != NULL).
+ */
+static void
+http_err(void *arg, err_t err)
+{
+ struct http_state *hs = (struct http_state *)arg;
+ LWIP_UNUSED_ARG(err);
+
+ LWIP_DEBUGF(HTTPD_DEBUG, ("http_err: %s", lwip_strerr(err)));
+
+ if (hs != NULL) {
+ http_state_free(hs);
+ }
+}
+
+/**
+ * Data has been sent and acknowledged by the remote host.
+ * This means that more data can be sent.
+ */
+static err_t
+http_sent(void *arg, struct altcp_pcb *pcb, u16_t len)
+{
+ struct http_state *hs = (struct http_state *)arg;
+
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_sent %p\n", (void *)pcb));
+
+ LWIP_UNUSED_ARG(len);
+
+ if (hs == NULL) {
+ return ERR_OK;
+ }
+
+ hs->retries = 0;
+
+ http_send(pcb, hs);
+
+ return ERR_OK;
+}
+
+/**
+ * The poll function is called every 2nd second.
+ * If there has been no data sent (which resets the retries) in 8 seconds, close.
+ * If the last portion of a file has not been sent in 2 seconds, close.
+ *
+ * This could be increased, but we don't want to waste resources for bad connections.
+ */
+static err_t
+http_poll(void *arg, struct altcp_pcb *pcb)
+{
+ struct http_state *hs = (struct http_state *)arg;
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_poll: pcb=%p hs=%p pcb_state=%s\n",
+ (void *)pcb, (void *)hs, tcp_debug_state_str(altcp_dbg_get_tcp_state(pcb))));
+
+ if (hs == NULL) {
+ err_t closed;
+ /* arg is null, close. */
+ LWIP_DEBUGF(HTTPD_DEBUG, ("http_poll: arg is NULL, close\n"));
+ closed = http_close_conn(pcb, NULL);
+ LWIP_UNUSED_ARG(closed);
+#if LWIP_HTTPD_ABORT_ON_CLOSE_MEM_ERROR
+ if (closed == ERR_MEM) {
+ altcp_abort(pcb);
+ return ERR_ABRT;
+ }
+#endif /* LWIP_HTTPD_ABORT_ON_CLOSE_MEM_ERROR */
+ return ERR_OK;
+ } else {
+ hs->retries++;
+ if (hs->retries == HTTPD_MAX_RETRIES) {
+ LWIP_DEBUGF(HTTPD_DEBUG, ("http_poll: too many retries, close\n"));
+ http_close_conn(pcb, hs);
+ return ERR_OK;
+ }
+
+ /* If this connection has a file open, try to send some more data. If
+ * it has not yet received a GET request, don't do this since it will
+ * cause the connection to close immediately. */
+ if (hs->handle) {
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_poll: try to send more data\n"));
+ if (http_send(pcb, hs)) {
+ /* If we wrote anything to be sent, go ahead and send it now. */
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("tcp_output\n"));
+ altcp_output(pcb);
+ }
+ }
+ }
+
+ return ERR_OK;
+}
+
+/**
+ * Data has been received on this pcb.
+ * For HTTP 1.0, this should normally only happen once (if the request fits in one packet).
+ */
+static err_t
+http_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err)
+{
+ struct http_state *hs = (struct http_state *)arg;
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_recv: pcb=%p pbuf=%p err=%s\n", (void *)pcb,
+ (void *)p, lwip_strerr(err)));
+
+ if ((err != ERR_OK) || (p == NULL) || (hs == NULL)) {
+ /* error or closed by other side? */
+ if (p != NULL) {
+ /* Inform TCP that we have taken the data. */
+ altcp_recved(pcb, p->tot_len);
+ pbuf_free(p);
+ }
+ if (hs == NULL) {
+ /* this should not happen, only to be robust */
+ LWIP_DEBUGF(HTTPD_DEBUG, ("Error, http_recv: hs is NULL, close\n"));
+ }
+ http_close_conn(pcb, hs);
+ return ERR_OK;
+ }
+
+#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND
+ if (hs->no_auto_wnd) {
+ hs->unrecved_bytes += p->tot_len;
+ } else
+#endif /* LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND */
+ {
+ /* Inform TCP that we have taken the data. */
+ altcp_recved(pcb, p->tot_len);
+ }
+
+#if LWIP_HTTPD_SUPPORT_POST
+ if (hs->post_content_len_left > 0) {
+ /* reset idle counter when POST data is received */
+ hs->retries = 0;
+ /* this is data for a POST, pass the complete pbuf to the application */
+ http_post_rxpbuf(hs, p);
+ /* pbuf is passed to the application, don't free it! */
+ if (hs->post_content_len_left == 0) {
+ /* all data received, send response or close connection */
+ http_send(pcb, hs);
+ }
+ return ERR_OK;
+ } else
+#endif /* LWIP_HTTPD_SUPPORT_POST */
+ {
+ if (hs->handle == NULL) {
+ err_t parsed = http_parse_request(p, hs, pcb);
+ LWIP_ASSERT("http_parse_request: unexpected return value", parsed == ERR_OK
+ || parsed == ERR_INPROGRESS || parsed == ERR_ARG || parsed == ERR_USE);
+#if LWIP_HTTPD_SUPPORT_REQUESTLIST
+ if (parsed != ERR_INPROGRESS) {
+ /* request fully parsed or error */
+ if (hs->req != NULL) {
+ pbuf_free(hs->req);
+ hs->req = NULL;
+ }
+ }
+#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+ pbuf_free(p);
+ if (parsed == ERR_OK) {
+#if LWIP_HTTPD_SUPPORT_POST
+ if (hs->post_content_len_left == 0)
+#endif /* LWIP_HTTPD_SUPPORT_POST */
+ {
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_recv: data %p len %"S32_F"\n", (const void *)hs->file, hs->left));
+ http_send(pcb, hs);
+ }
+ } else if (parsed == ERR_ARG) {
+ /* @todo: close on ERR_USE? */
+ http_close_conn(pcb, hs);
+ }
+ } else {
+ LWIP_DEBUGF(HTTPD_DEBUG, ("http_recv: already sending data\n"));
+ /* already sending but still receiving data, we might want to RST here? */
+ pbuf_free(p);
+ }
+ }
+ return ERR_OK;
+}
+
+/**
+ * A new incoming connection has been accepted.
+ */
+static err_t
+http_accept(void *arg, struct altcp_pcb *pcb, err_t err)
+{
+ struct http_state *hs;
+ LWIP_UNUSED_ARG(err);
+ LWIP_UNUSED_ARG(arg);
+ LWIP_DEBUGF(HTTPD_DEBUG, ("http_accept %p / %p\n", (void *)pcb, arg));
+
+ if ((err != ERR_OK) || (pcb == NULL)) {
+ return ERR_VAL;
+ }
+
+ /* Set priority */
+ altcp_setprio(pcb, HTTPD_TCP_PRIO);
+
+ /* Allocate memory for the structure that holds the state of the
+ connection - initialized by that function. */
+ hs = http_state_alloc();
+ if (hs == NULL) {
+ LWIP_DEBUGF(HTTPD_DEBUG, ("http_accept: Out of memory, RST\n"));
+ return ERR_MEM;
+ }
+ hs->pcb = pcb;
+
+ /* Tell TCP that this is the structure we wish to be passed for our
+ callbacks. */
+ altcp_arg(pcb, hs);
+
+ /* Set up the various callback functions */
+ altcp_recv(pcb, http_recv);
+ altcp_err(pcb, http_err);
+ altcp_poll(pcb, http_poll, HTTPD_POLL_INTERVAL);
+ altcp_sent(pcb, http_sent);
+
+ return ERR_OK;
+}
+
+static void
+httpd_init_pcb(struct altcp_pcb *pcb, u16_t port)
+{
+ err_t err;
+
+ if (pcb) {
+ altcp_setprio(pcb, HTTPD_TCP_PRIO);
+ /* set SOF_REUSEADDR here to explicitly bind httpd to multiple interfaces */
+ err = altcp_bind(pcb, IP_ANY_TYPE, port);
+ LWIP_UNUSED_ARG(err); /* in case of LWIP_NOASSERT */
+ LWIP_ASSERT("httpd_init: tcp_bind failed", err == ERR_OK);
+ pcb = altcp_listen(pcb);
+ LWIP_ASSERT("httpd_init: tcp_listen failed", pcb != NULL);
+ altcp_accept(pcb, http_accept);
+ }
+}
+
+/**
+ * @ingroup httpd
+ * Initialize the httpd: set up a listening PCB and bind it to the defined port
+ */
+void
+httpd_init(void)
+{
+ struct altcp_pcb *pcb;
+
+#if HTTPD_USE_MEM_POOL
+ LWIP_MEMPOOL_INIT(HTTPD_STATE);
+#if LWIP_HTTPD_SSI
+ LWIP_MEMPOOL_INIT(HTTPD_SSI_STATE);
+#endif
+#endif
+ LWIP_DEBUGF(HTTPD_DEBUG, ("httpd_init\n"));
+
+ /* LWIP_ASSERT_CORE_LOCKED(); is checked by tcp_new() */
+
+ pcb = altcp_tcp_new_ip_type(IPADDR_TYPE_ANY);
+ LWIP_ASSERT("httpd_init: tcp_new failed", pcb != NULL);
+ httpd_init_pcb(pcb, HTTPD_SERVER_PORT);
+}
+
+#if HTTPD_ENABLE_HTTPS
+/**
+ * @ingroup httpd
+ * Initialize the httpd: set up a listening PCB and bind it to the defined port.
+ * Also set up TLS connection handling (HTTPS).
+ */
+void
+httpd_inits(struct altcp_tls_config *conf)
+{
+#if LWIP_ALTCP_TLS
+ struct altcp_pcb *pcb_tls = altcp_tls_new(conf, IPADDR_TYPE_ANY);
+ LWIP_ASSERT("httpd_init: altcp_tls_new failed", pcb_tls != NULL);
+ httpd_init_pcb(pcb_tls, HTTPD_SERVER_PORT_HTTPS);
+#else /* LWIP_ALTCP_TLS */
+ LWIP_UNUSED_ARG(conf);
+#endif /* LWIP_ALTCP_TLS */
+}
+#endif /* HTTPD_ENABLE_HTTPS */
+
+#if LWIP_HTTPD_SSI
+/**
+ * @ingroup httpd
+ * Set the SSI handler function.
+ *
+ * @param ssi_handler the SSI handler function
+ * @param tags an array of SSI tag strings to search for in SSI-enabled files
+ * @param num_tags number of tags in the 'tags' array
+ */
+void
+http_set_ssi_handler(tSSIHandler ssi_handler, const char **tags, int num_tags)
+{
+ LWIP_DEBUGF(HTTPD_DEBUG, ("http_set_ssi_handler\n"));
+
+ LWIP_ASSERT("no ssi_handler given", ssi_handler != NULL);
+ httpd_ssi_handler = ssi_handler;
+
+#if LWIP_HTTPD_SSI_RAW
+ LWIP_UNUSED_ARG(tags);
+ LWIP_UNUSED_ARG(num_tags);
+#else /* LWIP_HTTPD_SSI_RAW */
+ LWIP_ASSERT("no tags given", tags != NULL);
+ LWIP_ASSERT("invalid number of tags", num_tags > 0);
+
+ httpd_tags = tags;
+ httpd_num_tags = num_tags;
+#endif /* !LWIP_HTTPD_SSI_RAW */
+}
+#endif /* LWIP_HTTPD_SSI */
+
+#if LWIP_HTTPD_CGI
+/**
+ * @ingroup httpd
+ * Set an array of CGI filenames/handler functions
+ *
+ * @param cgis an array of CGI filenames/handler functions
+ * @param num_handlers number of elements in the 'cgis' array
+ */
+void
+http_set_cgi_handlers(const tCGI *cgis, int num_handlers)
+{
+ LWIP_ASSERT("no cgis given", cgis != NULL);
+ LWIP_ASSERT("invalid number of handlers", num_handlers > 0);
+
+ httpd_cgis = cgis;
+ httpd_num_cgis = num_handlers;
+}
+#endif /* LWIP_HTTPD_CGI */
+
+#endif /* LWIP_TCP && LWIP_CALLBACK_API */
diff --git a/resources/ethernet/LwIP/apps/http/httpd_structs.h b/resources/ethernet/LwIP/apps/http/httpd_structs.h
new file mode 100644
index 00000000..519533af
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/http/httpd_structs.h
@@ -0,0 +1,123 @@
+#ifndef LWIP_HTTPD_STRUCTS_H
+#define LWIP_HTTPD_STRUCTS_H
+
+#include "lwip/apps/httpd.h"
+
+#if LWIP_HTTPD_DYNAMIC_HEADERS
+/** This struct is used for a list of HTTP header strings for various
+ * filename extensions. */
+typedef struct {
+ const char *extension;
+ const char *content_type;
+} tHTTPHeader;
+
+/** A list of strings used in HTTP headers (see RFC 1945 HTTP/1.0 and
+ * RFC 2616 HTTP/1.1 for header field definitions) */
+static const char *const g_psHTTPHeaderStrings[] = {
+ "HTTP/1.0 200 OK\r\n",
+ "HTTP/1.0 404 File not found\r\n",
+ "HTTP/1.0 400 Bad Request\r\n",
+ "HTTP/1.0 501 Not Implemented\r\n",
+ "HTTP/1.1 200 OK\r\n",
+ "HTTP/1.1 404 File not found\r\n",
+ "HTTP/1.1 400 Bad Request\r\n",
+ "HTTP/1.1 501 Not Implemented\r\n",
+ "Content-Length: ",
+ "Connection: Close\r\n",
+ "Connection: keep-alive\r\n",
+ "Connection: keep-alive\r\nContent-Length: ",
+ "Server: "HTTPD_SERVER_AGENT"\r\n",
+ "\r\n404: The requested file cannot be found.
\r\n"
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+ , "Connection: keep-alive\r\nContent-Length: 77\r\n\r\n404: The requested file cannot be found.
\r\n"
+#endif
+};
+
+/* Indexes into the g_psHTTPHeaderStrings array */
+#define HTTP_HDR_OK 0 /* 200 OK */
+#define HTTP_HDR_NOT_FOUND 1 /* 404 File not found */
+#define HTTP_HDR_BAD_REQUEST 2 /* 400 Bad request */
+#define HTTP_HDR_NOT_IMPL 3 /* 501 Not Implemented */
+#define HTTP_HDR_OK_11 4 /* 200 OK */
+#define HTTP_HDR_NOT_FOUND_11 5 /* 404 File not found */
+#define HTTP_HDR_BAD_REQUEST_11 6 /* 400 Bad request */
+#define HTTP_HDR_NOT_IMPL_11 7 /* 501 Not Implemented */
+#define HTTP_HDR_CONTENT_LENGTH 8 /* Content-Length: (HTTP 1.0)*/
+#define HTTP_HDR_CONN_CLOSE 9 /* Connection: Close (HTTP 1.1) */
+#define HTTP_HDR_CONN_KEEPALIVE 10 /* Connection: keep-alive (HTTP 1.1) */
+#define HTTP_HDR_KEEPALIVE_LEN 11 /* Connection: keep-alive + Content-Length: (HTTP 1.1)*/
+#define HTTP_HDR_SERVER 12 /* Server: HTTPD_SERVER_AGENT */
+#define DEFAULT_404_HTML 13 /* default 404 body */
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+#define DEFAULT_404_HTML_PERSISTENT 14 /* default 404 body, but including Connection: keep-alive */
+#endif
+
+#define HTTP_CONTENT_TYPE(contenttype) "Content-Type: "contenttype"\r\n\r\n"
+#define HTTP_CONTENT_TYPE_ENCODING(contenttype, encoding) "Content-Type: "contenttype"\r\nContent-Encoding: "encoding"\r\n\r\n"
+
+#define HTTP_HDR_HTML HTTP_CONTENT_TYPE("text/html")
+#define HTTP_HDR_SSI HTTP_CONTENT_TYPE("text/html\r\nExpires: Fri, 10 Apr 2008 14:00:00 GMT\r\nPragma: no-cache")
+#define HTTP_HDR_GIF HTTP_CONTENT_TYPE("image/gif")
+#define HTTP_HDR_PNG HTTP_CONTENT_TYPE("image/png")
+#define HTTP_HDR_JPG HTTP_CONTENT_TYPE("image/jpeg")
+#define HTTP_HDR_BMP HTTP_CONTENT_TYPE("image/bmp")
+#define HTTP_HDR_ICO HTTP_CONTENT_TYPE("image/x-icon")
+#define HTTP_HDR_APP HTTP_CONTENT_TYPE("application/octet-stream")
+#define HTTP_HDR_JS HTTP_CONTENT_TYPE("application/javascript")
+#define HTTP_HDR_RA HTTP_CONTENT_TYPE("application/javascript")
+#define HTTP_HDR_CSS HTTP_CONTENT_TYPE("text/css")
+#define HTTP_HDR_SWF HTTP_CONTENT_TYPE("application/x-shockwave-flash")
+#define HTTP_HDR_XML HTTP_CONTENT_TYPE("text/xml")
+#define HTTP_HDR_PDF HTTP_CONTENT_TYPE("application/pdf")
+#define HTTP_HDR_JSON HTTP_CONTENT_TYPE("application/json")
+#define HTTP_HDR_CSV HTTP_CONTENT_TYPE("text/csv")
+#define HTTP_HDR_TSV HTTP_CONTENT_TYPE("text/tsv")
+#define HTTP_HDR_SVG HTTP_CONTENT_TYPE("image/svg+xml")
+#define HTTP_HDR_SVGZ HTTP_CONTENT_TYPE_ENCODING("image/svg+xml", "gzip")
+
+#define HTTP_HDR_DEFAULT_TYPE HTTP_CONTENT_TYPE("text/plain")
+
+/** A list of extension-to-HTTP header strings (see outdated RFC 1700 MEDIA TYPES
+ * and http://www.iana.org/assignments/media-types for registered content types
+ * and subtypes) */
+static const tHTTPHeader g_psHTTPHeaders[] = {
+ { "html", HTTP_HDR_HTML},
+ { "htm", HTTP_HDR_HTML},
+ { "shtml", HTTP_HDR_SSI},
+ { "shtm", HTTP_HDR_SSI},
+ { "ssi", HTTP_HDR_SSI},
+ { "gif", HTTP_HDR_GIF},
+ { "png", HTTP_HDR_PNG},
+ { "jpg", HTTP_HDR_JPG},
+ { "bmp", HTTP_HDR_BMP},
+ { "ico", HTTP_HDR_ICO},
+ { "class", HTTP_HDR_APP},
+ { "cls", HTTP_HDR_APP},
+ { "js", HTTP_HDR_JS},
+ { "ram", HTTP_HDR_RA},
+ { "css", HTTP_HDR_CSS},
+ { "swf", HTTP_HDR_SWF},
+ { "xml", HTTP_HDR_XML},
+ { "xsl", HTTP_HDR_XML},
+ { "pdf", HTTP_HDR_PDF},
+ { "json", HTTP_HDR_JSON}
+#ifdef HTTPD_ADDITIONAL_CONTENT_TYPES
+ /* If you need to add content types not listed here:
+ * #define HTTPD_ADDITIONAL_CONTENT_TYPES {"ct1", HTTP_CONTENT_TYPE("text/ct1")}, {"exe", HTTP_CONTENT_TYPE("application/exe")}
+ */
+ , HTTPD_ADDITIONAL_CONTENT_TYPES
+#endif
+};
+
+#define NUM_HTTP_HEADERS LWIP_ARRAYSIZE(g_psHTTPHeaders)
+
+#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
+
+#if LWIP_HTTPD_SSI
+static const char *const g_pcSSIExtensions[] = {
+ ".shtml", ".shtm", ".ssi", ".xml", ".json"
+};
+#define NUM_SHTML_EXTENSIONS LWIP_ARRAYSIZE(g_pcSSIExtensions)
+#endif /* LWIP_HTTPD_SSI */
+
+#endif /* LWIP_HTTPD_STRUCTS_H */
diff --git a/resources/ethernet/LwIP/apps/http/makefsdata/makefsdata b/resources/ethernet/LwIP/apps/http/makefsdata/makefsdata
new file mode 100644
index 00000000..981c3d18
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/http/makefsdata/makefsdata
@@ -0,0 +1,97 @@
+#!/usr/bin/perl
+
+open(OUTPUT, "> fsdata.c");
+
+chdir("fs");
+open(FILES, "find . -type f |");
+
+while($file = ) {
+
+ # Do not include files in CVS directories nor backup files.
+ if($file =~ /(CVS|~)/) {
+ next;
+ }
+
+ chop($file);
+
+ open(HEADER, "> /tmp/header") || die $!;
+ if($file =~ /404/) {
+ print(HEADER "HTTP/1.0 404 File not found\r\n");
+ } else {
+ print(HEADER "HTTP/1.0 200 OK\r\n");
+ }
+ print(HEADER "Server: lwIP/pre-0.6 (http://www.sics.se/~adam/lwip/)\r\n");
+ if($file =~ /\.html$/) {
+ print(HEADER "Content-type: text/html\r\n");
+ } elsif($file =~ /\.gif$/) {
+ print(HEADER "Content-type: image/gif\r\n");
+ } elsif($file =~ /\.png$/) {
+ print(HEADER "Content-type: image/png\r\n");
+ } elsif($file =~ /\.jpg$/) {
+ print(HEADER "Content-type: image/jpeg\r\n");
+ } elsif($file =~ /\.class$/) {
+ print(HEADER "Content-type: application/octet-stream\r\n");
+ } elsif($file =~ /\.ram$/) {
+ print(HEADER "Content-type: audio/x-pn-realaudio\r\n");
+ } else {
+ print(HEADER "Content-type: text/plain\r\n");
+ }
+ print(HEADER "\r\n");
+ close(HEADER);
+
+ unless($file =~ /\.plain$/ || $file =~ /cgi/) {
+ system("cat /tmp/header $file > /tmp/file");
+ } else {
+ system("cp $file /tmp/file");
+ }
+
+ open(FILE, "/tmp/file");
+ unlink("/tmp/file");
+ unlink("/tmp/header");
+
+ $file =~ s/\.//;
+ $fvar = $file;
+ $fvar =~ s-/-_-g;
+ $fvar =~ s-\.-_-g;
+ print(OUTPUT "static const unsigned char data".$fvar."[] = {\n");
+ print(OUTPUT "\t/* $file */\n\t");
+ for($j = 0; $j < length($file); $j++) {
+ printf(OUTPUT "%#02x, ", unpack("C", substr($file, $j, 1)));
+ }
+ printf(OUTPUT "0,\n");
+
+
+ $i = 0;
+ while(read(FILE, $data, 1)) {
+ if($i == 0) {
+ print(OUTPUT "\t");
+ }
+ printf(OUTPUT "%#02x, ", unpack("C", $data));
+ $i++;
+ if($i == 10) {
+ print(OUTPUT "\n");
+ $i = 0;
+ }
+ }
+ print(OUTPUT "};\n\n");
+ close(FILE);
+ push(@fvars, $fvar);
+ push(@files, $file);
+}
+
+for($i = 0; $i < @fvars; $i++) {
+ $file = $files[$i];
+ $fvar = $fvars[$i];
+
+ if($i == 0) {
+ $prevfile = "NULL";
+ } else {
+ $prevfile = "file" . $fvars[$i - 1];
+ }
+ print(OUTPUT "const struct fsdata_file file".$fvar."[] = {{$prevfile, data$fvar, ");
+ print(OUTPUT "data$fvar + ". (length($file) + 1) .", ");
+ print(OUTPUT "sizeof(data$fvar) - ". (length($file) + 1) ."}};\n\n");
+}
+
+print(OUTPUT "#define FS_ROOT file$fvars[$i - 1]\n\n");
+print(OUTPUT "#define FS_NUMFILES $i\n");
diff --git a/resources/ethernet/LwIP/apps/http/makefsdata/makefsdata.c b/resources/ethernet/LwIP/apps/http/makefsdata/makefsdata.c
new file mode 100644
index 00000000..1906ce22
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/http/makefsdata/makefsdata.c
@@ -0,0 +1,1251 @@
+/**
+ * makefsdata: Converts a directory structure for use with the lwIP httpd.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Jim Pettinato
+ * Simon Goldschmidt
+ *
+ * @todo:
+ * - take TCP_MSS, LWIP_TCP_TIMESTAMPS and
+ * PAYLOAD_ALIGN_TYPE/PAYLOAD_ALIGNMENT as arguments
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#include "tinydir.h"
+
+/** Makefsdata can generate *all* files deflate-compressed (where file size shrinks).
+ * Since nearly all browsers support this, this is a good way to reduce ROM size.
+ * To compress the files, "miniz.c" must be downloaded seperately.
+ */
+#ifndef MAKEFS_SUPPORT_DEFLATE
+#define MAKEFS_SUPPORT_DEFLATE 0
+#endif
+
+#define COPY_BUFSIZE (1024*1024) /* 1 MByte */
+
+#if MAKEFS_SUPPORT_DEFLATE
+#include "../miniz.c"
+
+typedef unsigned char uint8;
+typedef unsigned short uint16;
+typedef unsigned int uint;
+
+#define my_max(a,b) (((a) > (b)) ? (a) : (b))
+#define my_min(a,b) (((a) < (b)) ? (a) : (b))
+
+/* COMP_OUT_BUF_SIZE is the size of the output buffer used during compression.
+ COMP_OUT_BUF_SIZE must be >= 1 and <= OUT_BUF_SIZE */
+#define COMP_OUT_BUF_SIZE COPY_BUFSIZE
+
+/* OUT_BUF_SIZE is the size of the output buffer used during decompression.
+ OUT_BUF_SIZE must be a power of 2 >= TINFL_LZ_DICT_SIZE (because the low-level decompressor not only writes, but reads from the output buffer as it decompresses) */
+#define OUT_BUF_SIZE COPY_BUFSIZE
+static uint8 s_outbuf[OUT_BUF_SIZE];
+static uint8 s_checkbuf[OUT_BUF_SIZE];
+
+/* tdefl_compressor contains all the state needed by the low-level compressor so it's a pretty big struct (~300k).
+ This example makes it a global vs. putting it on the stack, of course in real-world usage you'll probably malloc() or new it. */
+tdefl_compressor g_deflator;
+tinfl_decompressor g_inflator;
+
+int deflate_level = 10; /* default compression level, can be changed via command line */
+#define USAGE_ARG_DEFLATE " [-defl<:compr_level>]"
+#else /* MAKEFS_SUPPORT_DEFLATE */
+#define USAGE_ARG_DEFLATE ""
+#endif /* MAKEFS_SUPPORT_DEFLATE */
+
+#ifdef WIN32
+
+#define GETCWD(path, len) GetCurrentDirectoryA(len, path)
+#define CHDIR(path) SetCurrentDirectoryA(path)
+#define CHDIR_SUCCEEDED(ret) (ret == TRUE)
+
+#elif __linux__
+
+#define GETCWD(path, len) getcwd(path, len)
+#define CHDIR(path) chdir(path)
+#define CHDIR_SUCCEEDED(ret) (ret == 0)
+
+#else
+
+#error makefsdata not supported on this platform
+
+#endif
+
+#define NEWLINE "\r\n"
+#define NEWLINE_LEN 2
+
+/* define this to get the header variables we use to build HTTP headers */
+#define LWIP_HTTPD_DYNAMIC_HEADERS 1
+#define LWIP_HTTPD_SSI 1
+#include "lwip/init.h"
+#include "../httpd_structs.h"
+#include "lwip/apps/fs.h"
+
+#include "../core/inet_chksum.c"
+#include "../core/def.c"
+
+/** (Your server name here) */
+const char *serverID = "Server: "HTTPD_SERVER_AGENT"\r\n";
+char serverIDBuffer[1024];
+
+/* change this to suit your MEM_ALIGNMENT */
+#define PAYLOAD_ALIGNMENT 4
+/* set this to 0 to prevent aligning payload */
+#define ALIGN_PAYLOAD 1
+/* define this to a type that has the required alignment */
+#define PAYLOAD_ALIGN_TYPE "unsigned int"
+static int payload_alingment_dummy_counter = 0;
+
+#define HEX_BYTES_PER_LINE 16
+
+#define MAX_PATH_LEN 256
+
+struct file_entry {
+ struct file_entry *next;
+ const char *filename_c;
+};
+
+int process_sub(FILE *data_file, FILE *struct_file);
+int process_file(FILE *data_file, FILE *struct_file, const char *filename);
+int file_write_http_header(FILE *data_file, const char *filename, int file_size, u16_t *http_hdr_len,
+ u16_t *http_hdr_chksum, u8_t provide_content_len, int is_compressed);
+int file_put_ascii(FILE *file, const char *ascii_string, int len, int *i);
+int s_put_ascii(char *buf, const char *ascii_string, int len, int *i);
+void concat_files(const char *file1, const char *file2, const char *targetfile);
+int check_path(char *path, size_t size);
+static int checkSsiByFilelist(const char* filename_listfile);
+static int ext_in_list(const char* filename, const char *ext_list);
+static int file_to_exclude(const char* filename);
+static int file_can_be_compressed(const char* filename);
+
+/* 5 bytes per char + 3 bytes per line */
+static char file_buffer_c[COPY_BUFSIZE * 5 + ((COPY_BUFSIZE / HEX_BYTES_PER_LINE) * 3)];
+
+char curSubdir[MAX_PATH_LEN];
+char lastFileVar[MAX_PATH_LEN];
+char hdr_buf[4096];
+
+unsigned char processSubs = 1;
+unsigned char includeHttpHeader = 1;
+unsigned char useHttp11 = 0;
+unsigned char supportSsi = 1;
+unsigned char precalcChksum = 0;
+unsigned char includeLastModified = 0;
+#if MAKEFS_SUPPORT_DEFLATE
+unsigned char deflateNonSsiFiles = 0;
+size_t deflatedBytesReduced = 0;
+size_t overallDataBytes = 0;
+#endif
+const char *exclude_list = NULL;
+const char *ncompress_list = NULL;
+
+struct file_entry *first_file = NULL;
+struct file_entry *last_file = NULL;
+
+static char *ssi_file_buffer;
+static char **ssi_file_lines;
+static size_t ssi_file_num_lines;
+
+static void print_usage(void)
+{
+ printf(" Usage: htmlgen [targetdir] [-s] [-e] [-11] [-nossi] [-ssi:] [-c] [-f:] [-m] [-svr:] [-x:] [-xc:" USAGE_ARG_DEFLATE NEWLINE NEWLINE);
+ printf(" targetdir: relative or absolute path to files to convert" NEWLINE);
+ printf(" switch -s: toggle processing of subdirectories (default is on)" NEWLINE);
+ printf(" switch -e: exclude HTTP header from file (header is created at runtime, default is off)" NEWLINE);
+ printf(" switch -11: include HTTP 1.1 header (1.0 is default)" NEWLINE);
+ printf(" switch -nossi: no support for SSI (cannot calculate Content-Length for SSI)" NEWLINE);
+ printf(" switch -ssi: ssi filename (ssi support controlled by file list, not by extension)" NEWLINE);
+ printf(" switch -c: precalculate checksums for all pages (default is off)" NEWLINE);
+ printf(" switch -f: target filename (default is \"fsdata.c\")" NEWLINE);
+ printf(" switch -m: include \"Last-Modified\" header based on file time" NEWLINE);
+ printf(" switch -svr: server identifier sent in HTTP response header ('Server' field)" NEWLINE);
+ printf(" switch -x: comma separated list of extensions of files to exclude (e.g., -x:json,txt)" NEWLINE);
+ printf(" switch -xc: comma separated list of extensions of files to not compress (e.g., -xc:mp3,jpg)" NEWLINE);
+#if MAKEFS_SUPPORT_DEFLATE
+ printf(" switch -defl: deflate-compress all non-SSI files (with opt. compr.-level, default=10)" NEWLINE);
+ printf(" ATTENTION: browser has to support \"Content-Encoding: deflate\"!" NEWLINE);
+#endif
+ printf(" if targetdir not specified, htmlgen will attempt to" NEWLINE);
+ printf(" process files in subdirectory 'fs'" NEWLINE);
+}
+
+int main(int argc, char *argv[])
+{
+ char path[MAX_PATH_LEN];
+ char appPath[MAX_PATH_LEN];
+ FILE *data_file;
+ FILE *struct_file;
+ int filesProcessed;
+ int i;
+ char targetfile[MAX_PATH_LEN];
+ strcpy(targetfile, "fsdata.c");
+
+ memset(path, 0, sizeof(path));
+ memset(appPath, 0, sizeof(appPath));
+
+ printf(NEWLINE " makefsdata - HTML to C source converter" NEWLINE);
+ printf(" by Jim Pettinato - circa 2003 " NEWLINE);
+ printf(" extended by Simon Goldschmidt - 2009 " NEWLINE NEWLINE);
+
+ LWIP_ASSERT("sizeof(hdr_buf) must fit into an u16_t", sizeof(hdr_buf) <= 0xffff);
+
+ strcpy(path, "fs");
+ for (i = 1; i < argc; i++) {
+ if (argv[i] == NULL) {
+ continue;
+ }
+ if (argv[i][0] == '-') {
+ if (strstr(argv[i], "-svr:") == argv[i]) {
+ snprintf(serverIDBuffer, sizeof(serverIDBuffer), "Server: %s\r\n", &argv[i][5]);
+ serverID = serverIDBuffer;
+ printf("Using Server-ID: \"%s\"\n", serverID);
+ } else if (!strcmp(argv[i], "-s")) {
+ processSubs = 0;
+ } else if (!strcmp(argv[i], "-e")) {
+ includeHttpHeader = 0;
+ } else if (!strcmp(argv[i], "-11")) {
+ useHttp11 = 1;
+ } else if (!strcmp(argv[i], "-nossi")) {
+ supportSsi = 0;
+ } else if (strstr(argv[i], "-ssi:") == argv[i]) {
+ const char* ssi_list_filename = &argv[i][5];
+ if (checkSsiByFilelist(ssi_list_filename)) {
+ printf("Reading list of SSI files from \"%s\"\n", ssi_list_filename);
+ } else {
+ printf("Failed to load list of SSI files from \"%s\"\n", ssi_list_filename);
+ }
+ } else if (!strcmp(argv[i], "-c")) {
+ precalcChksum = 1;
+ } else if (strstr(argv[i], "-f:") == argv[i]) {
+ strncpy(targetfile, &argv[i][3], sizeof(targetfile) - 1);
+ targetfile[sizeof(targetfile) - 1] = 0;
+ printf("Writing to file \"%s\"\n", targetfile);
+ } else if (!strcmp(argv[i], "-m")) {
+ includeLastModified = 1;
+ } else if (!strcmp(argv[i], "-defl")) {
+#if MAKEFS_SUPPORT_DEFLATE
+ char *colon = strstr(argv[i], ":");
+ if (colon) {
+ if (colon[1] != 0) {
+ int defl_level = atoi(&colon[1]);
+ if ((defl_level >= 0) && (defl_level <= 10)) {
+ deflate_level = defl_level;
+ } else {
+ printf("ERROR: deflate level must be [0..10]" NEWLINE);
+ exit(0);
+ }
+ }
+ }
+ deflateNonSsiFiles = 1;
+ printf("Deflating all non-SSI files with level %d (but only if size is reduced)" NEWLINE, deflate_level);
+#else
+ printf("WARNING: Deflate support is disabled\n");
+#endif
+ } else if (strstr(argv[i], "-x:") == argv[i]) {
+ exclude_list = &argv[i][3];
+ printf("Excluding files with extensions %s" NEWLINE, exclude_list);
+ } else if (strstr(argv[i], "-xc:") == argv[i]) {
+ ncompress_list = &argv[i][4];
+ printf("Skipping compresion for files with extensions %s" NEWLINE, ncompress_list);
+ } else if ((strstr(argv[i], "-?")) || (strstr(argv[i], "-h"))) {
+ print_usage();
+ exit(0);
+ }
+ } else if ((argv[i][0] == '/') && (argv[i][1] == '?') && (argv[i][2] == 0)) {
+ print_usage();
+ exit(0);
+ } else {
+ strncpy(path, argv[i], sizeof(path) - 1);
+ path[sizeof(path) - 1] = 0;
+ }
+ }
+
+ if (!check_path(path, sizeof(path))) {
+ printf("Invalid path: \"%s\"." NEWLINE, path);
+ exit(-1);
+ }
+
+ GETCWD(appPath, MAX_PATH_LEN);
+ /* if command line param or subdir named 'fs' not found spout usage verbiage */
+ if (!CHDIR_SUCCEEDED(CHDIR(path))) {
+ /* if no subdir named 'fs' (or the one which was given) exists, spout usage verbiage */
+ printf(" Failed to open directory \"%s\"." NEWLINE NEWLINE, path);
+ print_usage();
+ exit(-1);
+ }
+ CHDIR(appPath);
+
+ printf("HTTP %sheader will %s statically included." NEWLINE,
+ (includeHttpHeader ? (useHttp11 ? "1.1 " : "1.0 ") : ""),
+ (includeHttpHeader ? "be" : "not be"));
+
+ curSubdir[0] = '\0'; /* start off in web page's root directory - relative paths */
+ printf(" Processing all files in directory %s", path);
+ if (processSubs) {
+ printf(" and subdirectories..." NEWLINE NEWLINE);
+ } else {
+ printf("..." NEWLINE NEWLINE);
+ }
+
+ data_file = fopen("fsdata.tmp", "wb");
+ if (data_file == NULL) {
+ printf("Failed to create file \"fsdata.tmp\"\n");
+ exit(-1);
+ }
+ struct_file = fopen("fshdr.tmp", "wb");
+ if (struct_file == NULL) {
+ printf("Failed to create file \"fshdr.tmp\"\n");
+ fclose(data_file);
+ exit(-1);
+ }
+
+ CHDIR(path);
+
+ fprintf(data_file, "#include \"lwip/apps/fs.h\"" NEWLINE);
+ fprintf(data_file, "#include \"lwip/def.h\"" NEWLINE NEWLINE NEWLINE);
+
+ fprintf(data_file, "#define file_NULL (struct fsdata_file *) NULL" NEWLINE NEWLINE NEWLINE);
+ /* define FS_FILE_FLAGS_HEADER_INCLUDED to 1 if not defined (compatibility with older httpd/fs) */
+ fprintf(data_file, "#ifndef FS_FILE_FLAGS_HEADER_INCLUDED" NEWLINE "#define FS_FILE_FLAGS_HEADER_INCLUDED 1" NEWLINE "#endif" NEWLINE);
+ /* define FS_FILE_FLAGS_HEADER_PERSISTENT to 0 if not defined (compatibility with older httpd/fs: wasn't supported back then) */
+ fprintf(data_file, "#ifndef FS_FILE_FLAGS_HEADER_PERSISTENT" NEWLINE "#define FS_FILE_FLAGS_HEADER_PERSISTENT 0" NEWLINE "#endif" NEWLINE);
+
+ /* define alignment defines */
+#if ALIGN_PAYLOAD
+ fprintf(data_file, "/* FSDATA_FILE_ALIGNMENT: 0=off, 1=by variable, 2=by include */" NEWLINE "#ifndef FSDATA_FILE_ALIGNMENT" NEWLINE "#define FSDATA_FILE_ALIGNMENT 0" NEWLINE "#endif" NEWLINE);
+#endif
+ fprintf(data_file, "#ifndef FSDATA_ALIGN_PRE" NEWLINE "#define FSDATA_ALIGN_PRE" NEWLINE "#endif" NEWLINE);
+ fprintf(data_file, "#ifndef FSDATA_ALIGN_POST" NEWLINE "#define FSDATA_ALIGN_POST" NEWLINE "#endif" NEWLINE);
+#if ALIGN_PAYLOAD
+ fprintf(data_file, "#if FSDATA_FILE_ALIGNMENT==2" NEWLINE "#include \"fsdata_alignment.h\"" NEWLINE "#endif" NEWLINE);
+#endif
+
+ sprintf(lastFileVar, "NULL");
+
+ filesProcessed = process_sub(data_file, struct_file);
+
+ /* data_file now contains all of the raw data.. now append linked list of
+ * file header structs to allow embedded app to search for a file name */
+ fprintf(data_file, NEWLINE NEWLINE);
+ fprintf(struct_file, "#define FS_ROOT file_%s" NEWLINE, lastFileVar);
+ fprintf(struct_file, "#define FS_NUMFILES %d" NEWLINE NEWLINE, filesProcessed);
+
+ fclose(data_file);
+ fclose(struct_file);
+
+ CHDIR(appPath);
+ /* append struct_file to data_file */
+ printf(NEWLINE "Creating target file..." NEWLINE NEWLINE);
+ concat_files("fsdata.tmp", "fshdr.tmp", targetfile);
+
+ /* if succeeded, delete the temporary files */
+ if (remove("fsdata.tmp") != 0) {
+ printf("Warning: failed to delete fsdata.tmp\n");
+ }
+ if (remove("fshdr.tmp") != 0) {
+ printf("Warning: failed to delete fshdr.tmp\n");
+ }
+
+ printf(NEWLINE "Processed %d files - done." NEWLINE, filesProcessed);
+#if MAKEFS_SUPPORT_DEFLATE
+ if (deflateNonSsiFiles) {
+ printf("(Deflated total byte reduction: %d bytes -> %d bytes (%.02f%%)" NEWLINE,
+ (int)overallDataBytes, (int)deflatedBytesReduced, (float)((deflatedBytesReduced * 100.0) / overallDataBytes));
+ }
+#endif
+ printf(NEWLINE);
+
+ while (first_file != NULL) {
+ struct file_entry *fe = first_file;
+ first_file = fe->next;
+ free(fe);
+ }
+
+ if (ssi_file_buffer) {
+ free(ssi_file_buffer);
+ }
+ if (ssi_file_lines) {
+ free(ssi_file_lines);
+ }
+
+ return 0;
+}
+
+int check_path(char *path, size_t size)
+{
+ size_t slen;
+ if (path[0] == 0) {
+ /* empty */
+ return 0;
+ }
+ slen = strlen(path);
+ if (slen >= size) {
+ /* not NULL-terminated */
+ return 0;
+ }
+ while ((slen > 0) && ((path[slen] == '\\') || (path[slen] == '/'))) {
+ /* path should not end with trailing backslash */
+ path[slen] = 0;
+ slen--;
+ }
+ if (slen == 0) {
+ return 0;
+ }
+ return 1;
+}
+
+static void copy_file(const char *filename_in, FILE *fout)
+{
+ FILE *fin;
+ size_t len;
+ void *buf;
+ fin = fopen(filename_in, "rb");
+ if (fin == NULL) {
+ printf("Failed to open file \"%s\"\n", filename_in);
+ exit(-1);
+ }
+ buf = malloc(COPY_BUFSIZE);
+ while ((len = fread(buf, 1, COPY_BUFSIZE, fin)) > 0) {
+ fwrite(buf, 1, len, fout);
+ }
+ free(buf);
+ fclose(fin);
+}
+
+void concat_files(const char *file1, const char *file2, const char *targetfile)
+{
+ FILE *fout;
+ fout = fopen(targetfile, "wb");
+ if (fout == NULL) {
+ printf("Failed to open file \"%s\"\n", targetfile);
+ exit(-1);
+ }
+ copy_file(file1, fout);
+ copy_file(file2, fout);
+ fclose(fout);
+}
+
+int process_sub(FILE *data_file, FILE *struct_file)
+{
+ tinydir_dir dir;
+ int filesProcessed = 0;
+
+ if (processSubs) {
+ /* process subs recursively */
+ size_t sublen = strlen(curSubdir);
+ size_t freelen = sizeof(curSubdir) - sublen - 1;
+ int ret;
+ LWIP_ASSERT("sublen < sizeof(curSubdir)", sublen < sizeof(curSubdir));
+
+ ret = tinydir_open_sorted(&dir, TINYDIR_STRING("."));
+
+ if (ret == 0) {
+ unsigned int i;
+ for (i = 0; i < dir.n_files; i++) {
+ tinydir_file file;
+
+ ret = tinydir_readfile_n(&dir, &file, i);
+
+ if (ret == 0) {
+#if (defined _MSC_VER || defined __MINGW32__) && (defined _UNICODE)
+ size_t num_char_converted;
+ char currName[256];
+ wcstombs_s(&num_char_converted, currName, sizeof(currName), file.name, sizeof(currName));
+#else
+ const char *currName = file.name;
+#endif
+
+ if (currName[0] == '.') {
+ continue;
+ }
+ if (!file.is_dir) {
+ continue;
+ }
+ if (freelen > 0) {
+ CHDIR(currName);
+ strncat(curSubdir, "/", freelen);
+ strncat(curSubdir, currName, freelen - 1);
+ curSubdir[sizeof(curSubdir) - 1] = 0;
+ printf("processing subdirectory %s/..." NEWLINE, curSubdir);
+ filesProcessed += process_sub(data_file, struct_file);
+ CHDIR("..");
+ curSubdir[sublen] = 0;
+ } else {
+ printf("WARNING: cannot process sub due to path length restrictions: \"%s/%s\"\n", curSubdir, currName);
+ }
+ }
+ }
+ }
+
+ ret = tinydir_open_sorted(&dir, TINYDIR_STRING("."));
+ if (ret == 0) {
+ unsigned int i;
+ for (i = 0; i < dir.n_files; i++) {
+ tinydir_file file;
+
+ ret = tinydir_readfile_n(&dir, &file, i);
+
+ if (ret == 0) {
+ if (!file.is_dir) {
+#if (defined _MSC_VER || defined __MINGW32__) && (defined _UNICODE)
+ size_t num_char_converted;
+ char curName[256];
+ wcstombs_s(&num_char_converted, curName, sizeof(curName), file.name, sizeof(curName));
+#else
+ const char *curName = file.name;
+#endif
+
+ if (strcmp(curName, "fsdata.tmp") == 0) {
+ continue;
+ }
+ if (strcmp(curName, "fshdr.tmp") == 0) {
+ continue;
+ }
+ if (file_to_exclude(curName)) {
+ printf("skipping %s/%s by exclude list (-x option)..." NEWLINE, curSubdir, curName);
+ continue;
+ }
+
+ printf("processing %s/%s..." NEWLINE, curSubdir, curName);
+
+ if (process_file(data_file, struct_file, curName) < 0) {
+ printf(NEWLINE "Error... aborting" NEWLINE);
+ return -1;
+ }
+ filesProcessed++;
+ }
+ }
+ }
+ }
+ }
+
+ return filesProcessed;
+}
+
+static u8_t *get_file_data(const char *filename, int *file_size, int can_be_compressed, int *is_compressed)
+{
+ FILE *inFile;
+ size_t fsize = 0;
+ u8_t *buf;
+ size_t r;
+ int rs;
+ LWIP_UNUSED_ARG(r); /* for LWIP_NOASSERT */
+ inFile = fopen(filename, "rb");
+ if (inFile == NULL) {
+ printf("Failed to open file \"%s\"\n", filename);
+ exit(-1);
+ }
+ fseek(inFile, 0, SEEK_END);
+ rs = ftell(inFile);
+ if (rs < 0) {
+ printf("ftell failed with %d\n", errno);
+ exit(-1);
+ }
+ fsize = (size_t)rs;
+ fseek(inFile, 0, SEEK_SET);
+ buf = (u8_t *)malloc(fsize);
+ LWIP_ASSERT("buf != NULL", buf != NULL);
+ r = fread(buf, 1, fsize, inFile);
+ LWIP_ASSERT("r == fsize", r == fsize);
+ *file_size = fsize;
+ *is_compressed = 0;
+#if MAKEFS_SUPPORT_DEFLATE
+ overallDataBytes += fsize;
+ if (deflateNonSsiFiles) {
+ if (can_be_compressed) {
+ if (fsize < OUT_BUF_SIZE) {
+ u8_t *ret_buf;
+ tdefl_status status;
+ size_t in_bytes = fsize;
+ size_t out_bytes = OUT_BUF_SIZE;
+ const void *next_in = buf;
+ void *next_out = s_outbuf;
+ /* create tdefl() compatible flags (we have to compose the low-level flags ourselves, or use tdefl_create_comp_flags_from_zip_params() but that means MINIZ_NO_ZLIB_APIS can't be defined). */
+ mz_uint comp_flags = s_tdefl_num_probes[MZ_MIN(10, deflate_level)] | ((deflate_level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0);
+ if (!deflate_level) {
+ comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS;
+ }
+ status = tdefl_init(&g_deflator, NULL, NULL, comp_flags);
+ if (status != TDEFL_STATUS_OKAY) {
+ printf("tdefl_init() failed!\n");
+ exit(-1);
+ }
+ memset(s_outbuf, 0, sizeof(s_outbuf));
+ status = tdefl_compress(&g_deflator, next_in, &in_bytes, next_out, &out_bytes, TDEFL_FINISH);
+ if (status != TDEFL_STATUS_DONE) {
+ printf("deflate failed: %d\n", status);
+ exit(-1);
+ }
+ LWIP_ASSERT("out_bytes <= COPY_BUFSIZE", out_bytes <= OUT_BUF_SIZE);
+ if (out_bytes < fsize) {
+ ret_buf = (u8_t *)malloc(out_bytes);
+ LWIP_ASSERT("ret_buf != NULL", ret_buf != NULL);
+ memcpy(ret_buf, s_outbuf, out_bytes);
+ {
+ /* sanity-check compression be inflating and comparing to the original */
+ tinfl_status dec_status;
+ tinfl_decompressor inflator;
+ size_t dec_in_bytes = out_bytes;
+ size_t dec_out_bytes = OUT_BUF_SIZE;
+ next_out = s_checkbuf;
+
+ tinfl_init(&inflator);
+ memset(s_checkbuf, 0, sizeof(s_checkbuf));
+ dec_status = tinfl_decompress(&inflator, (const mz_uint8 *)ret_buf, &dec_in_bytes, s_checkbuf, (mz_uint8 *)next_out, &dec_out_bytes, 0);
+ LWIP_ASSERT("tinfl_decompress failed", dec_status == TINFL_STATUS_DONE);
+ LWIP_ASSERT("tinfl_decompress size mismatch", fsize == dec_out_bytes);
+ LWIP_ASSERT("decompressed memcmp failed", !memcmp(s_checkbuf, buf, fsize));
+ }
+ /* free original buffer, use compressed data + size */
+ free(buf);
+ buf = ret_buf;
+ *file_size = out_bytes;
+ printf(" - deflate: %d bytes -> %d bytes (%.02f%%)" NEWLINE, (int)fsize, (int)out_bytes, (float)((out_bytes * 100.0) / fsize));
+ deflatedBytesReduced += (size_t)(fsize - out_bytes);
+ *is_compressed = 1;
+ } else {
+ printf(" - uncompressed: (would be %d bytes larger using deflate)" NEWLINE, (int)(out_bytes - fsize));
+ }
+ } else {
+ printf(" - uncompressed: (file is larger than deflate bufer)" NEWLINE);
+ }
+ } else {
+ printf(" - cannot be compressed" NEWLINE);
+ }
+ }
+#else
+ LWIP_UNUSED_ARG(can_be_compressed);
+#endif
+ fclose(inFile);
+ return buf;
+}
+
+static void process_file_data(FILE *data_file, u8_t *file_data, size_t file_size)
+{
+ size_t written, i, src_off = 0;
+ size_t off = 0;
+ LWIP_UNUSED_ARG(written); /* for LWIP_NOASSERT */
+ for (i = 0; i < file_size; i++) {
+ LWIP_ASSERT("file_buffer_c overflow", off < sizeof(file_buffer_c) - 5);
+ sprintf(&file_buffer_c[off], "0x%02x,", file_data[i]);
+ off += 5;
+ if ((++src_off % HEX_BYTES_PER_LINE) == 0) {
+ LWIP_ASSERT("file_buffer_c overflow", off < sizeof(file_buffer_c) - NEWLINE_LEN);
+ memcpy(&file_buffer_c[off], NEWLINE, NEWLINE_LEN);
+ off += NEWLINE_LEN;
+ }
+ if (off + 20 >= sizeof(file_buffer_c)) {
+ written = fwrite(file_buffer_c, 1, off, data_file);
+ LWIP_ASSERT("written == off", written == off);
+ off = 0;
+ }
+ }
+ written = fwrite(file_buffer_c, 1, off, data_file);
+ LWIP_ASSERT("written == off", written == off);
+}
+
+static int write_checksums(FILE *struct_file, const char *varname,
+ u16_t hdr_len, u16_t hdr_chksum, const u8_t *file_data, size_t file_size)
+{
+ int chunk_size = TCP_MSS;
+ int offset, src_offset;
+ size_t len;
+ int i = 0;
+#if LWIP_TCP_TIMESTAMPS
+ /* when timestamps are used, usable space is 12 bytes less per segment */
+ chunk_size -= 12;
+#endif
+
+ fprintf(struct_file, "#if HTTPD_PRECALCULATED_CHECKSUM" NEWLINE);
+ fprintf(struct_file, "const struct fsdata_chksum chksums_%s[] = {" NEWLINE, varname);
+
+ if (hdr_len > 0) {
+ /* add checksum for HTTP header */
+ fprintf(struct_file, "{%d, 0x%04x, %d}," NEWLINE, 0, hdr_chksum, hdr_len);
+ i++;
+ }
+ src_offset = 0;
+ for (offset = hdr_len; ; offset += len) {
+ unsigned short chksum;
+ const void *data = (const void *)&file_data[src_offset];
+ len = LWIP_MIN(chunk_size, (int)file_size - src_offset);
+ if (len == 0) {
+ break;
+ }
+ chksum = ~inet_chksum(data, (u16_t)len);
+ /* add checksum for data */
+ fprintf(struct_file, "{%d, 0x%04x, %"SZT_F"}," NEWLINE, offset, chksum, len);
+ i++;
+ }
+ fprintf(struct_file, "};" NEWLINE);
+ fprintf(struct_file, "#endif /* HTTPD_PRECALCULATED_CHECKSUM */" NEWLINE);
+ return i;
+}
+
+static int is_valid_char_for_c_var(char x)
+{
+ if (((x >= 'A') && (x <= 'Z')) ||
+ ((x >= 'a') && (x <= 'z')) ||
+ ((x >= '0') && (x <= '9')) ||
+ (x == '_')) {
+ return 1;
+ }
+ return 0;
+}
+
+static void fix_filename_for_c(char *qualifiedName, size_t max_len)
+{
+ struct file_entry *f;
+ size_t len = strlen(qualifiedName);
+ char *new_name = (char *)malloc(len + 2);
+ int filename_ok;
+ int cnt = 0;
+ size_t i;
+ if (len + 3 == max_len) {
+ printf("File name too long: \"%s\"\n", qualifiedName);
+ exit(-1);
+ }
+ strcpy(new_name, qualifiedName);
+ for (i = 0; i < len; i++) {
+ if (!is_valid_char_for_c_var(new_name[i])) {
+ new_name[i] = '_';
+ }
+ }
+ do {
+ filename_ok = 1;
+ for (f = first_file; f != NULL; f = f->next) {
+ if (!strcmp(f->filename_c, new_name)) {
+ filename_ok = 0;
+ cnt++;
+ /* try next unique file name */
+ sprintf(&new_name[len], "%d", cnt);
+ break;
+ }
+ }
+ } while (!filename_ok && (cnt < 999));
+ if (!filename_ok) {
+ printf("Failed to get unique file name: \"%s\"\n", qualifiedName);
+ exit(-1);
+ }
+ strcpy(qualifiedName, new_name);
+ free(new_name);
+}
+
+static void register_filename(const char *qualifiedName)
+{
+ struct file_entry *fe = (struct file_entry *)malloc(sizeof(struct file_entry));
+ fe->filename_c = strdup(qualifiedName);
+ fe->next = NULL;
+ if (first_file == NULL) {
+ first_file = last_file = fe;
+ } else {
+ last_file->next = fe;
+ last_file = fe;
+ }
+}
+
+static int checkSsiByFilelist(const char* filename_listfile)
+{
+ FILE *f = fopen(filename_listfile, "r");
+ if (f != NULL) {
+ char *buf;
+ long rs;
+ size_t fsize, readcount;
+ size_t i, l, num_lines;
+ char **lines;
+ int state;
+
+ fseek(f, 0, SEEK_END);
+ rs = ftell(f);
+ if (rs < 0) {
+ printf("ftell failed with %d\n", errno);
+ fclose(f);
+ return 0;
+ }
+ fsize = (size_t)rs;
+ fseek(f, 0, SEEK_SET);
+ buf = (char*)malloc(fsize);
+ if (!buf) {
+ printf("failed to allocate ssi file buffer\n");
+ fclose(f);
+ return 0;
+ }
+ memset(buf, 0, fsize);
+ readcount = fread(buf, 1, fsize, f);
+ fclose(f);
+ if ((readcount > fsize) || !readcount) {
+ printf("failed to read data from ssi file\n");
+ free(buf);
+ return 0;
+ }
+
+ /* first pass: get the number of lines (and convert newlines to '0') */
+ num_lines = 1;
+ for (i = 0; i < readcount; i++) {
+ if (buf[i] == '\n') {
+ num_lines++;
+ buf[i] = 0;
+ } else if (buf[i] == '\r') {
+ buf[i] = 0;
+ }
+ }
+ /* allocate the line pointer array */
+ lines = (char**)malloc(sizeof(char*) * num_lines);
+ if (!lines) {
+ printf("failed to allocate ssi line buffer\n");
+ free(buf);
+ return 0;
+ }
+ memset(lines, 0, sizeof(char*) * num_lines);
+ l = 0;
+ state = 0;
+ for (i = 0; i < readcount; i++) {
+ if (state) {
+ /* waiting for null */
+ if (buf[i] == 0) {
+ state = 0;
+ }
+ } else {
+ /* waiting for beginning of new string */
+ if (buf[i] != 0) {
+ LWIP_ASSERT("lines array overflow", l < num_lines);
+ lines[l] = &buf[i];
+ state = 1;
+ l++;
+ }
+ }
+ }
+ LWIP_ASSERT("lines array overflow", l < num_lines);
+
+ ssi_file_buffer = buf;
+ ssi_file_lines = lines;
+ ssi_file_num_lines = l;
+ }
+ return 0;
+}
+
+static int is_ssi_file(const char *filename)
+{
+ if (supportSsi) {
+ if (ssi_file_buffer) {
+ /* compare by list */
+ size_t i;
+ int ret = 0;
+ /* build up the relative path to this file */
+ size_t sublen = strlen(curSubdir);
+ size_t freelen = sizeof(curSubdir) - sublen - 1;
+ strncat(curSubdir, "/", freelen);
+ strncat(curSubdir, filename, freelen - 1);
+ curSubdir[sizeof(curSubdir) - 1] = 0;
+ for (i = 0; i < ssi_file_num_lines; i++) {
+ const char *listed_file = ssi_file_lines[i];
+ /* compare without the leading '/' */
+ if (!strcmp(&curSubdir[1], listed_file)) {
+ ret = 1;
+ }
+ }
+ curSubdir[sublen] = 0;
+ return ret;
+ } else {
+ /* check file extension */
+ size_t loop;
+ for (loop = 0; loop < NUM_SHTML_EXTENSIONS; loop++) {
+ if (strstr(filename, g_pcSSIExtensions[loop])) {
+ return 1;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static int ext_in_list(const char* filename, const char *ext_list)
+{
+ int found = 0;
+ const char *ext = ext_list;
+ if (ext_list == NULL) {
+ return 0;
+ }
+ while(*ext != '\0') {
+ const char *comma = strchr(ext, ',');
+ size_t ext_size;
+ size_t filename_size = strlen(filename);
+ if (comma == NULL) {
+ comma = strchr(ext, '\0');
+ }
+ ext_size = comma - ext;
+ if ((filename[filename_size - ext_size - 1] == '.') &&
+ !strncmp(&filename[filename_size - ext_size], ext, ext_size)) {
+ found = 1;
+ break;
+ }
+ ext = comma + 1;
+ }
+
+ return found;
+}
+
+static int file_to_exclude(const char *filename)
+{
+ return (exclude_list != NULL) && ext_in_list(filename, exclude_list);
+}
+
+static int file_can_be_compressed(const char *filename)
+{
+ return (ncompress_list == NULL) || !ext_in_list(filename, ncompress_list);
+}
+
+int process_file(FILE *data_file, FILE *struct_file, const char *filename)
+{
+ char varname[MAX_PATH_LEN];
+ int i = 0;
+ char qualifiedName[MAX_PATH_LEN];
+ int file_size;
+ u16_t http_hdr_chksum = 0;
+ u16_t http_hdr_len = 0;
+ int chksum_count = 0;
+ u8_t flags = 0;
+ u8_t has_content_len;
+ u8_t *file_data;
+ int is_ssi;
+ int can_be_compressed;
+ int is_compressed = 0;
+ int flags_printed;
+
+ /* create qualified name (@todo: prepend slash or not?) */
+ sprintf(qualifiedName, "%s/%s", curSubdir, filename);
+ /* create C variable name */
+ strcpy(varname, qualifiedName);
+ /* convert slashes & dots to underscores */
+ fix_filename_for_c(varname, MAX_PATH_LEN);
+ register_filename(varname);
+#if ALIGN_PAYLOAD
+ /* to force even alignment of array, type 1 */
+ fprintf(data_file, "#if FSDATA_FILE_ALIGNMENT==1" NEWLINE);
+ fprintf(data_file, "static const " PAYLOAD_ALIGN_TYPE " dummy_align_%s = %d;" NEWLINE, varname, payload_alingment_dummy_counter++);
+ fprintf(data_file, "#endif" NEWLINE);
+#endif /* ALIGN_PAYLOAD */
+ fprintf(data_file, "static const unsigned char FSDATA_ALIGN_PRE data_%s[] FSDATA_ALIGN_POST = {" NEWLINE, varname);
+ /* encode source file name (used by file system, not returned to browser) */
+ fprintf(data_file, "/* %s (%"SZT_F" chars) */" NEWLINE, qualifiedName, strlen(qualifiedName) + 1);
+ file_put_ascii(data_file, qualifiedName, strlen(qualifiedName) + 1, &i);
+#if ALIGN_PAYLOAD
+ /* pad to even number of bytes to assure payload is on aligned boundary */
+ while (i % PAYLOAD_ALIGNMENT != 0) {
+ fprintf(data_file, "0x%02x,", 0);
+ i++;
+ }
+#endif /* ALIGN_PAYLOAD */
+ fprintf(data_file, NEWLINE);
+
+ is_ssi = is_ssi_file(filename);
+ if (is_ssi) {
+ flags |= FS_FILE_FLAGS_SSI;
+ }
+ has_content_len = !is_ssi;
+ can_be_compressed = includeHttpHeader && !is_ssi && file_can_be_compressed(filename);
+ file_data = get_file_data(filename, &file_size, can_be_compressed, &is_compressed);
+ if (includeHttpHeader) {
+ file_write_http_header(data_file, filename, file_size, &http_hdr_len, &http_hdr_chksum, has_content_len, is_compressed);
+ flags |= FS_FILE_FLAGS_HEADER_INCLUDED;
+ if (has_content_len) {
+ flags |= FS_FILE_FLAGS_HEADER_PERSISTENT;
+ if (useHttp11) {
+ flags |= FS_FILE_FLAGS_HEADER_HTTPVER_1_1;
+ }
+ }
+ }
+ if (precalcChksum) {
+ chksum_count = write_checksums(struct_file, varname, http_hdr_len, http_hdr_chksum, file_data, file_size);
+ }
+
+ /* build declaration of struct fsdata_file in temp file */
+ fprintf(struct_file, "const struct fsdata_file file_%s[] = { {" NEWLINE, varname);
+ fprintf(struct_file, "file_%s," NEWLINE, lastFileVar);
+ fprintf(struct_file, "data_%s," NEWLINE, varname);
+ fprintf(struct_file, "data_%s + %d," NEWLINE, varname, i);
+ fprintf(struct_file, "sizeof(data_%s) - %d," NEWLINE, varname, i);
+
+ flags_printed = 0;
+ if (flags & FS_FILE_FLAGS_HEADER_INCLUDED) {
+ fputs("FS_FILE_FLAGS_HEADER_INCLUDED", struct_file);
+ flags_printed = 1;
+ }
+ if (flags & FS_FILE_FLAGS_HEADER_PERSISTENT) {
+ if (flags_printed) {
+ fputs(" | ", struct_file);
+ }
+ fputs("FS_FILE_FLAGS_HEADER_PERSISTENT", struct_file);
+ flags_printed = 1;
+ }
+ if (flags & FS_FILE_FLAGS_HEADER_HTTPVER_1_1) {
+ if (flags_printed) {
+ fputs(" | ", struct_file);
+ }
+ fputs("FS_FILE_FLAGS_HEADER_HTTPVER_1_1", struct_file);
+ flags_printed = 1;
+ }
+ if (flags & FS_FILE_FLAGS_SSI) {
+ if (flags_printed) {
+ fputs(" | ", struct_file);
+ }
+ fputs("FS_FILE_FLAGS_SSI", struct_file);
+ flags_printed = 1;
+ }
+ if (!flags_printed) {
+ fputs("0", struct_file);
+ }
+ fputs("," NEWLINE, struct_file);
+ if (precalcChksum) {
+ fprintf(struct_file, "#if HTTPD_PRECALCULATED_CHECKSUM" NEWLINE);
+ fprintf(struct_file, "%d, chksums_%s," NEWLINE, chksum_count, varname);
+ fprintf(struct_file, "#endif /* HTTPD_PRECALCULATED_CHECKSUM */" NEWLINE);
+ }
+ fprintf(struct_file, "}};" NEWLINE NEWLINE);
+ strcpy(lastFileVar, varname);
+
+ /* write actual file contents */
+ i = 0;
+ fprintf(data_file, NEWLINE "/* raw file data (%d bytes) */" NEWLINE, file_size);
+ process_file_data(data_file, file_data, file_size);
+ fprintf(data_file, "};" NEWLINE NEWLINE);
+ free(file_data);
+ return 0;
+}
+
+int file_write_http_header(FILE *data_file, const char *filename, int file_size, u16_t *http_hdr_len,
+ u16_t *http_hdr_chksum, u8_t provide_content_len, int is_compressed)
+{
+ int i = 0;
+ int response_type = HTTP_HDR_OK;
+ const char *file_type;
+ const char *cur_string;
+ size_t cur_len;
+ int written = 0;
+ size_t hdr_len = 0;
+ u16_t acc;
+ const char *file_ext;
+ size_t j;
+ u8_t provide_last_modified = includeLastModified;
+
+ memset(hdr_buf, 0, sizeof(hdr_buf));
+
+ if (useHttp11) {
+ response_type = HTTP_HDR_OK_11;
+ }
+
+ fprintf(data_file, NEWLINE "/* HTTP header */");
+ if (strstr(filename, "404") == filename) {
+ response_type = HTTP_HDR_NOT_FOUND;
+ if (useHttp11) {
+ response_type = HTTP_HDR_NOT_FOUND_11;
+ }
+ } else if (strstr(filename, "400") == filename) {
+ response_type = HTTP_HDR_BAD_REQUEST;
+ if (useHttp11) {
+ response_type = HTTP_HDR_BAD_REQUEST_11;
+ }
+ } else if (strstr(filename, "501") == filename) {
+ response_type = HTTP_HDR_NOT_IMPL;
+ if (useHttp11) {
+ response_type = HTTP_HDR_NOT_IMPL_11;
+ }
+ }
+ cur_string = g_psHTTPHeaderStrings[response_type];
+ cur_len = strlen(cur_string);
+ fprintf(data_file, NEWLINE "/* \"%s\" (%"SZT_F" bytes) */" NEWLINE, cur_string, cur_len);
+ written += file_put_ascii(data_file, cur_string, cur_len, &i);
+ i = 0;
+ if (precalcChksum) {
+ memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
+ hdr_len += cur_len;
+ }
+
+ cur_string = serverID;
+ cur_len = strlen(cur_string);
+ fprintf(data_file, NEWLINE "/* \"%s\" (%"SZT_F" bytes) */" NEWLINE, cur_string, cur_len);
+ written += file_put_ascii(data_file, cur_string, cur_len, &i);
+ i = 0;
+ if (precalcChksum) {
+ memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
+ hdr_len += cur_len;
+ }
+
+ file_ext = filename;
+ if (file_ext != NULL) {
+ while (strstr(file_ext, ".") != NULL) {
+ file_ext = strstr(file_ext, ".");
+ file_ext++;
+ }
+ }
+ if ((file_ext == NULL) || (*file_ext == 0)) {
+ printf("failed to get extension for file \"%s\", using default.\n", filename);
+ file_type = HTTP_HDR_DEFAULT_TYPE;
+ } else {
+ file_type = NULL;
+ for (j = 0; j < NUM_HTTP_HEADERS; j++) {
+ if (!strcmp(file_ext, g_psHTTPHeaders[j].extension)) {
+ file_type = g_psHTTPHeaders[j].content_type;
+ break;
+ }
+ }
+ if (file_type == NULL) {
+ printf("failed to get file type for extension \"%s\", using default.\n", file_ext);
+ file_type = HTTP_HDR_DEFAULT_TYPE;
+ }
+ }
+
+ /* Content-Length is used for persistent connections in HTTP/1.1 but also for
+ download progress in older versions
+ @todo: just use a big-enough buffer and let the HTTPD send spaces? */
+ if (provide_content_len) {
+ char intbuf[MAX_PATH_LEN];
+ int content_len = file_size;
+ memset(intbuf, 0, sizeof(intbuf));
+ cur_string = g_psHTTPHeaderStrings[HTTP_HDR_CONTENT_LENGTH];
+ cur_len = strlen(cur_string);
+ fprintf(data_file, NEWLINE "/* \"%s%d\r\n\" (%"SZT_F"+ bytes) */" NEWLINE, cur_string, content_len, cur_len + 2);
+ written += file_put_ascii(data_file, cur_string, cur_len, &i);
+ if (precalcChksum) {
+ memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
+ hdr_len += cur_len;
+ }
+
+ lwip_itoa(intbuf, sizeof(intbuf), content_len);
+ strcat(intbuf, "\r\n");
+ cur_len = strlen(intbuf);
+ written += file_put_ascii(data_file, intbuf, cur_len, &i);
+ i = 0;
+ if (precalcChksum) {
+ memcpy(&hdr_buf[hdr_len], intbuf, cur_len);
+ hdr_len += cur_len;
+ }
+ }
+ if (provide_last_modified) {
+ char modbuf[256];
+ struct stat stat_data;
+ struct tm *t;
+ memset(modbuf, 0, sizeof(modbuf));
+ memset(&stat_data, 0, sizeof(stat_data));
+ cur_string = modbuf;
+ strcpy(modbuf, "Last-Modified: ");
+ if (stat(filename, &stat_data) != 0) {
+ printf("stat(%s) failed with error %d\n", filename, errno);
+ exit(-1);
+ }
+ t = gmtime(&stat_data.st_mtime);
+ if (t == NULL) {
+ printf("gmtime() failed with error %d\n", errno);
+ exit(-1);
+ }
+ strftime(&modbuf[15], sizeof(modbuf) - 15, "%a, %d %b %Y %H:%M:%S GMT", t);
+ cur_len = strlen(cur_string);
+ fprintf(data_file, NEWLINE "/* \"%s\"\r\n\" (%"SZT_F"+ bytes) */" NEWLINE, cur_string, cur_len + 2);
+ written += file_put_ascii(data_file, cur_string, cur_len, &i);
+ if (precalcChksum) {
+ memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
+ hdr_len += cur_len;
+ }
+
+ modbuf[0] = 0;
+ strcat(modbuf, "\r\n");
+ cur_len = strlen(modbuf);
+ written += file_put_ascii(data_file, modbuf, cur_len, &i);
+ i = 0;
+ if (precalcChksum) {
+ memcpy(&hdr_buf[hdr_len], modbuf, cur_len);
+ hdr_len += cur_len;
+ }
+ }
+
+ /* HTTP/1.1 implements persistent connections */
+ if (useHttp11) {
+ if (provide_content_len) {
+ cur_string = g_psHTTPHeaderStrings[HTTP_HDR_CONN_KEEPALIVE];
+ } else {
+ /* no Content-Length available, so a persistent connection is no possible
+ because the client does not know the data length */
+ cur_string = g_psHTTPHeaderStrings[HTTP_HDR_CONN_CLOSE];
+ }
+ cur_len = strlen(cur_string);
+ fprintf(data_file, NEWLINE "/* \"%s\" (%"SZT_F" bytes) */" NEWLINE, cur_string, cur_len);
+ written += file_put_ascii(data_file, cur_string, cur_len, &i);
+ i = 0;
+ if (precalcChksum) {
+ memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
+ hdr_len += cur_len;
+ }
+ }
+
+#if MAKEFS_SUPPORT_DEFLATE
+ if (is_compressed) {
+ /* tell the client about the deflate encoding */
+ LWIP_ASSERT("error", deflateNonSsiFiles);
+ cur_string = "Content-Encoding: deflate\r\n";
+ cur_len = strlen(cur_string);
+ fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len);
+ written += file_put_ascii(data_file, cur_string, cur_len, &i);
+ i = 0;
+ }
+#else
+ LWIP_UNUSED_ARG(is_compressed);
+#endif
+
+ /* write content-type, ATTENTION: this includes the double-CRLF! */
+ cur_string = file_type;
+ cur_len = strlen(cur_string);
+ fprintf(data_file, NEWLINE "/* \"%s\" (%"SZT_F" bytes) */" NEWLINE, cur_string, cur_len);
+ written += file_put_ascii(data_file, cur_string, cur_len, &i);
+ i = 0;
+
+ /* ATTENTION: headers are done now (double-CRLF has been written!) */
+
+ if (precalcChksum) {
+ LWIP_ASSERT("hdr_len + cur_len <= sizeof(hdr_buf)", hdr_len + cur_len <= sizeof(hdr_buf));
+ memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
+ hdr_len += cur_len;
+
+ LWIP_ASSERT("strlen(hdr_buf) == hdr_len", strlen(hdr_buf) == hdr_len);
+ acc = ~inet_chksum(hdr_buf, (u16_t)hdr_len);
+ *http_hdr_len = (u16_t)hdr_len;
+ *http_hdr_chksum = acc;
+ }
+
+ return written;
+}
+
+int file_put_ascii(FILE *file, const char *ascii_string, int len, int *i)
+{
+ int x;
+ for (x = 0; x < len; x++) {
+ unsigned char cur = ascii_string[x];
+ fprintf(file, "0x%02x,", cur);
+ if ((++(*i) % HEX_BYTES_PER_LINE) == 0) {
+ fprintf(file, NEWLINE);
+ }
+ }
+ return len;
+}
+
+int s_put_ascii(char *buf, const char *ascii_string, int len, int *i)
+{
+ int x;
+ int idx = 0;
+ for (x = 0; x < len; x++) {
+ unsigned char cur = ascii_string[x];
+ sprintf(&buf[idx], "0x%02x,", cur);
+ idx += 5;
+ if ((++(*i) % HEX_BYTES_PER_LINE) == 0) {
+ sprintf(&buf[idx], NEWLINE);
+ idx += NEWLINE_LEN;
+ }
+ }
+ return len;
+}
diff --git a/resources/ethernet/LwIP/apps/http/makefsdata/readme.txt b/resources/ethernet/LwIP/apps/http/makefsdata/readme.txt
new file mode 100644
index 00000000..41c7d800
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/http/makefsdata/readme.txt
@@ -0,0 +1,13 @@
+This directory contains a script ('makefsdata') to create C code suitable for
+httpd for given html pages (or other files) in a directory.
+
+There is also a plain C console application doing the same and extended a bit.
+
+Usage: htmlgen [targetdir] [-s] [-i]s
+ targetdir: relative or absolute path to files to convert
+ switch -s: toggle processing of subdirectories (default is on)
+ switch -e: exclude HTTP header from file (header is created at runtime, default is on)
+ switch -11: include HTTP 1.1 header (1.0 is default)
+
+ if targetdir not specified, makefsdata will attempt to
+ process files in subdirectory 'fs'.
diff --git a/resources/ethernet/LwIP/apps/http/makefsdata/tinydir.h b/resources/ethernet/LwIP/apps/http/makefsdata/tinydir.h
new file mode 100644
index 00000000..69dac0a5
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/http/makefsdata/tinydir.h
@@ -0,0 +1,808 @@
+/*
+Copyright (c) 2013-2017, tinydir authors:
+- Cong Xu
+- Lautis Sun
+- Baudouin Feildel
+- Andargor
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef TINYDIR_H
+#define TINYDIR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if ((defined _UNICODE) && !(defined UNICODE))
+#define UNICODE
+#endif
+
+#if ((defined UNICODE) && !(defined _UNICODE))
+#define _UNICODE
+#endif
+
+#include
+#include
+#include
+#ifdef _MSC_VER
+# define WIN32_LEAN_AND_MEAN
+# include
+# include
+# pragma warning(push)
+# pragma warning (disable : 4996)
+#else
+# include
+# include
+# include
+# include
+#endif
+#ifdef __MINGW32__
+# include
+#endif
+
+
+/* types */
+
+/* Windows UNICODE wide character support */
+#if defined _MSC_VER || defined __MINGW32__
+#define _tinydir_char_t TCHAR
+#define TINYDIR_STRING(s) _TEXT(s)
+#define _tinydir_strlen _tcslen
+#define _tinydir_strcpy _tcscpy
+#define _tinydir_strcat _tcscat
+#define _tinydir_strcmp _tcscmp
+#define _tinydir_strrchr _tcsrchr
+#define _tinydir_strncmp _tcsncmp
+#else
+#define _tinydir_char_t char
+#define TINYDIR_STRING(s) s
+#define _tinydir_strlen strlen
+#define _tinydir_strcpy strcpy
+#define _tinydir_strcat strcat
+#define _tinydir_strcmp strcmp
+#define _tinydir_strrchr strrchr
+#define _tinydir_strncmp strncmp
+#endif
+
+#if (defined _MSC_VER || defined __MINGW32__)
+#include
+#define _TINYDIR_PATH_MAX MAX_PATH
+#elif defined __linux__
+#include
+#define _TINYDIR_PATH_MAX PATH_MAX
+#else
+#define _TINYDIR_PATH_MAX 4096
+#endif
+
+#ifdef _MSC_VER
+/* extra chars for the "\\*" mask */
+# define _TINYDIR_PATH_EXTRA 2
+#else
+# define _TINYDIR_PATH_EXTRA 0
+#endif
+
+#define _TINYDIR_FILENAME_MAX 256
+
+#if (defined _MSC_VER || defined __MINGW32__)
+#define _TINYDIR_DRIVE_MAX 3
+#endif
+
+#ifdef _MSC_VER
+# define _TINYDIR_FUNC static __inline
+#elif !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L
+# define _TINYDIR_FUNC static __inline__
+#else
+# define _TINYDIR_FUNC static inline
+#endif
+
+/* readdir_r usage; define TINYDIR_USE_READDIR_R to use it (if supported) */
+#ifdef TINYDIR_USE_READDIR_R
+
+/* readdir_r is a POSIX-only function, and may not be available under various
+ * environments/settings, e.g. MinGW. Use readdir fallback */
+#if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _BSD_SOURCE || _SVID_SOURCE ||\
+ _POSIX_SOURCE
+# define _TINYDIR_HAS_READDIR_R
+#endif
+#if _POSIX_C_SOURCE >= 200112L
+# define _TINYDIR_HAS_FPATHCONF
+# include
+#endif
+#if _BSD_SOURCE || _SVID_SOURCE || \
+ (_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700)
+# define _TINYDIR_HAS_DIRFD
+# include
+#endif
+#if defined _TINYDIR_HAS_FPATHCONF && defined _TINYDIR_HAS_DIRFD &&\
+ defined _PC_NAME_MAX
+# define _TINYDIR_USE_FPATHCONF
+#endif
+#if defined __MINGW32__ || !defined _TINYDIR_HAS_READDIR_R ||\
+ !(defined _TINYDIR_USE_FPATHCONF || defined NAME_MAX)
+# define _TINYDIR_USE_READDIR
+#endif
+
+/* Use readdir by default */
+#else
+# define _TINYDIR_USE_READDIR
+#endif
+
+/* MINGW32 has two versions of dirent, ASCII and UNICODE*/
+#ifndef _MSC_VER
+#if (defined __MINGW32__) && (defined _UNICODE)
+#define _TINYDIR_DIR _WDIR
+#define _tinydir_dirent _wdirent
+#define _tinydir_opendir _wopendir
+#define _tinydir_readdir _wreaddir
+#define _tinydir_closedir _wclosedir
+#else
+#define _TINYDIR_DIR DIR
+#define _tinydir_dirent dirent
+#define _tinydir_opendir opendir
+#define _tinydir_readdir readdir
+#define _tinydir_closedir closedir
+#endif
+#endif
+
+/* Allow user to use a custom allocator by defining _TINYDIR_MALLOC and _TINYDIR_FREE. */
+#if defined(_TINYDIR_MALLOC) && defined(_TINYDIR_FREE)
+#elif !defined(_TINYDIR_MALLOC) && !defined(_TINYDIR_FREE)
+#else
+#error "Either define both alloc and free or none of them!"
+#endif
+
+#if !defined(_TINYDIR_MALLOC)
+ #define _TINYDIR_MALLOC(_size) malloc(_size)
+ #define _TINYDIR_FREE(_ptr) free(_ptr)
+#endif /* !defined(_TINYDIR_MALLOC) */
+
+typedef struct tinydir_file
+{
+ _tinydir_char_t path[_TINYDIR_PATH_MAX];
+ _tinydir_char_t name[_TINYDIR_FILENAME_MAX];
+ _tinydir_char_t *extension;
+ int is_dir;
+ int is_reg;
+
+#ifndef _MSC_VER
+#ifdef __MINGW32__
+ struct _stat _s;
+#else
+ struct stat _s;
+#endif
+#endif
+} tinydir_file;
+
+typedef struct tinydir_dir
+{
+ _tinydir_char_t path[_TINYDIR_PATH_MAX];
+ int has_next;
+ size_t n_files;
+
+ tinydir_file *_files;
+#ifdef _MSC_VER
+ HANDLE _h;
+ WIN32_FIND_DATA _f;
+#else
+ _TINYDIR_DIR *_d;
+ struct _tinydir_dirent *_e;
+#ifndef _TINYDIR_USE_READDIR
+ struct _tinydir_dirent *_ep;
+#endif
+#endif
+} tinydir_dir;
+
+
+/* declarations */
+
+_TINYDIR_FUNC
+int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path);
+_TINYDIR_FUNC
+int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path);
+_TINYDIR_FUNC
+void tinydir_close(tinydir_dir *dir);
+
+_TINYDIR_FUNC
+int tinydir_next(tinydir_dir *dir);
+_TINYDIR_FUNC
+int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file);
+_TINYDIR_FUNC
+int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i);
+_TINYDIR_FUNC
+int tinydir_open_subdir_n(tinydir_dir *dir, size_t i);
+
+_TINYDIR_FUNC
+int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path);
+_TINYDIR_FUNC
+void _tinydir_get_ext(tinydir_file *file);
+_TINYDIR_FUNC
+int _tinydir_file_cmp(const void *a, const void *b);
+#ifndef _MSC_VER
+#ifndef _TINYDIR_USE_READDIR
+_TINYDIR_FUNC
+size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp);
+#endif
+#endif
+
+
+/* definitions*/
+
+_TINYDIR_FUNC
+int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path)
+{
+#ifndef _MSC_VER
+#ifndef _TINYDIR_USE_READDIR
+ int error;
+ int size; /* using int size */
+#endif
+#else
+ _tinydir_char_t path_buf[_TINYDIR_PATH_MAX];
+#endif
+ _tinydir_char_t *pathp;
+
+ if (dir == NULL || path == NULL || _tinydir_strlen(path) == 0)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX)
+ {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+
+ /* initialise dir */
+ dir->_files = NULL;
+#ifdef _MSC_VER
+ dir->_h = INVALID_HANDLE_VALUE;
+#else
+ dir->_d = NULL;
+#ifndef _TINYDIR_USE_READDIR
+ dir->_ep = NULL;
+#endif
+#endif
+ tinydir_close(dir);
+
+ _tinydir_strcpy(dir->path, path);
+ /* Remove trailing slashes */
+ pathp = &dir->path[_tinydir_strlen(dir->path) - 1];
+ while (pathp != dir->path && (*pathp == TINYDIR_STRING('\\') || *pathp == TINYDIR_STRING('/')))
+ {
+ *pathp = TINYDIR_STRING('\0');
+ pathp++;
+ }
+#ifdef _MSC_VER
+ _tinydir_strcpy(path_buf, dir->path);
+ _tinydir_strcat(path_buf, TINYDIR_STRING("\\*"));
+#if (defined WINAPI_FAMILY) && (WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP)
+ dir->_h = FindFirstFileEx(path_buf, FindExInfoStandard, &dir->_f, FindExSearchNameMatch, NULL, 0);
+#else
+ dir->_h = FindFirstFile(path_buf, &dir->_f);
+#endif
+ if (dir->_h == INVALID_HANDLE_VALUE)
+ {
+ errno = ENOENT;
+#else
+ dir->_d = _tinydir_opendir(path);
+ if (dir->_d == NULL)
+ {
+#endif
+ goto bail;
+ }
+
+ /* read first file */
+ dir->has_next = 1;
+#ifndef _MSC_VER
+#ifdef _TINYDIR_USE_READDIR
+ dir->_e = _tinydir_readdir(dir->_d);
+#else
+ /* allocate dirent buffer for readdir_r */
+ size = _tinydir_dirent_buf_size(dir->_d); /* conversion to int */
+ if (size == -1) return -1;
+ dir->_ep = (struct _tinydir_dirent*)_TINYDIR_MALLOC(size);
+ if (dir->_ep == NULL) return -1;
+
+ error = readdir_r(dir->_d, dir->_ep, &dir->_e);
+ if (error != 0) return -1;
+#endif
+ if (dir->_e == NULL)
+ {
+ dir->has_next = 0;
+ }
+#endif
+
+ return 0;
+
+bail:
+ tinydir_close(dir);
+ return -1;
+}
+
+_TINYDIR_FUNC
+int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path)
+{
+ /* Count the number of files first, to pre-allocate the files array */
+ size_t n_files = 0;
+ if (tinydir_open(dir, path) == -1)
+ {
+ return -1;
+ }
+ while (dir->has_next)
+ {
+ n_files++;
+ if (tinydir_next(dir) == -1)
+ {
+ goto bail;
+ }
+ }
+ tinydir_close(dir);
+
+ if (tinydir_open(dir, path) == -1)
+ {
+ return -1;
+ }
+
+ dir->n_files = 0;
+ dir->_files = (tinydir_file *)_TINYDIR_MALLOC(sizeof *dir->_files * n_files);
+ if (dir->_files == NULL)
+ {
+ goto bail;
+ }
+ while (dir->has_next)
+ {
+ tinydir_file *p_file;
+ dir->n_files++;
+
+ p_file = &dir->_files[dir->n_files - 1];
+ if (tinydir_readfile(dir, p_file) == -1)
+ {
+ goto bail;
+ }
+
+ if (tinydir_next(dir) == -1)
+ {
+ goto bail;
+ }
+
+ /* Just in case the number of files has changed between the first and
+ second reads, terminate without writing into unallocated memory */
+ if (dir->n_files == n_files)
+ {
+ break;
+ }
+ }
+
+ qsort(dir->_files, dir->n_files, sizeof(tinydir_file), _tinydir_file_cmp);
+
+ return 0;
+
+bail:
+ tinydir_close(dir);
+ return -1;
+}
+
+_TINYDIR_FUNC
+void tinydir_close(tinydir_dir *dir)
+{
+ if (dir == NULL)
+ {
+ return;
+ }
+
+ memset(dir->path, 0, sizeof(dir->path));
+ dir->has_next = 0;
+ dir->n_files = 0;
+ _TINYDIR_FREE(dir->_files);
+ dir->_files = NULL;
+#ifdef _MSC_VER
+ if (dir->_h != INVALID_HANDLE_VALUE)
+ {
+ FindClose(dir->_h);
+ }
+ dir->_h = INVALID_HANDLE_VALUE;
+#else
+ if (dir->_d)
+ {
+ _tinydir_closedir(dir->_d);
+ }
+ dir->_d = NULL;
+ dir->_e = NULL;
+#ifndef _TINYDIR_USE_READDIR
+ _TINYDIR_FREE(dir->_ep);
+ dir->_ep = NULL;
+#endif
+#endif
+}
+
+_TINYDIR_FUNC
+int tinydir_next(tinydir_dir *dir)
+{
+ if (dir == NULL)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ if (!dir->has_next)
+ {
+ errno = ENOENT;
+ return -1;
+ }
+
+#ifdef _MSC_VER
+ if (FindNextFile(dir->_h, &dir->_f) == 0)
+#else
+#ifdef _TINYDIR_USE_READDIR
+ dir->_e = _tinydir_readdir(dir->_d);
+#else
+ if (dir->_ep == NULL)
+ {
+ return -1;
+ }
+ if (readdir_r(dir->_d, dir->_ep, &dir->_e) != 0)
+ {
+ return -1;
+ }
+#endif
+ if (dir->_e == NULL)
+#endif
+ {
+ dir->has_next = 0;
+#ifdef _MSC_VER
+ if (GetLastError() != ERROR_SUCCESS &&
+ GetLastError() != ERROR_NO_MORE_FILES)
+ {
+ tinydir_close(dir);
+ errno = EIO;
+ return -1;
+ }
+#endif
+ }
+
+ return 0;
+}
+
+_TINYDIR_FUNC
+int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file)
+{
+ if (dir == NULL || file == NULL)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+#ifdef _MSC_VER
+ if (dir->_h == INVALID_HANDLE_VALUE)
+#else
+ if (dir->_e == NULL)
+#endif
+ {
+ errno = ENOENT;
+ return -1;
+ }
+ if (_tinydir_strlen(dir->path) +
+ _tinydir_strlen(
+#ifdef _MSC_VER
+ dir->_f.cFileName
+#else
+ dir->_e->d_name
+#endif
+ ) + 1 + _TINYDIR_PATH_EXTRA >=
+ _TINYDIR_PATH_MAX)
+ {
+ /* the path for the file will be too long */
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+ if (_tinydir_strlen(
+#ifdef _MSC_VER
+ dir->_f.cFileName
+#else
+ dir->_e->d_name
+#endif
+ ) >= _TINYDIR_FILENAME_MAX)
+ {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+
+ _tinydir_strcpy(file->path, dir->path);
+ _tinydir_strcat(file->path, TINYDIR_STRING("/"));
+ _tinydir_strcpy(file->name,
+#ifdef _MSC_VER
+ dir->_f.cFileName
+#else
+ dir->_e->d_name
+#endif
+ );
+ _tinydir_strcat(file->path, file->name);
+#ifndef _MSC_VER
+#ifdef __MINGW32__
+ if (_tstat(
+#else
+ if (stat(
+#endif
+ file->path, &file->_s) == -1)
+ {
+ return -1;
+ }
+#endif
+ _tinydir_get_ext(file);
+
+ file->is_dir =
+#ifdef _MSC_VER
+ !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
+#else
+ S_ISDIR(file->_s.st_mode);
+#endif
+ file->is_reg =
+#ifdef _MSC_VER
+ !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) ||
+ (
+ !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) &&
+ !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
+ !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) &&
+#ifdef FILE_ATTRIBUTE_INTEGRITY_STREAM
+ !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_INTEGRITY_STREAM) &&
+#endif
+#ifdef FILE_ATTRIBUTE_NO_SCRUB_DATA
+ !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NO_SCRUB_DATA) &&
+#endif
+ !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) &&
+ !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY));
+#else
+ S_ISREG(file->_s.st_mode);
+#endif
+
+ return 0;
+}
+
+_TINYDIR_FUNC
+int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i)
+{
+ if (dir == NULL || file == NULL)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ if (i >= dir->n_files)
+ {
+ errno = ENOENT;
+ return -1;
+ }
+
+ memcpy(file, &dir->_files[i], sizeof(tinydir_file));
+ _tinydir_get_ext(file);
+
+ return 0;
+}
+
+_TINYDIR_FUNC
+int tinydir_open_subdir_n(tinydir_dir *dir, size_t i)
+{
+ _tinydir_char_t path[_TINYDIR_PATH_MAX];
+ if (dir == NULL)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ if (i >= dir->n_files || !dir->_files[i].is_dir)
+ {
+ errno = ENOENT;
+ return -1;
+ }
+
+ _tinydir_strcpy(path, dir->_files[i].path);
+ tinydir_close(dir);
+ if (tinydir_open_sorted(dir, path) == -1)
+ {
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Open a single file given its path */
+_TINYDIR_FUNC
+int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path)
+{
+ tinydir_dir dir;
+ int result = 0;
+ int found = 0;
+ _tinydir_char_t dir_name_buf[_TINYDIR_PATH_MAX];
+ _tinydir_char_t file_name_buf[_TINYDIR_FILENAME_MAX];
+ _tinydir_char_t *dir_name;
+ _tinydir_char_t *base_name;
+#if (defined _MSC_VER || defined __MINGW32__)
+ _tinydir_char_t drive_buf[_TINYDIR_PATH_MAX];
+ _tinydir_char_t ext_buf[_TINYDIR_FILENAME_MAX];
+#endif
+
+ if (file == NULL || path == NULL || _tinydir_strlen(path) == 0)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX)
+ {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+
+ /* Get the parent path */
+#if (defined _MSC_VER || defined __MINGW32__)
+#if ((defined _MSC_VER) && (_MSC_VER >= 1400))
+ _tsplitpath_s(
+ path,
+ drive_buf, _TINYDIR_DRIVE_MAX,
+ dir_name_buf, _TINYDIR_FILENAME_MAX,
+ file_name_buf, _TINYDIR_FILENAME_MAX,
+ ext_buf, _TINYDIR_FILENAME_MAX);
+#else
+ _tsplitpath(
+ path,
+ drive_buf,
+ dir_name_buf,
+ file_name_buf,
+ ext_buf);
+#endif
+
+/* _splitpath_s not work fine with only filename and widechar support */
+#ifdef _UNICODE
+ if (drive_buf[0] == L'\xFEFE')
+ drive_buf[0] = '\0';
+ if (dir_name_buf[0] == L'\xFEFE')
+ dir_name_buf[0] = '\0';
+#endif
+
+ if (errno)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ /* Emulate the behavior of dirname by returning "." for dir name if it's
+ empty */
+ if (drive_buf[0] == '\0' && dir_name_buf[0] == '\0')
+ {
+ _tinydir_strcpy(dir_name_buf, TINYDIR_STRING("."));
+ }
+ /* Concatenate the drive letter and dir name to form full dir name */
+ _tinydir_strcat(drive_buf, dir_name_buf);
+ dir_name = drive_buf;
+ /* Concatenate the file name and extension to form base name */
+ _tinydir_strcat(file_name_buf, ext_buf);
+ base_name = file_name_buf;
+#else
+ _tinydir_strcpy(dir_name_buf, path);
+ dir_name = dirname(dir_name_buf);
+ _tinydir_strcpy(file_name_buf, path);
+ base_name =basename(file_name_buf);
+#endif
+
+ /* Open the parent directory */
+ if (tinydir_open(&dir, dir_name) == -1)
+ {
+ return -1;
+ }
+
+ /* Read through the parent directory and look for the file */
+ while (dir.has_next)
+ {
+ if (tinydir_readfile(&dir, file) == -1)
+ {
+ result = -1;
+ goto bail;
+ }
+ if (_tinydir_strcmp(file->name, base_name) == 0)
+ {
+ /* File found */
+ found = 1;
+ break;
+ }
+ tinydir_next(&dir);
+ }
+ if (!found)
+ {
+ result = -1;
+ errno = ENOENT;
+ }
+
+bail:
+ tinydir_close(&dir);
+ return result;
+}
+
+_TINYDIR_FUNC
+void _tinydir_get_ext(tinydir_file *file)
+{
+ _tinydir_char_t *period = _tinydir_strrchr(file->name, TINYDIR_STRING('.'));
+ if (period == NULL)
+ {
+ file->extension = &(file->name[_tinydir_strlen(file->name)]);
+ }
+ else
+ {
+ file->extension = period + 1;
+ }
+}
+
+_TINYDIR_FUNC
+int _tinydir_file_cmp(const void *a, const void *b)
+{
+ const tinydir_file *fa = (const tinydir_file *)a;
+ const tinydir_file *fb = (const tinydir_file *)b;
+ if (fa->is_dir != fb->is_dir)
+ {
+ return -(fa->is_dir - fb->is_dir);
+ }
+ return _tinydir_strncmp(fa->name, fb->name, _TINYDIR_FILENAME_MAX);
+}
+
+#ifndef _MSC_VER
+#ifndef _TINYDIR_USE_READDIR
+/*
+The following authored by Ben Hutchings
+from https://womble.decadent.org.uk/readdir_r-advisory.html
+*/
+/* Calculate the required buffer size (in bytes) for directory *
+* entries read from the given directory handle. Return -1 if this *
+* this cannot be done. *
+* *
+* This code does not trust values of NAME_MAX that are less than *
+* 255, since some systems (including at least HP-UX) incorrectly *
+* define it to be a smaller value. */
+_TINYDIR_FUNC
+size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp)
+{
+ long name_max;
+ size_t name_end;
+ /* parameter may be unused */
+ (void)dirp;
+
+#if defined _TINYDIR_USE_FPATHCONF
+ name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX);
+ if (name_max == -1)
+#if defined(NAME_MAX)
+ name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
+#else
+ return (size_t)(-1);
+#endif
+#elif defined(NAME_MAX)
+ name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
+#else
+#error "buffer size for readdir_r cannot be determined"
+#endif
+ name_end = (size_t)offsetof(struct _tinydir_dirent, d_name) + name_max + 1;
+ return (name_end > sizeof(struct _tinydir_dirent) ?
+ name_end : sizeof(struct _tinydir_dirent));
+}
+#endif
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+# if defined (_MSC_VER)
+# pragma warning(pop)
+# endif
+
+#endif
diff --git a/resources/ethernet/LwIP/apps/lwiperf/lwiperf.c b/resources/ethernet/LwIP/apps/lwiperf/lwiperf.c
new file mode 100644
index 00000000..ec2c9bb9
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/lwiperf/lwiperf.c
@@ -0,0 +1,841 @@
+/**
+ * @file
+ * lwIP iPerf server implementation
+ */
+
+/**
+ * @defgroup iperf Iperf server
+ * @ingroup apps
+ *
+ * This is a simple performance measuring client/server to check your bandwith using
+ * iPerf2 on a PC as server/client.
+ * It is currently a minimal implementation providing a TCP client/server only.
+ *
+ * @todo:
+ * - implement UDP mode
+ * - protect combined sessions handling (via 'related_master_state') against reallocation
+ * (this is a pointer address, currently, so if the same memory is allocated again,
+ * session pairs (tx/rx) can be confused on reallocation)
+ */
+
+/*
+ * Copyright (c) 2014 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ */
+
+#include "lwip/apps/lwiperf.h"
+
+#include "lwip/tcp.h"
+#include "lwip/sys.h"
+
+#include
+
+/* Currently, only TCP is implemented */
+#if LWIP_TCP && LWIP_CALLBACK_API
+
+/** Specify the idle timeout (in seconds) after that the test fails */
+#ifndef LWIPERF_TCP_MAX_IDLE_SEC
+#define LWIPERF_TCP_MAX_IDLE_SEC 10U
+#endif
+#if LWIPERF_TCP_MAX_IDLE_SEC > 255
+#error LWIPERF_TCP_MAX_IDLE_SEC must fit into an u8_t
+#endif
+
+/** Change this if you don't want to lwiperf to listen to any IP version */
+#ifndef LWIPERF_SERVER_IP_TYPE
+#define LWIPERF_SERVER_IP_TYPE IPADDR_TYPE_ANY
+#endif
+
+/* File internal memory allocation (struct lwiperf_*): this defaults to
+ the heap */
+#ifndef LWIPERF_ALLOC
+#define LWIPERF_ALLOC(type) mem_malloc(sizeof(type))
+#define LWIPERF_FREE(type, item) mem_free(item)
+#endif
+
+/** If this is 1, check that received data has the correct format */
+#ifndef LWIPERF_CHECK_RX_DATA
+#define LWIPERF_CHECK_RX_DATA 0
+#endif
+
+/** This is the Iperf settings struct sent from the client */
+typedef struct _lwiperf_settings {
+#define LWIPERF_FLAGS_ANSWER_TEST 0x80000000
+#define LWIPERF_FLAGS_ANSWER_NOW 0x00000001
+ u32_t flags;
+ u32_t num_threads; /* unused for now */
+ u32_t remote_port;
+ u32_t buffer_len; /* unused for now */
+ u32_t win_band; /* TCP window / UDP rate: unused for now */
+ u32_t amount; /* pos. value: bytes?; neg. values: time (unit is 10ms: 1/100 second) */
+} lwiperf_settings_t;
+
+/** Basic connection handle */
+struct _lwiperf_state_base;
+typedef struct _lwiperf_state_base lwiperf_state_base_t;
+struct _lwiperf_state_base {
+ /* linked list */
+ lwiperf_state_base_t *next;
+ /* 1=tcp, 0=udp */
+ u8_t tcp;
+ /* 1=server, 0=client */
+ u8_t server;
+ /* master state used to abort sessions (e.g. listener, main client) */
+ lwiperf_state_base_t *related_master_state;
+};
+
+/** Connection handle for a TCP iperf session */
+typedef struct _lwiperf_state_tcp {
+ lwiperf_state_base_t base;
+ struct tcp_pcb *server_pcb;
+ struct tcp_pcb *conn_pcb;
+ u32_t time_started;
+ lwiperf_report_fn report_fn;
+ void *report_arg;
+ u8_t poll_count;
+ u8_t next_num;
+ /* 1=start server when client is closed */
+ u8_t client_tradeoff_mode;
+ u32_t bytes_transferred;
+ lwiperf_settings_t settings;
+ u8_t have_settings_buf;
+ u8_t specific_remote;
+ ip_addr_t remote_addr;
+} lwiperf_state_tcp_t;
+
+/** List of active iperf sessions */
+static lwiperf_state_base_t *lwiperf_all_connections;
+/** A const buffer to send from: we want to measure sending, not copying! */
+static const u8_t lwiperf_txbuf_const[1600] = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+};
+
+static err_t lwiperf_tcp_poll(void *arg, struct tcp_pcb *tpcb);
+static void lwiperf_tcp_err(void *arg, err_t err);
+static err_t lwiperf_start_tcp_server_impl(const ip_addr_t *local_addr, u16_t local_port,
+ lwiperf_report_fn report_fn, void *report_arg,
+ lwiperf_state_base_t *related_master_state, lwiperf_state_tcp_t **state);
+
+
+/** Add an iperf session to the 'active' list */
+static void
+lwiperf_list_add(lwiperf_state_base_t *item)
+{
+ item->next = lwiperf_all_connections;
+ lwiperf_all_connections = item;
+}
+
+/** Remove an iperf session from the 'active' list */
+static void
+lwiperf_list_remove(lwiperf_state_base_t *item)
+{
+ lwiperf_state_base_t *prev = NULL;
+ lwiperf_state_base_t *iter;
+ for (iter = lwiperf_all_connections; iter != NULL; prev = iter, iter = iter->next) {
+ if (iter == item) {
+ if (prev == NULL) {
+ lwiperf_all_connections = iter->next;
+ } else {
+ prev->next = iter->next;
+ }
+ /* @debug: ensure this item is listed only once */
+ for (iter = iter->next; iter != NULL; iter = iter->next) {
+ LWIP_ASSERT("duplicate entry", iter != item);
+ }
+ break;
+ }
+ }
+}
+
+static lwiperf_state_base_t *
+lwiperf_list_find(lwiperf_state_base_t *item)
+{
+ lwiperf_state_base_t *iter;
+ for (iter = lwiperf_all_connections; iter != NULL; iter = iter->next) {
+ if (iter == item) {
+ return item;
+ }
+ }
+ return NULL;
+}
+
+/** Call the report function of an iperf tcp session */
+static void
+lwip_tcp_conn_report(lwiperf_state_tcp_t *conn, enum lwiperf_report_type report_type)
+{
+ if ((conn != NULL) && (conn->report_fn != NULL)) {
+ u32_t now, duration_ms, bandwidth_kbitpsec;
+ now = sys_now();
+ duration_ms = now - conn->time_started;
+ if (duration_ms == 0) {
+ bandwidth_kbitpsec = 0;
+ } else {
+ bandwidth_kbitpsec = (conn->bytes_transferred / duration_ms) * 8U;
+ }
+ conn->report_fn(conn->report_arg, report_type,
+ &conn->conn_pcb->local_ip, conn->conn_pcb->local_port,
+ &conn->conn_pcb->remote_ip, conn->conn_pcb->remote_port,
+ conn->bytes_transferred, duration_ms, bandwidth_kbitpsec);
+ }
+}
+
+/** Close an iperf tcp session */
+static void
+lwiperf_tcp_close(lwiperf_state_tcp_t *conn, enum lwiperf_report_type report_type)
+{
+ err_t err;
+
+ lwiperf_list_remove(&conn->base);
+ lwip_tcp_conn_report(conn, report_type);
+ if (conn->conn_pcb != NULL) {
+ tcp_arg(conn->conn_pcb, NULL);
+ tcp_poll(conn->conn_pcb, NULL, 0);
+ tcp_sent(conn->conn_pcb, NULL);
+ tcp_recv(conn->conn_pcb, NULL);
+ tcp_err(conn->conn_pcb, NULL);
+ err = tcp_close(conn->conn_pcb);
+ if (err != ERR_OK) {
+ /* don't want to wait for free memory here... */
+ tcp_abort(conn->conn_pcb);
+ }
+ } else {
+ /* no conn pcb, this is the listener pcb */
+ err = tcp_close(conn->server_pcb);
+ LWIP_ASSERT("error", err == ERR_OK);
+ }
+ LWIPERF_FREE(lwiperf_state_tcp_t, conn);
+}
+
+/** Try to send more data on an iperf tcp session */
+static err_t
+lwiperf_tcp_client_send_more(lwiperf_state_tcp_t *conn)
+{
+ int send_more;
+ err_t err;
+ u16_t txlen;
+ u16_t txlen_max;
+ void *txptr;
+ u8_t apiflags;
+
+ LWIP_ASSERT("conn invalid", (conn != NULL) && conn->base.tcp && (conn->base.server == 0));
+
+ do {
+ send_more = 0;
+ if (conn->settings.amount & PP_HTONL(0x80000000)) {
+ /* this session is time-limited */
+ u32_t now = sys_now();
+ u32_t diff_ms = now - conn->time_started;
+ u32_t time = (u32_t) - (s32_t)lwip_htonl(conn->settings.amount);
+ u32_t time_ms = time * 10;
+ if (diff_ms >= time_ms) {
+ /* time specified by the client is over -> close the connection */
+ lwiperf_tcp_close(conn, LWIPERF_TCP_DONE_CLIENT);
+ return ERR_OK;
+ }
+ } else {
+ /* this session is byte-limited */
+ u32_t amount_bytes = lwip_htonl(conn->settings.amount);
+ /* @todo: this can send up to 1*MSS more than requested... */
+ if (amount_bytes >= conn->bytes_transferred) {
+ /* all requested bytes transferred -> close the connection */
+ lwiperf_tcp_close(conn, LWIPERF_TCP_DONE_CLIENT);
+ return ERR_OK;
+ }
+ }
+
+ if (conn->bytes_transferred < 24) {
+ /* transmit the settings a first time */
+ txptr = &((u8_t *)&conn->settings)[conn->bytes_transferred];
+ txlen_max = (u16_t)(24 - conn->bytes_transferred);
+ apiflags = TCP_WRITE_FLAG_COPY;
+ } else if (conn->bytes_transferred < 48) {
+ /* transmit the settings a second time */
+ txptr = &((u8_t *)&conn->settings)[conn->bytes_transferred - 24];
+ txlen_max = (u16_t)(48 - conn->bytes_transferred);
+ apiflags = TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE;
+ send_more = 1;
+ } else {
+ /* transmit data */
+ /* @todo: every x bytes, transmit the settings again */
+ txptr = LWIP_CONST_CAST(void *, &lwiperf_txbuf_const[conn->bytes_transferred % 10]);
+ txlen_max = TCP_MSS;
+ if (conn->bytes_transferred == 48) { /* @todo: fix this for intermediate settings, too */
+ txlen_max = TCP_MSS - 24;
+ }
+ apiflags = 0; /* no copying needed */
+ send_more = 1;
+ }
+ txlen = txlen_max;
+ do {
+ err = tcp_write(conn->conn_pcb, txptr, txlen, apiflags);
+ if (err == ERR_MEM) {
+ txlen /= 2;
+ }
+ } while ((err == ERR_MEM) && (txlen >= (TCP_MSS / 2)));
+
+ if (err == ERR_OK) {
+ conn->bytes_transferred += txlen;
+ } else {
+ send_more = 0;
+ }
+ } while (send_more);
+
+ tcp_output(conn->conn_pcb);
+ return ERR_OK;
+}
+
+/** TCP sent callback, try to send more data */
+static err_t
+lwiperf_tcp_client_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
+{
+ lwiperf_state_tcp_t *conn = (lwiperf_state_tcp_t *)arg;
+ /* @todo: check 'len' (e.g. to time ACK of all data)? for now, we just send more... */
+ LWIP_ASSERT("invalid conn", conn->conn_pcb == tpcb);
+ LWIP_UNUSED_ARG(tpcb);
+ LWIP_UNUSED_ARG(len);
+
+ conn->poll_count = 0;
+
+ return lwiperf_tcp_client_send_more(conn);
+}
+
+/** TCP connected callback (active connection), send data now */
+static err_t
+lwiperf_tcp_client_connected(void *arg, struct tcp_pcb *tpcb, err_t err)
+{
+ lwiperf_state_tcp_t *conn = (lwiperf_state_tcp_t *)arg;
+ LWIP_ASSERT("invalid conn", conn->conn_pcb == tpcb);
+ LWIP_UNUSED_ARG(tpcb);
+ if (err != ERR_OK) {
+ lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_REMOTE);
+ return ERR_OK;
+ }
+ conn->poll_count = 0;
+ conn->time_started = sys_now();
+ return lwiperf_tcp_client_send_more(conn);
+}
+
+/** Start TCP connection back to the client (either parallel or after the
+ * receive test has finished.
+ */
+static err_t
+lwiperf_tx_start_impl(const ip_addr_t *remote_ip, u16_t remote_port, lwiperf_settings_t *settings, lwiperf_report_fn report_fn,
+ void *report_arg, lwiperf_state_base_t *related_master_state, lwiperf_state_tcp_t **new_conn)
+{
+ err_t err;
+ lwiperf_state_tcp_t *client_conn;
+ struct tcp_pcb *newpcb;
+ ip_addr_t remote_addr;
+
+ LWIP_ASSERT("remote_ip != NULL", remote_ip != NULL);
+ LWIP_ASSERT("remote_ip != NULL", settings != NULL);
+ LWIP_ASSERT("new_conn != NULL", new_conn != NULL);
+ *new_conn = NULL;
+
+ client_conn = (lwiperf_state_tcp_t *)LWIPERF_ALLOC(lwiperf_state_tcp_t);
+ if (client_conn == NULL) {
+ return ERR_MEM;
+ }
+ newpcb = tcp_new_ip_type(IP_GET_TYPE(remote_ip));
+ if (newpcb == NULL) {
+ LWIPERF_FREE(lwiperf_state_tcp_t, client_conn);
+ return ERR_MEM;
+ }
+ memset(client_conn, 0, sizeof(lwiperf_state_tcp_t));
+ client_conn->base.tcp = 1;
+ client_conn->base.related_master_state = related_master_state;
+ client_conn->conn_pcb = newpcb;
+ client_conn->time_started = sys_now(); /* @todo: set this again on 'connected' */
+ client_conn->report_fn = report_fn;
+ client_conn->report_arg = report_arg;
+ client_conn->next_num = 4; /* initial nr is '4' since the header has 24 byte */
+ client_conn->bytes_transferred = 0;
+ memcpy(&client_conn->settings, settings, sizeof(*settings));
+ client_conn->have_settings_buf = 1;
+
+ tcp_arg(newpcb, client_conn);
+ tcp_sent(newpcb, lwiperf_tcp_client_sent);
+ tcp_poll(newpcb, lwiperf_tcp_poll, 2U);
+ tcp_err(newpcb, lwiperf_tcp_err);
+
+ ip_addr_copy(remote_addr, *remote_ip);
+
+ err = tcp_connect(newpcb, &remote_addr, remote_port, lwiperf_tcp_client_connected);
+ if (err != ERR_OK) {
+ lwiperf_tcp_close(client_conn, LWIPERF_TCP_ABORTED_LOCAL);
+ return err;
+ }
+ lwiperf_list_add(&client_conn->base);
+ *new_conn = client_conn;
+ return ERR_OK;
+}
+
+static err_t
+lwiperf_tx_start_passive(lwiperf_state_tcp_t *conn)
+{
+ err_t ret;
+ lwiperf_state_tcp_t *new_conn = NULL;
+ u16_t remote_port = (u16_t)lwip_htonl(conn->settings.remote_port);
+
+ ret = lwiperf_tx_start_impl(&conn->conn_pcb->remote_ip, remote_port, &conn->settings, conn->report_fn, conn->report_arg,
+ conn->base.related_master_state, &new_conn);
+ if (ret == ERR_OK) {
+ LWIP_ASSERT("new_conn != NULL", new_conn != NULL);
+ new_conn->settings.flags = 0; /* prevent the remote side starting back as client again */
+ }
+ return ret;
+}
+
+/** Receive data on an iperf tcp session */
+static err_t
+lwiperf_tcp_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
+{
+ u8_t tmp;
+ u16_t tot_len;
+ u32_t packet_idx;
+ struct pbuf *q;
+ lwiperf_state_tcp_t *conn = (lwiperf_state_tcp_t *)arg;
+
+ LWIP_ASSERT("pcb mismatch", conn->conn_pcb == tpcb);
+ LWIP_UNUSED_ARG(tpcb);
+
+ if (err != ERR_OK) {
+ lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_REMOTE);
+ return ERR_OK;
+ }
+ if (p == NULL) {
+ /* connection closed -> test done */
+ if (conn->settings.flags & PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST)) {
+ if ((conn->settings.flags & PP_HTONL(LWIPERF_FLAGS_ANSWER_NOW)) == 0) {
+ /* client requested transmission after end of test */
+ lwiperf_tx_start_passive(conn);
+ }
+ }
+ lwiperf_tcp_close(conn, LWIPERF_TCP_DONE_SERVER);
+ return ERR_OK;
+ }
+ tot_len = p->tot_len;
+
+ conn->poll_count = 0;
+
+ if ((!conn->have_settings_buf) || ((conn->bytes_transferred - 24) % (1024 * 128) == 0)) {
+ /* wait for 24-byte header */
+ if (p->tot_len < sizeof(lwiperf_settings_t)) {
+ lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_DATAERROR);
+ pbuf_free(p);
+ return ERR_OK;
+ }
+ if (!conn->have_settings_buf) {
+ if (pbuf_copy_partial(p, &conn->settings, sizeof(lwiperf_settings_t), 0) != sizeof(lwiperf_settings_t)) {
+ lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL);
+ pbuf_free(p);
+ return ERR_OK;
+ }
+ conn->have_settings_buf = 1;
+ if (conn->settings.flags & PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST)) {
+ if (conn->settings.flags & PP_HTONL(LWIPERF_FLAGS_ANSWER_NOW)) {
+ /* client requested parallel transmission test */
+ err_t err2 = lwiperf_tx_start_passive(conn);
+ if (err2 != ERR_OK) {
+ lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_TXERROR);
+ pbuf_free(p);
+ return ERR_OK;
+ }
+ }
+ }
+ } else {
+ if (conn->settings.flags & PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST)) {
+ if (pbuf_memcmp(p, 0, &conn->settings, sizeof(lwiperf_settings_t)) != 0) {
+ lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_DATAERROR);
+ pbuf_free(p);
+ return ERR_OK;
+ }
+ }
+ }
+ conn->bytes_transferred += sizeof(lwiperf_settings_t);
+ if (conn->bytes_transferred <= 24) {
+ conn->time_started = sys_now();
+ tcp_recved(tpcb, p->tot_len);
+ pbuf_free(p);
+ return ERR_OK;
+ }
+ conn->next_num = 4; /* 24 bytes received... */
+ tmp = pbuf_remove_header(p, 24);
+ LWIP_ASSERT("pbuf_remove_header failed", tmp == 0);
+ LWIP_UNUSED_ARG(tmp); /* for LWIP_NOASSERT */
+ }
+
+ packet_idx = 0;
+ for (q = p; q != NULL; q = q->next) {
+#if LWIPERF_CHECK_RX_DATA
+ const u8_t *payload = (const u8_t *)q->payload;
+ u16_t i;
+ for (i = 0; i < q->len; i++) {
+ u8_t val = payload[i];
+ u8_t num = val - '0';
+ if (num == conn->next_num) {
+ conn->next_num++;
+ if (conn->next_num == 10) {
+ conn->next_num = 0;
+ }
+ } else {
+ lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_DATAERROR);
+ pbuf_free(p);
+ return ERR_OK;
+ }
+ }
+#endif
+ packet_idx += q->len;
+ }
+ LWIP_ASSERT("count mismatch", packet_idx == p->tot_len);
+ conn->bytes_transferred += packet_idx;
+ tcp_recved(tpcb, tot_len);
+ pbuf_free(p);
+ return ERR_OK;
+}
+
+/** Error callback, iperf tcp session aborted */
+static void
+lwiperf_tcp_err(void *arg, err_t err)
+{
+ lwiperf_state_tcp_t *conn = (lwiperf_state_tcp_t *)arg;
+ LWIP_UNUSED_ARG(err);
+ lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_REMOTE);
+}
+
+/** TCP poll callback, try to send more data */
+static err_t
+lwiperf_tcp_poll(void *arg, struct tcp_pcb *tpcb)
+{
+ lwiperf_state_tcp_t *conn = (lwiperf_state_tcp_t *)arg;
+ LWIP_ASSERT("pcb mismatch", conn->conn_pcb == tpcb);
+ LWIP_UNUSED_ARG(tpcb);
+ if (++conn->poll_count >= LWIPERF_TCP_MAX_IDLE_SEC) {
+ lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL);
+ return ERR_OK; /* lwiperf_tcp_close frees conn */
+ }
+
+ if (!conn->base.server) {
+ lwiperf_tcp_client_send_more(conn);
+ }
+
+ return ERR_OK;
+}
+
+/** This is called when a new client connects for an iperf tcp session */
+static err_t
+lwiperf_tcp_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
+{
+ lwiperf_state_tcp_t *s, *conn;
+ if ((err != ERR_OK) || (newpcb == NULL) || (arg == NULL)) {
+ return ERR_VAL;
+ }
+
+ s = (lwiperf_state_tcp_t *)arg;
+ LWIP_ASSERT("invalid session", s->base.server);
+ LWIP_ASSERT("invalid listen pcb", s->server_pcb != NULL);
+ LWIP_ASSERT("invalid conn pcb", s->conn_pcb == NULL);
+ if (s->specific_remote) {
+ LWIP_ASSERT("s->base.related_master_state != NULL", s->base.related_master_state != NULL);
+ if (!ip_addr_cmp(&newpcb->remote_ip, &s->remote_addr)) {
+ /* this listener belongs to a client session, and this is not the correct remote */
+ return ERR_VAL;
+ }
+ } else {
+ LWIP_ASSERT("s->base.related_master_state == NULL", s->base.related_master_state == NULL);
+ }
+
+ conn = (lwiperf_state_tcp_t *)LWIPERF_ALLOC(lwiperf_state_tcp_t);
+ if (conn == NULL) {
+ return ERR_MEM;
+ }
+ memset(conn, 0, sizeof(lwiperf_state_tcp_t));
+ conn->base.tcp = 1;
+ conn->base.server = 1;
+ conn->base.related_master_state = &s->base;
+ conn->conn_pcb = newpcb;
+ conn->time_started = sys_now();
+ conn->report_fn = s->report_fn;
+ conn->report_arg = s->report_arg;
+
+ /* setup the tcp rx connection */
+ tcp_arg(newpcb, conn);
+ tcp_recv(newpcb, lwiperf_tcp_recv);
+ tcp_poll(newpcb, lwiperf_tcp_poll, 2U);
+ tcp_err(conn->conn_pcb, lwiperf_tcp_err);
+
+ if (s->specific_remote) {
+ /* this listener belongs to a client, so make the client the master of the newly created connection */
+ conn->base.related_master_state = s->base.related_master_state;
+ /* if dual mode or (tradeoff mode AND client is done): close the listener */
+ if (!s->client_tradeoff_mode || !lwiperf_list_find(s->base.related_master_state)) {
+ /* prevent report when closing: this is expected */
+ s->report_fn = NULL;
+ lwiperf_tcp_close(s, LWIPERF_TCP_ABORTED_LOCAL);
+ }
+ }
+ lwiperf_list_add(&conn->base);
+ return ERR_OK;
+}
+
+/**
+ * @ingroup iperf
+ * Start a TCP iperf server on the default TCP port (5001) and listen for
+ * incoming connections from iperf clients.
+ *
+ * @returns a connection handle that can be used to abort the server
+ * by calling @ref lwiperf_abort()
+ */
+void *
+lwiperf_start_tcp_server_default(lwiperf_report_fn report_fn, void *report_arg)
+{
+ return lwiperf_start_tcp_server(IP_ADDR_ANY, LWIPERF_TCP_PORT_DEFAULT,
+ report_fn, report_arg);
+}
+
+/**
+ * @ingroup iperf
+ * Start a TCP iperf server on a specific IP address and port and listen for
+ * incoming connections from iperf clients.
+ *
+ * @returns a connection handle that can be used to abort the server
+ * by calling @ref lwiperf_abort()
+ */
+void *
+lwiperf_start_tcp_server(const ip_addr_t *local_addr, u16_t local_port,
+ lwiperf_report_fn report_fn, void *report_arg)
+{
+ err_t err;
+ lwiperf_state_tcp_t *state = NULL;
+
+ err = lwiperf_start_tcp_server_impl(local_addr, local_port, report_fn, report_arg,
+ NULL, &state);
+ if (err == ERR_OK) {
+ return state;
+ }
+ return NULL;
+}
+
+static err_t lwiperf_start_tcp_server_impl(const ip_addr_t *local_addr, u16_t local_port,
+ lwiperf_report_fn report_fn, void *report_arg,
+ lwiperf_state_base_t *related_master_state, lwiperf_state_tcp_t **state)
+{
+ err_t err;
+ struct tcp_pcb *pcb;
+ lwiperf_state_tcp_t *s;
+
+ LWIP_ASSERT_CORE_LOCKED();
+
+ LWIP_ASSERT("state != NULL", state != NULL);
+
+ if (local_addr == NULL) {
+ return ERR_ARG;
+ }
+
+ s = (lwiperf_state_tcp_t *)LWIPERF_ALLOC(lwiperf_state_tcp_t);
+ if (s == NULL) {
+ return ERR_MEM;
+ }
+ memset(s, 0, sizeof(lwiperf_state_tcp_t));
+ s->base.tcp = 1;
+ s->base.server = 1;
+ s->base.related_master_state = related_master_state;
+ s->report_fn = report_fn;
+ s->report_arg = report_arg;
+
+ pcb = tcp_new_ip_type(LWIPERF_SERVER_IP_TYPE);
+ if (pcb == NULL) {
+ return ERR_MEM;
+ }
+ err = tcp_bind(pcb, local_addr, local_port);
+ if (err != ERR_OK) {
+ return err;
+ }
+ s->server_pcb = tcp_listen_with_backlog(pcb, 1);
+ if (s->server_pcb == NULL) {
+ if (pcb != NULL) {
+ tcp_close(pcb);
+ }
+ LWIPERF_FREE(lwiperf_state_tcp_t, s);
+ return ERR_MEM;
+ }
+ pcb = NULL;
+
+ tcp_arg(s->server_pcb, s);
+ tcp_accept(s->server_pcb, lwiperf_tcp_accept);
+
+ lwiperf_list_add(&s->base);
+ *state = s;
+ return ERR_OK;
+}
+
+/**
+ * @ingroup iperf
+ * Start a TCP iperf client to the default TCP port (5001).
+ *
+ * @returns a connection handle that can be used to abort the client
+ * by calling @ref lwiperf_abort()
+ */
+void* lwiperf_start_tcp_client_default(const ip_addr_t* remote_addr,
+ lwiperf_report_fn report_fn, void* report_arg)
+{
+ return lwiperf_start_tcp_client(remote_addr, LWIPERF_TCP_PORT_DEFAULT, LWIPERF_CLIENT,
+ report_fn, report_arg);
+}
+
+/**
+ * @ingroup iperf
+ * Start a TCP iperf client to a specific IP address and port.
+ *
+ * @returns a connection handle that can be used to abort the client
+ * by calling @ref lwiperf_abort()
+ */
+void* lwiperf_start_tcp_client(const ip_addr_t* remote_addr, u16_t remote_port,
+ enum lwiperf_client_type type, lwiperf_report_fn report_fn, void* report_arg)
+{
+ err_t ret;
+ lwiperf_settings_t settings;
+ lwiperf_state_tcp_t *state = NULL;
+
+ memset(&settings, 0, sizeof(settings));
+ switch (type) {
+ case LWIPERF_CLIENT:
+ /* Unidirectional tx only test */
+ settings.flags = 0;
+ break;
+ case LWIPERF_DUAL:
+ /* Do a bidirectional test simultaneously */
+ settings.flags = htonl(LWIPERF_FLAGS_ANSWER_TEST | LWIPERF_FLAGS_ANSWER_NOW);
+ break;
+ case LWIPERF_TRADEOFF:
+ /* Do a bidirectional test individually */
+ settings.flags = htonl(LWIPERF_FLAGS_ANSWER_TEST);
+ break;
+ default:
+ /* invalid argument */
+ return NULL;
+ }
+ settings.num_threads = htonl(1);
+ settings.remote_port = htonl(LWIPERF_TCP_PORT_DEFAULT);
+ /* TODO: implement passing duration/amount of bytes to transfer */
+ settings.amount = htonl((u32_t)-1000);
+
+ ret = lwiperf_tx_start_impl(remote_addr, remote_port, &settings, report_fn, report_arg, NULL, &state);
+ if (ret == ERR_OK) {
+ LWIP_ASSERT("state != NULL", state != NULL);
+ if (type != LWIPERF_CLIENT) {
+ /* start corresponding server now */
+ lwiperf_state_tcp_t *server = NULL;
+ ret = lwiperf_start_tcp_server_impl(&state->conn_pcb->local_ip, LWIPERF_TCP_PORT_DEFAULT,
+ report_fn, report_arg, (lwiperf_state_base_t *)state, &server);
+ if (ret != ERR_OK) {
+ /* starting server failed, abort client */
+ lwiperf_abort(state);
+ return NULL;
+ }
+ /* make this server accept one connection only */
+ server->specific_remote = 1;
+ server->remote_addr = state->conn_pcb->remote_ip;
+ if (type == LWIPERF_TRADEOFF) {
+ /* tradeoff means that the remote host connects only after the client is done,
+ so keep the listen pcb open until the client is done */
+ server->client_tradeoff_mode = 1;
+ }
+ }
+ return state;
+ }
+ return NULL;
+}
+
+/**
+ * @ingroup iperf
+ * Abort an iperf session (handle returned by lwiperf_start_tcp_server*())
+ */
+void
+lwiperf_abort(void *lwiperf_session)
+{
+ lwiperf_state_base_t *i, *dealloc, *last = NULL;
+
+ LWIP_ASSERT_CORE_LOCKED();
+
+ for (i = lwiperf_all_connections; i != NULL; ) {
+ if ((i == lwiperf_session) || (i->related_master_state == lwiperf_session)) {
+ dealloc = i;
+ i = i->next;
+ if (last != NULL) {
+ last->next = i;
+ }
+ LWIPERF_FREE(lwiperf_state_tcp_t, dealloc); /* @todo: type? */
+ } else {
+ last = i;
+ i = i->next;
+ }
+ }
+}
+
+#endif /* LWIP_TCP && LWIP_CALLBACK_API */
diff --git a/resources/ethernet/LwIP/apps/mdns/mdns.c b/resources/ethernet/LwIP/apps/mdns/mdns.c
new file mode 100644
index 00000000..46314d25
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/mdns/mdns.c
@@ -0,0 +1,2407 @@
+/**
+ * @file
+ * MDNS responder implementation
+ *
+ * @defgroup mdns MDNS
+ * @ingroup apps
+ *
+ * RFC 6762 - Multicast DNS\n
+ * RFC 6763 - DNS-Based Service Discovery\n
+ *
+ * @verbinclude mdns.txt
+ *
+ * Things left to implement:
+ * -------------------------
+ *
+ * - Tiebreaking for simultaneous probing
+ * - Sending goodbye messages (zero ttl) - shutdown, DHCP lease about to expire, DHCP turned off...
+ * - Checking that source address of unicast requests are on the same network
+ * - Limiting multicast responses to 1 per second per resource record
+ * - Fragmenting replies if required
+ * - Handling multi-packet known answers
+ * - Individual known answer detection for all local IPv6 addresses
+ * - Dynamic size of outgoing packet
+ */
+
+/*
+ * Copyright (c) 2015 Verisure Innovation AB
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Erik Ekman
+ *
+ */
+
+#include "lwip/apps/mdns.h"
+#include "lwip/apps/mdns_priv.h"
+#include "lwip/netif.h"
+#include "lwip/udp.h"
+#include "lwip/ip_addr.h"
+#include "lwip/mem.h"
+#include "lwip/prot/dns.h"
+#include "lwip/prot/iana.h"
+#include "lwip/timeouts.h"
+
+#include
+
+#if LWIP_MDNS_RESPONDER
+
+#if (LWIP_IPV4 && !LWIP_IGMP)
+#error "If you want to use MDNS with IPv4, you have to define LWIP_IGMP=1 in your lwipopts.h"
+#endif
+#if (LWIP_IPV6 && !LWIP_IPV6_MLD)
+#error "If you want to use MDNS with IPv6, you have to define LWIP_IPV6_MLD=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP)
+#error "If you want to use MDNS, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+
+#if LWIP_IPV4
+#include "lwip/igmp.h"
+/* IPv4 multicast group 224.0.0.251 */
+static const ip_addr_t v4group = DNS_MQUERY_IPV4_GROUP_INIT;
+#endif
+
+#if LWIP_IPV6
+#include "lwip/mld6.h"
+/* IPv6 multicast group FF02::FB */
+static const ip_addr_t v6group = DNS_MQUERY_IPV6_GROUP_INIT;
+#endif
+
+#define MDNS_TTL 255
+
+/* Stored offsets to beginning of domain names
+ * Used for compression.
+ */
+#define NUM_DOMAIN_OFFSETS 10
+#define DOMAIN_JUMP_SIZE 2
+#define DOMAIN_JUMP 0xc000
+
+static u8_t mdns_netif_client_id;
+static struct udp_pcb *mdns_pcb;
+#if MDNS_RESP_USENETIF_EXTCALLBACK
+NETIF_DECLARE_EXT_CALLBACK(netif_callback)
+#endif
+static mdns_name_result_cb_t mdns_name_result_cb;
+
+#define NETIF_TO_HOST(netif) (struct mdns_host*)(netif_get_client_data(netif, mdns_netif_client_id))
+
+#define TOPDOMAIN_LOCAL "local"
+
+#define REVERSE_PTR_TOPDOMAIN "arpa"
+#define REVERSE_PTR_V4_DOMAIN "in-addr"
+#define REVERSE_PTR_V6_DOMAIN "ip6"
+
+#define SRV_PRIORITY 0
+#define SRV_WEIGHT 0
+
+/* Payload size allocated for each outgoing UDP packet */
+#define OUTPACKET_SIZE 500
+
+/* Lookup from hostname -> IPv4 */
+#define REPLY_HOST_A 0x01
+/* Lookup from IPv4/v6 -> hostname */
+#define REPLY_HOST_PTR_V4 0x02
+/* Lookup from hostname -> IPv6 */
+#define REPLY_HOST_AAAA 0x04
+/* Lookup from hostname -> IPv6 */
+#define REPLY_HOST_PTR_V6 0x08
+
+/* Lookup for service types */
+#define REPLY_SERVICE_TYPE_PTR 0x10
+/* Lookup for instances of service */
+#define REPLY_SERVICE_NAME_PTR 0x20
+/* Lookup for location of service instance */
+#define REPLY_SERVICE_SRV 0x40
+/* Lookup for text info on service instance */
+#define REPLY_SERVICE_TXT 0x80
+
+#define MDNS_PROBE_DELAY_MS 250
+#define MDNS_PROBE_COUNT 3
+#ifdef LWIP_RAND
+/* first probe timeout SHOULD be random 0-250 ms*/
+#define MDNS_INITIAL_PROBE_DELAY_MS (LWIP_RAND() % MDNS_PROBE_DELAY_MS)
+#else
+#define MDNS_INITIAL_PROBE_DELAY_MS MDNS_PROBE_DELAY_MS
+#endif
+
+#define MDNS_PROBING_NOT_STARTED 0
+#define MDNS_PROBING_ONGOING 1
+#define MDNS_PROBING_COMPLETE 2
+
+static const char *dnssd_protos[] = {
+ "_udp", /* DNSSD_PROTO_UDP */
+ "_tcp", /* DNSSD_PROTO_TCP */
+};
+
+/** Description of a service */
+struct mdns_service {
+ /** TXT record to answer with */
+ struct mdns_domain txtdata;
+ /** Name of service, like 'myweb' */
+ char name[MDNS_LABEL_MAXLEN + 1];
+ /** Type of service, like '_http' */
+ char service[MDNS_LABEL_MAXLEN + 1];
+ /** Callback function and userdata
+ * to update txtdata buffer */
+ service_get_txt_fn_t txt_fn;
+ void *txt_userdata;
+ /** TTL in seconds of SRV/TXT replies */
+ u32_t dns_ttl;
+ /** Protocol, TCP or UDP */
+ u16_t proto;
+ /** Port of the service */
+ u16_t port;
+};
+
+/** Description of a host/netif */
+struct mdns_host {
+ /** Hostname */
+ char name[MDNS_LABEL_MAXLEN + 1];
+ /** Pointer to services */
+ struct mdns_service *services[MDNS_MAX_SERVICES];
+ /** TTL in seconds of A/AAAA/PTR replies */
+ u32_t dns_ttl;
+ /** Number of probes sent for the current name */
+ u8_t probes_sent;
+ /** State in probing sequence */
+ u8_t probing_state;
+};
+
+/** Information about received packet */
+struct mdns_packet {
+ /** Sender IP/port */
+ ip_addr_t source_addr;
+ u16_t source_port;
+ /** If packet was received unicast */
+ u16_t recv_unicast;
+ /** Netif that received the packet */
+ struct netif *netif;
+ /** Packet data */
+ struct pbuf *pbuf;
+ /** Current parsing offset in packet */
+ u16_t parse_offset;
+ /** Identifier. Used in legacy queries */
+ u16_t tx_id;
+ /** Number of questions in packet,
+ * read from packet header */
+ u16_t questions;
+ /** Number of unparsed questions */
+ u16_t questions_left;
+ /** Number of answers in packet,
+ * (sum of normal, authoritative and additional answers)
+ * read from packet header */
+ u16_t answers;
+ /** Number of unparsed answers */
+ u16_t answers_left;
+};
+
+/** Information about outgoing packet */
+struct mdns_outpacket {
+ /** Netif to send the packet on */
+ struct netif *netif;
+ /** Packet data */
+ struct pbuf *pbuf;
+ /** Current write offset in packet */
+ u16_t write_offset;
+ /** Identifier. Used in legacy queries */
+ u16_t tx_id;
+ /** Destination IP/port if sent unicast */
+ ip_addr_t dest_addr;
+ u16_t dest_port;
+ /** Number of questions written */
+ u16_t questions;
+ /** Number of normal answers written */
+ u16_t answers;
+ /** Number of authoritative answers written */
+ u16_t authoritative;
+ /** Number of additional answers written */
+ u16_t additional;
+ /** Offsets for written domain names in packet.
+ * Used for compression */
+ u16_t domain_offsets[NUM_DOMAIN_OFFSETS];
+ /** If all answers in packet should set cache_flush bit */
+ u8_t cache_flush;
+ /** If reply should be sent unicast */
+ u8_t unicast_reply;
+ /** If legacy query. (tx_id needed, and write
+ * question again in reply before answer) */
+ u8_t legacy_query;
+ /* Reply bitmask for host information */
+ u8_t host_replies;
+ /* Bitmask for which reverse IPv6 hosts to answer */
+ u8_t host_reverse_v6_replies;
+ /* Reply bitmask per service */
+ u8_t serv_replies[MDNS_MAX_SERVICES];
+};
+
+/** Domain, type and class.
+ * Shared between questions and answers */
+struct mdns_rr_info {
+ struct mdns_domain domain;
+ u16_t type;
+ u16_t klass;
+};
+
+struct mdns_question {
+ struct mdns_rr_info info;
+ /** unicast reply requested */
+ u16_t unicast;
+};
+
+struct mdns_answer {
+ struct mdns_rr_info info;
+ /** cache flush command bit */
+ u16_t cache_flush;
+ /* Validity time in seconds */
+ u32_t ttl;
+ /** Length of variable answer */
+ u16_t rd_length;
+ /** Offset of start of variable answer in packet */
+ u16_t rd_offset;
+};
+
+static err_t mdns_send_outpacket(struct mdns_outpacket *outpkt, u8_t flags);
+static void mdns_probe(void* arg);
+
+static err_t
+mdns_domain_add_label_base(struct mdns_domain *domain, u8_t len)
+{
+ if (len > MDNS_LABEL_MAXLEN) {
+ return ERR_VAL;
+ }
+ if (len > 0 && (1 + len + domain->length >= MDNS_DOMAIN_MAXLEN)) {
+ return ERR_VAL;
+ }
+ /* Allow only zero marker on last byte */
+ if (len == 0 && (1 + domain->length > MDNS_DOMAIN_MAXLEN)) {
+ return ERR_VAL;
+ }
+ domain->name[domain->length] = len;
+ domain->length++;
+ return ERR_OK;
+}
+
+/**
+ * Add a label part to a domain
+ * @param domain The domain to add a label to
+ * @param label The label to add, like <hostname>, 'local', 'com' or ''
+ * @param len The length of the label
+ * @return ERR_OK on success, an err_t otherwise if label too long
+ */
+err_t
+mdns_domain_add_label(struct mdns_domain *domain, const char *label, u8_t len)
+{
+ err_t err = mdns_domain_add_label_base(domain, len);
+ if (err != ERR_OK) {
+ return err;
+ }
+ if (len) {
+ MEMCPY(&domain->name[domain->length], label, len);
+ domain->length += len;
+ }
+ return ERR_OK;
+}
+
+/**
+ * Add a label part to a domain (@see mdns_domain_add_label but copy directly from pbuf)
+ */
+static err_t
+mdns_domain_add_label_pbuf(struct mdns_domain *domain, const struct pbuf *p, u16_t offset, u8_t len)
+{
+ err_t err = mdns_domain_add_label_base(domain, len);
+ if (err != ERR_OK) {
+ return err;
+ }
+ if (len) {
+ if (pbuf_copy_partial(p, &domain->name[domain->length], len, offset) != len) {
+ /* take back the ++ done before */
+ domain->length--;
+ return ERR_ARG;
+ }
+ domain->length += len;
+ }
+ return ERR_OK;
+}
+
+/**
+ * Internal readname function with max 6 levels of recursion following jumps
+ * while decompressing name
+ */
+static u16_t
+mdns_readname_loop(struct pbuf *p, u16_t offset, struct mdns_domain *domain, unsigned depth)
+{
+ u8_t c;
+
+ do {
+ if (depth > 5) {
+ /* Too many jumps */
+ return MDNS_READNAME_ERROR;
+ }
+
+ c = pbuf_get_at(p, offset);
+ offset++;
+
+ /* is this a compressed label? */
+ if ((c & 0xc0) == 0xc0) {
+ u16_t jumpaddr;
+ if (offset >= p->tot_len) {
+ /* Make sure both jump bytes fit in the packet */
+ return MDNS_READNAME_ERROR;
+ }
+ jumpaddr = (((c & 0x3f) << 8) | (pbuf_get_at(p, offset) & 0xff));
+ offset++;
+ if (jumpaddr >= SIZEOF_DNS_HDR && jumpaddr < p->tot_len) {
+ u16_t res;
+ /* Recursive call, maximum depth will be checked */
+ res = mdns_readname_loop(p, jumpaddr, domain, depth + 1);
+ /* Dont return offset since new bytes were not read (jumped to somewhere in packet) */
+ if (res == MDNS_READNAME_ERROR) {
+ return res;
+ }
+ } else {
+ return MDNS_READNAME_ERROR;
+ }
+ break;
+ }
+
+ /* normal label */
+ if (c <= MDNS_LABEL_MAXLEN) {
+ err_t res;
+
+ if (c + domain->length >= MDNS_DOMAIN_MAXLEN) {
+ return MDNS_READNAME_ERROR;
+ }
+ res = mdns_domain_add_label_pbuf(domain, p, offset, c);
+ if (res != ERR_OK) {
+ return MDNS_READNAME_ERROR;
+ }
+ offset += c;
+ } else {
+ /* bad length byte */
+ return MDNS_READNAME_ERROR;
+ }
+ } while (c != 0);
+
+ return offset;
+}
+
+/**
+ * Read possibly compressed domain name from packet buffer
+ * @param p The packet
+ * @param offset start position of domain name in packet
+ * @param domain The domain name destination
+ * @return The new offset after the domain, or MDNS_READNAME_ERROR
+ * if reading failed
+ */
+u16_t
+mdns_readname(struct pbuf *p, u16_t offset, struct mdns_domain *domain)
+{
+ memset(domain, 0, sizeof(struct mdns_domain));
+ return mdns_readname_loop(p, offset, domain, 0);
+}
+
+/**
+ * Print domain name to debug output
+ * @param domain The domain name
+ */
+static void
+mdns_domain_debug_print(struct mdns_domain *domain)
+{
+ u8_t *src = domain->name;
+ u8_t i;
+
+ while (*src) {
+ u8_t label_len = *src;
+ src++;
+ for (i = 0; i < label_len; i++) {
+ LWIP_DEBUGF(MDNS_DEBUG, ("%c", src[i]));
+ }
+ src += label_len;
+ LWIP_DEBUGF(MDNS_DEBUG, ("."));
+ }
+}
+
+/**
+ * Return 1 if contents of domains match (case-insensitive)
+ * @param a Domain name to compare 1
+ * @param b Domain name to compare 2
+ * @return 1 if domains are equal ignoring case, 0 otherwise
+ */
+int
+mdns_domain_eq(struct mdns_domain *a, struct mdns_domain *b)
+{
+ u8_t *ptra, *ptrb;
+ u8_t len;
+ int res;
+
+ if (a->length != b->length) {
+ return 0;
+ }
+
+ ptra = a->name;
+ ptrb = b->name;
+ while (*ptra && *ptrb && ptra < &a->name[a->length]) {
+ if (*ptra != *ptrb) {
+ return 0;
+ }
+ len = *ptra;
+ ptra++;
+ ptrb++;
+ res = lwip_strnicmp((char *) ptra, (char *) ptrb, len);
+ if (res != 0) {
+ return 0;
+ }
+ ptra += len;
+ ptrb += len;
+ }
+ if (*ptra != *ptrb && ptra < &a->name[a->length]) {
+ return 0;
+ }
+ return 1;
+}
+
+/**
+ * Call user supplied function to setup TXT data
+ * @param service The service to build TXT record for
+ */
+static void
+mdns_prepare_txtdata(struct mdns_service *service)
+{
+ memset(&service->txtdata, 0, sizeof(struct mdns_domain));
+ if (service->txt_fn) {
+ service->txt_fn(service, service->txt_userdata);
+ }
+}
+
+#if LWIP_IPV4
+/**
+ * Build domain for reverse lookup of IPv4 address
+ * like 12.0.168.192.in-addr.arpa. for 192.168.0.12
+ * @param domain Where to write the domain name
+ * @param addr Pointer to an IPv4 address to encode
+ * @return ERR_OK if domain was written, an err_t otherwise
+ */
+static err_t
+mdns_build_reverse_v4_domain(struct mdns_domain *domain, const ip4_addr_t *addr)
+{
+ int i;
+ err_t res;
+ const u8_t *ptr;
+
+ LWIP_UNUSED_ARG(res);
+ if (!domain || !addr) {
+ return ERR_ARG;
+ }
+ memset(domain, 0, sizeof(struct mdns_domain));
+ ptr = (const u8_t *) addr;
+ for (i = sizeof(ip4_addr_t) - 1; i >= 0; i--) {
+ char buf[4];
+ u8_t val = ptr[i];
+
+ lwip_itoa(buf, sizeof(buf), val);
+ res = mdns_domain_add_label(domain, buf, (u8_t)strlen(buf));
+ LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res);
+ }
+ res = mdns_domain_add_label(domain, REVERSE_PTR_V4_DOMAIN, (u8_t)(sizeof(REVERSE_PTR_V4_DOMAIN) - 1));
+ LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res);
+ res = mdns_domain_add_label(domain, REVERSE_PTR_TOPDOMAIN, (u8_t)(sizeof(REVERSE_PTR_TOPDOMAIN) - 1));
+ LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res);
+ res = mdns_domain_add_label(domain, NULL, 0);
+ LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res);
+
+ return ERR_OK;
+}
+#endif
+
+#if LWIP_IPV6
+/**
+ * Build domain for reverse lookup of IP address
+ * like b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa. for 2001:db8::567:89ab
+ * @param domain Where to write the domain name
+ * @param addr Pointer to an IPv6 address to encode
+ * @return ERR_OK if domain was written, an err_t otherwise
+ */
+static err_t
+mdns_build_reverse_v6_domain(struct mdns_domain *domain, const ip6_addr_t *addr)
+{
+ int i;
+ err_t res;
+ const u8_t *ptr;
+ LWIP_UNUSED_ARG(res);
+ if (!domain || !addr) {
+ return ERR_ARG;
+ }
+ memset(domain, 0, sizeof(struct mdns_domain));
+ ptr = (const u8_t *) addr;
+ for (i = sizeof(ip6_addr_p_t) - 1; i >= 0; i--) {
+ char buf;
+ u8_t byte = ptr[i];
+ int j;
+ for (j = 0; j < 2; j++) {
+ if ((byte & 0x0F) < 0xA) {
+ buf = '0' + (byte & 0x0F);
+ } else {
+ buf = 'a' + (byte & 0x0F) - 0xA;
+ }
+ res = mdns_domain_add_label(domain, &buf, sizeof(buf));
+ LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res);
+ byte >>= 4;
+ }
+ }
+ res = mdns_domain_add_label(domain, REVERSE_PTR_V6_DOMAIN, (u8_t)(sizeof(REVERSE_PTR_V6_DOMAIN) - 1));
+ LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res);
+ res = mdns_domain_add_label(domain, REVERSE_PTR_TOPDOMAIN, (u8_t)(sizeof(REVERSE_PTR_TOPDOMAIN) - 1));
+ LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res);
+ res = mdns_domain_add_label(domain, NULL, 0);
+ LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res);
+
+ return ERR_OK;
+}
+#endif
+
+/* Add .local. to domain */
+static err_t
+mdns_add_dotlocal(struct mdns_domain *domain)
+{
+ err_t res = mdns_domain_add_label(domain, TOPDOMAIN_LOCAL, (u8_t)(sizeof(TOPDOMAIN_LOCAL) - 1));
+ LWIP_UNUSED_ARG(res);
+ LWIP_ERROR("mdns_add_dotlocal: Failed to add label", (res == ERR_OK), return res);
+ return mdns_domain_add_label(domain, NULL, 0);
+}
+
+/**
+ * Build the .local. domain name
+ * @param domain Where to write the domain name
+ * @param mdns TMDNS netif descriptor.
+ * @return ERR_OK if domain .local. was written, an err_t otherwise
+ */
+static err_t
+mdns_build_host_domain(struct mdns_domain *domain, struct mdns_host *mdns)
+{
+ err_t res;
+ LWIP_UNUSED_ARG(res);
+ memset(domain, 0, sizeof(struct mdns_domain));
+ LWIP_ERROR("mdns_build_host_domain: mdns != NULL", (mdns != NULL), return ERR_VAL);
+ res = mdns_domain_add_label(domain, mdns->name, (u8_t)strlen(mdns->name));
+ LWIP_ERROR("mdns_build_host_domain: Failed to add label", (res == ERR_OK), return res);
+ return mdns_add_dotlocal(domain);
+}
+
+/**
+ * Build the lookup-all-services special DNS-SD domain name
+ * @param domain Where to write the domain name
+ * @return ERR_OK if domain _services._dns-sd._udp.local. was written, an err_t otherwise
+ */
+static err_t
+mdns_build_dnssd_domain(struct mdns_domain *domain)
+{
+ err_t res;
+ LWIP_UNUSED_ARG(res);
+ memset(domain, 0, sizeof(struct mdns_domain));
+ res = mdns_domain_add_label(domain, "_services", (u8_t)(sizeof("_services") - 1));
+ LWIP_ERROR("mdns_build_dnssd_domain: Failed to add label", (res == ERR_OK), return res);
+ res = mdns_domain_add_label(domain, "_dns-sd", (u8_t)(sizeof("_dns-sd") - 1));
+ LWIP_ERROR("mdns_build_dnssd_domain: Failed to add label", (res == ERR_OK), return res);
+ res = mdns_domain_add_label(domain, dnssd_protos[DNSSD_PROTO_UDP], (u8_t)strlen(dnssd_protos[DNSSD_PROTO_UDP]));
+ LWIP_ERROR("mdns_build_dnssd_domain: Failed to add label", (res == ERR_OK), return res);
+ return mdns_add_dotlocal(domain);
+}
+
+/**
+ * Build domain name for a service
+ * @param domain Where to write the domain name
+ * @param service The service struct, containing service name, type and protocol
+ * @param include_name Whether to include the service name in the domain
+ * @return ERR_OK if domain was written. If service name is included,
+ * ...local. will be written, otherwise ..local.
+ * An err_t is returned on error.
+ */
+static err_t
+mdns_build_service_domain(struct mdns_domain *domain, struct mdns_service *service, int include_name)
+{
+ err_t res;
+ LWIP_UNUSED_ARG(res);
+ memset(domain, 0, sizeof(struct mdns_domain));
+ if (include_name) {
+ res = mdns_domain_add_label(domain, service->name, (u8_t)strlen(service->name));
+ LWIP_ERROR("mdns_build_service_domain: Failed to add label", (res == ERR_OK), return res);
+ }
+ res = mdns_domain_add_label(domain, service->service, (u8_t)strlen(service->service));
+ LWIP_ERROR("mdns_build_service_domain: Failed to add label", (res == ERR_OK), return res);
+ res = mdns_domain_add_label(domain, dnssd_protos[service->proto], (u8_t)strlen(dnssd_protos[service->proto]));
+ LWIP_ERROR("mdns_build_service_domain: Failed to add label", (res == ERR_OK), return res);
+ return mdns_add_dotlocal(domain);
+}
+
+/**
+ * Check which replies we should send for a host/netif based on question
+ * @param netif The network interface that received the question
+ * @param rr Domain/type/class from a question
+ * @param reverse_v6_reply Bitmask of which IPv6 addresses to send reverse PTRs for
+ * if reply bit has REPLY_HOST_PTR_V6 set
+ * @return Bitmask of which replies to send
+ */
+static int
+check_host(struct netif *netif, struct mdns_rr_info *rr, u8_t *reverse_v6_reply)
+{
+ err_t res;
+ int replies = 0;
+ struct mdns_domain mydomain;
+
+ LWIP_UNUSED_ARG(reverse_v6_reply); /* if ipv6 is disabled */
+
+ if (rr->klass != DNS_RRCLASS_IN && rr->klass != DNS_RRCLASS_ANY) {
+ /* Invalid class */
+ return replies;
+ }
+
+ /* Handle PTR for our addresses */
+ if (rr->type == DNS_RRTYPE_PTR || rr->type == DNS_RRTYPE_ANY) {
+#if LWIP_IPV6
+ int i;
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) {
+ res = mdns_build_reverse_v6_domain(&mydomain, netif_ip6_addr(netif, i));
+ if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) {
+ replies |= REPLY_HOST_PTR_V6;
+ /* Mark which addresses where requested */
+ if (reverse_v6_reply) {
+ *reverse_v6_reply |= (1 << i);
+ }
+ }
+ }
+ }
+#endif
+#if LWIP_IPV4
+ if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
+ res = mdns_build_reverse_v4_domain(&mydomain, netif_ip4_addr(netif));
+ if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) {
+ replies |= REPLY_HOST_PTR_V4;
+ }
+ }
+#endif
+ }
+
+ res = mdns_build_host_domain(&mydomain, NETIF_TO_HOST(netif));
+ /* Handle requests for our hostname */
+ if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) {
+ /* TODO return NSEC if unsupported protocol requested */
+#if LWIP_IPV4
+ if (!ip4_addr_isany_val(*netif_ip4_addr(netif))
+ && (rr->type == DNS_RRTYPE_A || rr->type == DNS_RRTYPE_ANY)) {
+ replies |= REPLY_HOST_A;
+ }
+#endif
+#if LWIP_IPV6
+ if (rr->type == DNS_RRTYPE_AAAA || rr->type == DNS_RRTYPE_ANY) {
+ replies |= REPLY_HOST_AAAA;
+ }
+#endif
+ }
+
+ return replies;
+}
+
+/**
+ * Check which replies we should send for a service based on question
+ * @param service A registered MDNS service
+ * @param rr Domain/type/class from a question
+ * @return Bitmask of which replies to send
+ */
+static int
+check_service(struct mdns_service *service, struct mdns_rr_info *rr)
+{
+ err_t res;
+ int replies = 0;
+ struct mdns_domain mydomain;
+
+ if (rr->klass != DNS_RRCLASS_IN && rr->klass != DNS_RRCLASS_ANY) {
+ /* Invalid class */
+ return 0;
+ }
+
+ res = mdns_build_dnssd_domain(&mydomain);
+ if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain) &&
+ (rr->type == DNS_RRTYPE_PTR || rr->type == DNS_RRTYPE_ANY)) {
+ /* Request for all service types */
+ replies |= REPLY_SERVICE_TYPE_PTR;
+ }
+
+ res = mdns_build_service_domain(&mydomain, service, 0);
+ if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain) &&
+ (rr->type == DNS_RRTYPE_PTR || rr->type == DNS_RRTYPE_ANY)) {
+ /* Request for the instance of my service */
+ replies |= REPLY_SERVICE_NAME_PTR;
+ }
+
+ res = mdns_build_service_domain(&mydomain, service, 1);
+ if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) {
+ /* Request for info about my service */
+ if (rr->type == DNS_RRTYPE_SRV || rr->type == DNS_RRTYPE_ANY) {
+ replies |= REPLY_SERVICE_SRV;
+ }
+ if (rr->type == DNS_RRTYPE_TXT || rr->type == DNS_RRTYPE_ANY) {
+ replies |= REPLY_SERVICE_TXT;
+ }
+ }
+
+ return replies;
+}
+
+/**
+ * Return bytes needed to write before jump for best result of compressing supplied domain
+ * against domain in outpacket starting at specified offset.
+ * If a match is found, offset is updated to where to jump to
+ * @param pbuf Pointer to pbuf with the partially constructed DNS packet
+ * @param offset Start position of a domain written earlier. If this location is suitable
+ * for compression, the pointer is updated to where in the domain to jump to.
+ * @param domain The domain to write
+ * @return Number of bytes to write of the new domain before writing a jump to the offset.
+ * If compression can not be done against this previous domain name, the full new
+ * domain length is returned.
+ */
+u16_t
+mdns_compress_domain(struct pbuf *pbuf, u16_t *offset, struct mdns_domain *domain)
+{
+ struct mdns_domain target;
+ u16_t target_end;
+ u8_t target_len;
+ u8_t writelen = 0;
+ u8_t *ptr;
+ if (pbuf == NULL) {
+ return domain->length;
+ }
+ target_end = mdns_readname(pbuf, *offset, &target);
+ if (target_end == MDNS_READNAME_ERROR) {
+ return domain->length;
+ }
+ target_len = (u8_t)(target_end - *offset);
+ ptr = domain->name;
+ while (writelen < domain->length) {
+ u8_t domainlen = (u8_t)(domain->length - writelen);
+ u8_t labellen;
+ if (domainlen <= target.length && domainlen > DOMAIN_JUMP_SIZE) {
+ /* Compare domains if target is long enough, and we have enough left of the domain */
+ u8_t targetpos = (u8_t)(target.length - domainlen);
+ if ((targetpos + DOMAIN_JUMP_SIZE) >= target_len) {
+ /* We are checking at or beyond a jump in the original, stop looking */
+ break;
+ }
+ if (target.length >= domainlen &&
+ memcmp(&domain->name[writelen], &target.name[targetpos], domainlen) == 0) {
+ *offset += targetpos;
+ return writelen;
+ }
+ }
+ /* Skip to next label in domain */
+ labellen = *ptr;
+ writelen += 1 + labellen;
+ ptr += 1 + labellen;
+ }
+ /* Nothing found */
+ return domain->length;
+}
+
+/**
+ * Write domain to outpacket. Compression will be attempted,
+ * unless domain->skip_compression is set.
+ * @param outpkt The outpacket to write to
+ * @param domain The domain name to write
+ * @return ERR_OK on success, an err_t otherwise
+ */
+static err_t
+mdns_write_domain(struct mdns_outpacket *outpkt, struct mdns_domain *domain)
+{
+ int i;
+ err_t res;
+ u16_t writelen = domain->length;
+ u16_t jump_offset = 0;
+ u16_t jump;
+
+ if (!domain->skip_compression) {
+ for (i = 0; i < NUM_DOMAIN_OFFSETS; i++) {
+ u16_t offset = outpkt->domain_offsets[i];
+ if (offset) {
+ u16_t len = mdns_compress_domain(outpkt->pbuf, &offset, domain);
+ if (len < writelen) {
+ writelen = len;
+ jump_offset = offset;
+ }
+ }
+ }
+ }
+
+ if (writelen) {
+ /* Write uncompressed part of name */
+ res = pbuf_take_at(outpkt->pbuf, domain->name, writelen, outpkt->write_offset);
+ if (res != ERR_OK) {
+ return res;
+ }
+
+ /* Store offset of this new domain */
+ for (i = 0; i < NUM_DOMAIN_OFFSETS; i++) {
+ if (outpkt->domain_offsets[i] == 0) {
+ outpkt->domain_offsets[i] = outpkt->write_offset;
+ break;
+ }
+ }
+
+ outpkt->write_offset += writelen;
+ }
+ if (jump_offset) {
+ /* Write jump */
+ jump = lwip_htons(DOMAIN_JUMP | jump_offset);
+ res = pbuf_take_at(outpkt->pbuf, &jump, DOMAIN_JUMP_SIZE, outpkt->write_offset);
+ if (res != ERR_OK) {
+ return res;
+ }
+ outpkt->write_offset += DOMAIN_JUMP_SIZE;
+ }
+ return ERR_OK;
+}
+
+/**
+ * Write a question to an outpacket
+ * A question contains domain, type and class. Since an answer also starts with these fields this function is also
+ * called from mdns_add_answer().
+ * @param outpkt The outpacket to write to
+ * @param domain The domain name the answer is for
+ * @param type The DNS type of the answer (like 'AAAA', 'SRV')
+ * @param klass The DNS type of the answer (like 'IN')
+ * @param unicast If highest bit in class should be set, to instruct the responder to
+ * reply with a unicast packet
+ * @return ERR_OK on success, an err_t otherwise
+ */
+static err_t
+mdns_add_question(struct mdns_outpacket *outpkt, struct mdns_domain *domain, u16_t type, u16_t klass, u16_t unicast)
+{
+ u16_t question_len;
+ u16_t field16;
+ err_t res;
+
+ if (!outpkt->pbuf) {
+ /* If no pbuf is active, allocate one */
+ outpkt->pbuf = pbuf_alloc(PBUF_TRANSPORT, OUTPACKET_SIZE, PBUF_RAM);
+ if (!outpkt->pbuf) {
+ return ERR_MEM;
+ }
+ outpkt->write_offset = SIZEOF_DNS_HDR;
+ }
+
+ /* Worst case calculation. Domain string might be compressed */
+ question_len = domain->length + sizeof(type) + sizeof(klass);
+ if (outpkt->write_offset + question_len > outpkt->pbuf->tot_len) {
+ /* No space */
+ return ERR_MEM;
+ }
+
+ /* Write name */
+ res = mdns_write_domain(outpkt, domain);
+ if (res != ERR_OK) {
+ return res;
+ }
+
+ /* Write type */
+ field16 = lwip_htons(type);
+ res = pbuf_take_at(outpkt->pbuf, &field16, sizeof(field16), outpkt->write_offset);
+ if (res != ERR_OK) {
+ return res;
+ }
+ outpkt->write_offset += sizeof(field16);
+
+ /* Write class */
+ if (unicast) {
+ klass |= 0x8000;
+ }
+ field16 = lwip_htons(klass);
+ res = pbuf_take_at(outpkt->pbuf, &field16, sizeof(field16), outpkt->write_offset);
+ if (res != ERR_OK) {
+ return res;
+ }
+ outpkt->write_offset += sizeof(field16);
+
+ return ERR_OK;
+}
+
+/**
+ * Write answer to reply packet.
+ * buf or answer_domain can be null. The rd_length written will be buf_length +
+ * size of (compressed) domain. Most uses will need either buf or answer_domain,
+ * special case is SRV that starts with 3 u16 and then a domain name.
+ * @param reply The outpacket to write to
+ * @param domain The domain name the answer is for
+ * @param type The DNS type of the answer (like 'AAAA', 'SRV')
+ * @param klass The DNS type of the answer (like 'IN')
+ * @param cache_flush If highest bit in class should be set, to instruct receiver that
+ * this reply replaces any earlier answer for this domain/type/class
+ * @param ttl Validity time in seconds to send out for IP address data in DNS replies
+ * @param buf Pointer to buffer of answer data
+ * @param buf_length Length of variable data
+ * @param answer_domain A domain to write after any buffer data as answer
+ * @return ERR_OK on success, an err_t otherwise
+ */
+static err_t
+mdns_add_answer(struct mdns_outpacket *reply, struct mdns_domain *domain, u16_t type, u16_t klass, u16_t cache_flush,
+ u32_t ttl, const u8_t *buf, size_t buf_length, struct mdns_domain *answer_domain)
+{
+ u16_t answer_len;
+ u16_t field16;
+ u16_t rdlen_offset;
+ u16_t answer_offset;
+ u32_t field32;
+ err_t res;
+
+ if (!reply->pbuf) {
+ /* If no pbuf is active, allocate one */
+ reply->pbuf = pbuf_alloc(PBUF_TRANSPORT, OUTPACKET_SIZE, PBUF_RAM);
+ if (!reply->pbuf) {
+ return ERR_MEM;
+ }
+ reply->write_offset = SIZEOF_DNS_HDR;
+ }
+
+ /* Worst case calculation. Domain strings might be compressed */
+ answer_len = domain->length + sizeof(type) + sizeof(klass) + sizeof(ttl) + sizeof(field16)/*rd_length*/;
+ if (buf) {
+ answer_len += (u16_t)buf_length;
+ }
+ if (answer_domain) {
+ answer_len += answer_domain->length;
+ }
+ if (reply->write_offset + answer_len > reply->pbuf->tot_len) {
+ /* No space */
+ return ERR_MEM;
+ }
+
+ /* Answer starts with same data as question, then more fields */
+ mdns_add_question(reply, domain, type, klass, cache_flush);
+
+ /* Write TTL */
+ field32 = lwip_htonl(ttl);
+ res = pbuf_take_at(reply->pbuf, &field32, sizeof(field32), reply->write_offset);
+ if (res != ERR_OK) {
+ return res;
+ }
+ reply->write_offset += sizeof(field32);
+
+ /* Store offsets and skip forward to the data */
+ rdlen_offset = reply->write_offset;
+ reply->write_offset += sizeof(field16);
+ answer_offset = reply->write_offset;
+
+ if (buf) {
+ /* Write static data */
+ res = pbuf_take_at(reply->pbuf, buf, (u16_t)buf_length, reply->write_offset);
+ if (res != ERR_OK) {
+ return res;
+ }
+ reply->write_offset += (u16_t)buf_length;
+ }
+
+ if (answer_domain) {
+ /* Write name answer (compressed if possible) */
+ res = mdns_write_domain(reply, answer_domain);
+ if (res != ERR_OK) {
+ return res;
+ }
+ }
+
+ /* Write rd_length after when we know the answer size */
+ field16 = lwip_htons(reply->write_offset - answer_offset);
+ res = pbuf_take_at(reply->pbuf, &field16, sizeof(field16), rdlen_offset);
+
+ return res;
+}
+
+/**
+ * Helper function for mdns_read_question/mdns_read_answer
+ * Reads a domain, type and class from the packet
+ * @param pkt The MDNS packet to read from. The parse_offset field will be
+ * incremented to point to the next unparsed byte.
+ * @param info The struct to fill with domain, type and class
+ * @return ERR_OK on success, an err_t otherwise
+ */
+static err_t
+mdns_read_rr_info(struct mdns_packet *pkt, struct mdns_rr_info *info)
+{
+ u16_t field16, copied;
+ pkt->parse_offset = mdns_readname(pkt->pbuf, pkt->parse_offset, &info->domain);
+ if (pkt->parse_offset == MDNS_READNAME_ERROR) {
+ return ERR_VAL;
+ }
+
+ copied = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), pkt->parse_offset);
+ if (copied != sizeof(field16)) {
+ return ERR_VAL;
+ }
+ pkt->parse_offset += copied;
+ info->type = lwip_ntohs(field16);
+
+ copied = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), pkt->parse_offset);
+ if (copied != sizeof(field16)) {
+ return ERR_VAL;
+ }
+ pkt->parse_offset += copied;
+ info->klass = lwip_ntohs(field16);
+
+ return ERR_OK;
+}
+
+/**
+ * Read a question from the packet.
+ * All questions have to be read before the answers.
+ * @param pkt The MDNS packet to read from. The questions_left field will be decremented
+ * and the parse_offset will be updated.
+ * @param question The struct to fill with question data
+ * @return ERR_OK on success, an err_t otherwise
+ */
+static err_t
+mdns_read_question(struct mdns_packet *pkt, struct mdns_question *question)
+{
+ /* Safety check */
+ if (pkt->pbuf->tot_len < pkt->parse_offset) {
+ return ERR_VAL;
+ }
+
+ if (pkt->questions_left) {
+ err_t res;
+ pkt->questions_left--;
+
+ memset(question, 0, sizeof(struct mdns_question));
+ res = mdns_read_rr_info(pkt, &question->info);
+ if (res != ERR_OK) {
+ return res;
+ }
+
+ /* Extract unicast flag from class field */
+ question->unicast = question->info.klass & 0x8000;
+ question->info.klass &= 0x7FFF;
+
+ return ERR_OK;
+ }
+ return ERR_VAL;
+}
+
+/**
+ * Read an answer from the packet
+ * The variable length reply is not copied, its pbuf offset and length is stored instead.
+ * @param pkt The MDNS packet to read. The answers_left field will be decremented and
+ * the parse_offset will be updated.
+ * @param answer The struct to fill with answer data
+ * @return ERR_OK on success, an err_t otherwise
+ */
+static err_t
+mdns_read_answer(struct mdns_packet *pkt, struct mdns_answer *answer)
+{
+ /* Read questions first */
+ if (pkt->questions_left) {
+ return ERR_VAL;
+ }
+
+ /* Safety check */
+ if (pkt->pbuf->tot_len < pkt->parse_offset) {
+ return ERR_VAL;
+ }
+
+ if (pkt->answers_left) {
+ u16_t copied, field16;
+ u32_t ttl;
+ err_t res;
+ pkt->answers_left--;
+
+ memset(answer, 0, sizeof(struct mdns_answer));
+ res = mdns_read_rr_info(pkt, &answer->info);
+ if (res != ERR_OK) {
+ return res;
+ }
+
+ /* Extract cache_flush flag from class field */
+ answer->cache_flush = answer->info.klass & 0x8000;
+ answer->info.klass &= 0x7FFF;
+
+ copied = pbuf_copy_partial(pkt->pbuf, &ttl, sizeof(ttl), pkt->parse_offset);
+ if (copied != sizeof(ttl)) {
+ return ERR_VAL;
+ }
+ pkt->parse_offset += copied;
+ answer->ttl = lwip_ntohl(ttl);
+
+ copied = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), pkt->parse_offset);
+ if (copied != sizeof(field16)) {
+ return ERR_VAL;
+ }
+ pkt->parse_offset += copied;
+ answer->rd_length = lwip_ntohs(field16);
+
+ answer->rd_offset = pkt->parse_offset;
+ pkt->parse_offset += answer->rd_length;
+
+ return ERR_OK;
+ }
+ return ERR_VAL;
+}
+
+#if LWIP_IPV4
+/** Write an IPv4 address (A) RR to outpacket */
+static err_t
+mdns_add_a_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif)
+{
+ struct mdns_domain host;
+ mdns_build_host_domain(&host, NETIF_TO_HOST(netif));
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with A record\n"));
+ return mdns_add_answer(reply, &host, DNS_RRTYPE_A, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, (const u8_t *) netif_ip4_addr(netif), sizeof(ip4_addr_t), NULL);
+}
+
+/** Write a 4.3.2.1.in-addr.arpa -> hostname.local PTR RR to outpacket */
+static err_t
+mdns_add_hostv4_ptr_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif)
+{
+ struct mdns_domain host, revhost;
+ mdns_build_host_domain(&host, NETIF_TO_HOST(netif));
+ mdns_build_reverse_v4_domain(&revhost, netif_ip4_addr(netif));
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with v4 PTR record\n"));
+ return mdns_add_answer(reply, &revhost, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, NULL, 0, &host);
+}
+#endif
+
+#if LWIP_IPV6
+/** Write an IPv6 address (AAAA) RR to outpacket */
+static err_t
+mdns_add_aaaa_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif, int addrindex)
+{
+ struct mdns_domain host;
+ mdns_build_host_domain(&host, NETIF_TO_HOST(netif));
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with AAAA record\n"));
+ return mdns_add_answer(reply, &host, DNS_RRTYPE_AAAA, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, (const u8_t *) netif_ip6_addr(netif, addrindex), sizeof(ip6_addr_p_t), NULL);
+}
+
+/** Write a x.y.z.ip6.arpa -> hostname.local PTR RR to outpacket */
+static err_t
+mdns_add_hostv6_ptr_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif, int addrindex)
+{
+ struct mdns_domain host, revhost;
+ mdns_build_host_domain(&host, NETIF_TO_HOST(netif));
+ mdns_build_reverse_v6_domain(&revhost, netif_ip6_addr(netif, addrindex));
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with v6 PTR record\n"));
+ return mdns_add_answer(reply, &revhost, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, NULL, 0, &host);
+}
+#endif
+
+/** Write an all-services -> servicetype PTR RR to outpacket */
+static err_t
+mdns_add_servicetype_ptr_answer(struct mdns_outpacket *reply, struct mdns_service *service)
+{
+ struct mdns_domain service_type, service_dnssd;
+ mdns_build_service_domain(&service_type, service, 0);
+ mdns_build_dnssd_domain(&service_dnssd);
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with service type PTR record\n"));
+ return mdns_add_answer(reply, &service_dnssd, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, 0, service->dns_ttl, NULL, 0, &service_type);
+}
+
+/** Write a servicetype -> servicename PTR RR to outpacket */
+static err_t
+mdns_add_servicename_ptr_answer(struct mdns_outpacket *reply, struct mdns_service *service)
+{
+ struct mdns_domain service_type, service_instance;
+ mdns_build_service_domain(&service_type, service, 0);
+ mdns_build_service_domain(&service_instance, service, 1);
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with service name PTR record\n"));
+ return mdns_add_answer(reply, &service_type, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, 0, service->dns_ttl, NULL, 0, &service_instance);
+}
+
+/** Write a SRV RR to outpacket */
+static err_t
+mdns_add_srv_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct mdns_host *mdns, struct mdns_service *service)
+{
+ struct mdns_domain service_instance, srvhost;
+ u16_t srvdata[3];
+ mdns_build_service_domain(&service_instance, service, 1);
+ mdns_build_host_domain(&srvhost, mdns);
+ if (reply->legacy_query) {
+ /* RFC 6762 section 18.14:
+ * In legacy unicast responses generated to answer legacy queries,
+ * name compression MUST NOT be performed on SRV records.
+ */
+ srvhost.skip_compression = 1;
+ }
+ srvdata[0] = lwip_htons(SRV_PRIORITY);
+ srvdata[1] = lwip_htons(SRV_WEIGHT);
+ srvdata[2] = lwip_htons(service->port);
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with SRV record\n"));
+ return mdns_add_answer(reply, &service_instance, DNS_RRTYPE_SRV, DNS_RRCLASS_IN, cache_flush, service->dns_ttl,
+ (const u8_t *) &srvdata, sizeof(srvdata), &srvhost);
+}
+
+/** Write a TXT RR to outpacket */
+static err_t
+mdns_add_txt_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct mdns_service *service)
+{
+ struct mdns_domain service_instance;
+ mdns_build_service_domain(&service_instance, service, 1);
+ mdns_prepare_txtdata(service);
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with TXT record\n"));
+ return mdns_add_answer(reply, &service_instance, DNS_RRTYPE_TXT, DNS_RRCLASS_IN, cache_flush, service->dns_ttl,
+ (u8_t *) &service->txtdata.name, service->txtdata.length, NULL);
+}
+
+/**
+ * Setup outpacket as a reply to the incoming packet
+ */
+static void
+mdns_init_outpacket(struct mdns_outpacket *out, struct mdns_packet *in)
+{
+ memset(out, 0, sizeof(struct mdns_outpacket));
+ out->cache_flush = 1;
+ out->netif = in->netif;
+
+ /* Copy source IP/port to use when responding unicast, or to choose
+ * which pcb to use for multicast (IPv4/IPv6)
+ */
+ SMEMCPY(&out->dest_addr, &in->source_addr, sizeof(ip_addr_t));
+ out->dest_port = in->source_port;
+
+ if (in->source_port != LWIP_IANA_PORT_MDNS) {
+ out->unicast_reply = 1;
+ out->cache_flush = 0;
+ if (in->questions == 1) {
+ out->legacy_query = 1;
+ out->tx_id = in->tx_id;
+ }
+ }
+
+ if (in->recv_unicast) {
+ out->unicast_reply = 1;
+ }
+}
+
+/**
+ * Send chosen answers as a reply
+ *
+ * Add all selected answers (first write will allocate pbuf)
+ * Add additional answers based on the selected answers
+ * Send the packet
+ */
+static err_t
+mdns_send_outpacket(struct mdns_outpacket *outpkt, u8_t flags)
+{
+ struct mdns_service *service;
+ err_t res = ERR_ARG;
+ int i;
+ struct mdns_host *mdns = NETIF_TO_HOST(outpkt->netif);
+ u16_t answers = 0;
+
+ /* Write answers to host questions */
+#if LWIP_IPV4
+ if (outpkt->host_replies & REPLY_HOST_A) {
+ res = mdns_add_a_answer(outpkt, outpkt->cache_flush, outpkt->netif);
+ if (res != ERR_OK) {
+ goto cleanup;
+ }
+ answers++;
+ }
+ if (outpkt->host_replies & REPLY_HOST_PTR_V4) {
+ res = mdns_add_hostv4_ptr_answer(outpkt, outpkt->cache_flush, outpkt->netif);
+ if (res != ERR_OK) {
+ goto cleanup;
+ }
+ answers++;
+ }
+#endif
+#if LWIP_IPV6
+ if (outpkt->host_replies & REPLY_HOST_AAAA) {
+ int addrindex;
+ for (addrindex = 0; addrindex < LWIP_IPV6_NUM_ADDRESSES; addrindex++) {
+ if (ip6_addr_isvalid(netif_ip6_addr_state(outpkt->netif, addrindex))) {
+ res = mdns_add_aaaa_answer(outpkt, outpkt->cache_flush, outpkt->netif, addrindex);
+ if (res != ERR_OK) {
+ goto cleanup;
+ }
+ answers++;
+ }
+ }
+ }
+ if (outpkt->host_replies & REPLY_HOST_PTR_V6) {
+ u8_t rev_addrs = outpkt->host_reverse_v6_replies;
+ int addrindex = 0;
+ while (rev_addrs) {
+ if (rev_addrs & 1) {
+ res = mdns_add_hostv6_ptr_answer(outpkt, outpkt->cache_flush, outpkt->netif, addrindex);
+ if (res != ERR_OK) {
+ goto cleanup;
+ }
+ answers++;
+ }
+ addrindex++;
+ rev_addrs >>= 1;
+ }
+ }
+#endif
+
+ /* Write answers to service questions */
+ for (i = 0; i < MDNS_MAX_SERVICES; i++) {
+ service = mdns->services[i];
+ if (!service) {
+ continue;
+ }
+
+ if (outpkt->serv_replies[i] & REPLY_SERVICE_TYPE_PTR) {
+ res = mdns_add_servicetype_ptr_answer(outpkt, service);
+ if (res != ERR_OK) {
+ goto cleanup;
+ }
+ answers++;
+ }
+
+ if (outpkt->serv_replies[i] & REPLY_SERVICE_NAME_PTR) {
+ res = mdns_add_servicename_ptr_answer(outpkt, service);
+ if (res != ERR_OK) {
+ goto cleanup;
+ }
+ answers++;
+ }
+
+ if (outpkt->serv_replies[i] & REPLY_SERVICE_SRV) {
+ res = mdns_add_srv_answer(outpkt, outpkt->cache_flush, mdns, service);
+ if (res != ERR_OK) {
+ goto cleanup;
+ }
+ answers++;
+ }
+
+ if (outpkt->serv_replies[i] & REPLY_SERVICE_TXT) {
+ res = mdns_add_txt_answer(outpkt, outpkt->cache_flush, service);
+ if (res != ERR_OK) {
+ goto cleanup;
+ }
+ answers++;
+ }
+ }
+
+ /* if this is a response, the data above is anwers, else this is a probe and the answers above goes into auth section */
+ if (flags & DNS_FLAG1_RESPONSE) {
+ outpkt->answers += answers;
+ } else {
+ outpkt->authoritative += answers;
+ }
+
+ /* All answers written, add additional RRs */
+ for (i = 0; i < MDNS_MAX_SERVICES; i++) {
+ service = mdns->services[i];
+ if (!service) {
+ continue;
+ }
+
+ if (outpkt->serv_replies[i] & REPLY_SERVICE_NAME_PTR) {
+ /* Our service instance requested, include SRV & TXT
+ * if they are already not requested. */
+ if (!(outpkt->serv_replies[i] & REPLY_SERVICE_SRV)) {
+ res = mdns_add_srv_answer(outpkt, outpkt->cache_flush, mdns, service);
+ if (res != ERR_OK) {
+ goto cleanup;
+ }
+ outpkt->additional++;
+ }
+
+ if (!(outpkt->serv_replies[i] & REPLY_SERVICE_TXT)) {
+ res = mdns_add_txt_answer(outpkt, outpkt->cache_flush, service);
+ if (res != ERR_OK) {
+ goto cleanup;
+ }
+ outpkt->additional++;
+ }
+ }
+
+ /* If service instance, SRV, record or an IP address is requested,
+ * supply all addresses for the host
+ */
+ if ((outpkt->serv_replies[i] & (REPLY_SERVICE_NAME_PTR | REPLY_SERVICE_SRV)) ||
+ (outpkt->host_replies & (REPLY_HOST_A | REPLY_HOST_AAAA))) {
+#if LWIP_IPV6
+ if (!(outpkt->host_replies & REPLY_HOST_AAAA)) {
+ int addrindex;
+ for (addrindex = 0; addrindex < LWIP_IPV6_NUM_ADDRESSES; addrindex++) {
+ if (ip6_addr_isvalid(netif_ip6_addr_state(outpkt->netif, addrindex))) {
+ res = mdns_add_aaaa_answer(outpkt, outpkt->cache_flush, outpkt->netif, addrindex);
+ if (res != ERR_OK) {
+ goto cleanup;
+ }
+ outpkt->additional++;
+ }
+ }
+ }
+#endif
+#if LWIP_IPV4
+ if (!(outpkt->host_replies & REPLY_HOST_A) &&
+ !ip4_addr_isany_val(*netif_ip4_addr(outpkt->netif))) {
+ res = mdns_add_a_answer(outpkt, outpkt->cache_flush, outpkt->netif);
+ if (res != ERR_OK) {
+ goto cleanup;
+ }
+ outpkt->additional++;
+ }
+#endif
+ }
+ }
+
+ if (outpkt->pbuf) {
+ const ip_addr_t *mcast_destaddr;
+ struct dns_hdr hdr;
+
+ /* Write header */
+ memset(&hdr, 0, sizeof(hdr));
+ hdr.flags1 = flags;
+ hdr.numquestions = lwip_htons(outpkt->questions);
+ hdr.numanswers = lwip_htons(outpkt->answers);
+ hdr.numauthrr = lwip_htons(outpkt->authoritative);
+ hdr.numextrarr = lwip_htons(outpkt->additional);
+ hdr.id = lwip_htons(outpkt->tx_id);
+ pbuf_take(outpkt->pbuf, &hdr, sizeof(hdr));
+
+ /* Shrink packet */
+ pbuf_realloc(outpkt->pbuf, outpkt->write_offset);
+
+ if (IP_IS_V6_VAL(outpkt->dest_addr)) {
+#if LWIP_IPV6
+ mcast_destaddr = &v6group;
+#endif
+ } else {
+#if LWIP_IPV4
+ mcast_destaddr = &v4group;
+#endif
+ }
+ /* Send created packet */
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Sending packet, len=%d, unicast=%d\n", outpkt->write_offset, outpkt->unicast_reply));
+ if (outpkt->unicast_reply) {
+ res = udp_sendto_if(mdns_pcb, outpkt->pbuf, &outpkt->dest_addr, outpkt->dest_port, outpkt->netif);
+ } else {
+ res = udp_sendto_if(mdns_pcb, outpkt->pbuf, mcast_destaddr, LWIP_IANA_PORT_MDNS, outpkt->netif);
+ }
+ }
+
+cleanup:
+ if (outpkt->pbuf) {
+ pbuf_free(outpkt->pbuf);
+ outpkt->pbuf = NULL;
+ }
+ return res;
+}
+
+/**
+ * Send unsolicited answer containing all our known data
+ * @param netif The network interface to send on
+ * @param destination The target address to send to (usually multicast address)
+ */
+static void
+mdns_announce(struct netif *netif, const ip_addr_t *destination)
+{
+ struct mdns_outpacket announce;
+ int i;
+ struct mdns_host *mdns = NETIF_TO_HOST(netif);
+
+ memset(&announce, 0, sizeof(announce));
+ announce.netif = netif;
+ announce.cache_flush = 1;
+#if LWIP_IPV4
+ if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
+ announce.host_replies = REPLY_HOST_A | REPLY_HOST_PTR_V4;
+ }
+#endif
+#if LWIP_IPV6
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) {
+ announce.host_replies |= REPLY_HOST_AAAA | REPLY_HOST_PTR_V6;
+ announce.host_reverse_v6_replies |= (1 << i);
+ }
+ }
+#endif
+
+ for (i = 0; i < MDNS_MAX_SERVICES; i++) {
+ struct mdns_service *serv = mdns->services[i];
+ if (serv) {
+ announce.serv_replies[i] = REPLY_SERVICE_TYPE_PTR | REPLY_SERVICE_NAME_PTR |
+ REPLY_SERVICE_SRV | REPLY_SERVICE_TXT;
+ }
+ }
+
+ announce.dest_port = LWIP_IANA_PORT_MDNS;
+ SMEMCPY(&announce.dest_addr, destination, sizeof(announce.dest_addr));
+ mdns_send_outpacket(&announce, DNS_FLAG1_RESPONSE | DNS_FLAG1_AUTHORATIVE);
+}
+
+/**
+ * Handle question MDNS packet
+ * 1. Parse all questions and set bits what answers to send
+ * 2. Clear pending answers if known answers are supplied
+ * 3. Put chosen answers in new packet and send as reply
+ */
+static void
+mdns_handle_question(struct mdns_packet *pkt)
+{
+ struct mdns_service *service;
+ struct mdns_outpacket reply;
+ int replies = 0;
+ int i;
+ err_t res;
+ struct mdns_host *mdns = NETIF_TO_HOST(pkt->netif);
+
+ if (mdns->probing_state != MDNS_PROBING_COMPLETE) {
+ /* Don't answer questions until we've verified our domains via probing */
+ /* @todo we should check incoming questions during probing for tiebreaking */
+ return;
+ }
+
+ mdns_init_outpacket(&reply, pkt);
+
+ while (pkt->questions_left) {
+ struct mdns_question q;
+
+ res = mdns_read_question(pkt, &q);
+ if (res != ERR_OK) {
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse question, skipping query packet\n"));
+ return;
+ }
+
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Query for domain "));
+ mdns_domain_debug_print(&q.info.domain);
+ LWIP_DEBUGF(MDNS_DEBUG, (" type %d class %d\n", q.info.type, q.info.klass));
+
+ if (q.unicast) {
+ /* Reply unicast if any question is unicast */
+ reply.unicast_reply = 1;
+ }
+
+ reply.host_replies |= check_host(pkt->netif, &q.info, &reply.host_reverse_v6_replies);
+ replies |= reply.host_replies;
+
+ for (i = 0; i < MDNS_MAX_SERVICES; i++) {
+ service = mdns->services[i];
+ if (!service) {
+ continue;
+ }
+ reply.serv_replies[i] |= check_service(service, &q.info);
+ replies |= reply.serv_replies[i];
+ }
+
+ if (replies && reply.legacy_query) {
+ /* Add question to reply packet (legacy packet only has 1 question) */
+ res = mdns_add_question(&reply, &q.info.domain, q.info.type, q.info.klass, 0);
+ reply.questions = 1;
+ if (res != ERR_OK) {
+ goto cleanup;
+ }
+ }
+ }
+
+ /* Handle known answers */
+ while (pkt->answers_left) {
+ struct mdns_answer ans;
+ u8_t rev_v6;
+ int match;
+
+ res = mdns_read_answer(pkt, &ans);
+ if (res != ERR_OK) {
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse answer, skipping query packet\n"));
+ goto cleanup;
+ }
+
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Known answer for domain "));
+ mdns_domain_debug_print(&ans.info.domain);
+ LWIP_DEBUGF(MDNS_DEBUG, (" type %d class %d\n", ans.info.type, ans.info.klass));
+
+
+ if (ans.info.type == DNS_RRTYPE_ANY || ans.info.klass == DNS_RRCLASS_ANY) {
+ /* Skip known answers for ANY type & class */
+ continue;
+ }
+
+ rev_v6 = 0;
+ match = reply.host_replies & check_host(pkt->netif, &ans.info, &rev_v6);
+ if (match && (ans.ttl > (mdns->dns_ttl / 2))) {
+ /* The RR in the known answer matches an RR we are planning to send,
+ * and the TTL is less than half gone.
+ * If the payload matches we should not send that answer.
+ */
+ if (ans.info.type == DNS_RRTYPE_PTR) {
+ /* Read domain and compare */
+ struct mdns_domain known_ans, my_ans;
+ u16_t len;
+ len = mdns_readname(pkt->pbuf, ans.rd_offset, &known_ans);
+ res = mdns_build_host_domain(&my_ans, mdns);
+ if (len != MDNS_READNAME_ERROR && res == ERR_OK && mdns_domain_eq(&known_ans, &my_ans)) {
+#if LWIP_IPV4
+ if (match & REPLY_HOST_PTR_V4) {
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: v4 PTR\n"));
+ reply.host_replies &= ~REPLY_HOST_PTR_V4;
+ }
+#endif
+#if LWIP_IPV6
+ if (match & REPLY_HOST_PTR_V6) {
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: v6 PTR\n"));
+ reply.host_reverse_v6_replies &= ~rev_v6;
+ if (reply.host_reverse_v6_replies == 0) {
+ reply.host_replies &= ~REPLY_HOST_PTR_V6;
+ }
+ }
+#endif
+ }
+ } else if (match & REPLY_HOST_A) {
+#if LWIP_IPV4
+ if (ans.rd_length == sizeof(ip4_addr_t) &&
+ pbuf_memcmp(pkt->pbuf, ans.rd_offset, netif_ip4_addr(pkt->netif), ans.rd_length) == 0) {
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: A\n"));
+ reply.host_replies &= ~REPLY_HOST_A;
+ }
+#endif
+ } else if (match & REPLY_HOST_AAAA) {
+#if LWIP_IPV6
+ if (ans.rd_length == sizeof(ip6_addr_p_t) &&
+ /* TODO this clears all AAAA responses if first addr is set as known */
+ pbuf_memcmp(pkt->pbuf, ans.rd_offset, netif_ip6_addr(pkt->netif, 0), ans.rd_length) == 0) {
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: AAAA\n"));
+ reply.host_replies &= ~REPLY_HOST_AAAA;
+ }
+#endif
+ }
+ }
+
+ for (i = 0; i < MDNS_MAX_SERVICES; i++) {
+ service = mdns->services[i];
+ if (!service) {
+ continue;
+ }
+ match = reply.serv_replies[i] & check_service(service, &ans.info);
+ if (match && (ans.ttl > (service->dns_ttl / 2))) {
+ /* The RR in the known answer matches an RR we are planning to send,
+ * and the TTL is less than half gone.
+ * If the payload matches we should not send that answer.
+ */
+ if (ans.info.type == DNS_RRTYPE_PTR) {
+ /* Read domain and compare */
+ struct mdns_domain known_ans, my_ans;
+ u16_t len;
+ len = mdns_readname(pkt->pbuf, ans.rd_offset, &known_ans);
+ if (len != MDNS_READNAME_ERROR) {
+ if (match & REPLY_SERVICE_TYPE_PTR) {
+ res = mdns_build_service_domain(&my_ans, service, 0);
+ if (res == ERR_OK && mdns_domain_eq(&known_ans, &my_ans)) {
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: service type PTR\n"));
+ reply.serv_replies[i] &= ~REPLY_SERVICE_TYPE_PTR;
+ }
+ }
+ if (match & REPLY_SERVICE_NAME_PTR) {
+ res = mdns_build_service_domain(&my_ans, service, 1);
+ if (res == ERR_OK && mdns_domain_eq(&known_ans, &my_ans)) {
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: service name PTR\n"));
+ reply.serv_replies[i] &= ~REPLY_SERVICE_NAME_PTR;
+ }
+ }
+ }
+ } else if (match & REPLY_SERVICE_SRV) {
+ /* Read and compare to my SRV record */
+ u16_t field16, len, read_pos;
+ struct mdns_domain known_ans, my_ans;
+ read_pos = ans.rd_offset;
+ do {
+ /* Check priority field */
+ len = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), read_pos);
+ if (len != sizeof(field16) || lwip_ntohs(field16) != SRV_PRIORITY) {
+ break;
+ }
+ read_pos += len;
+ /* Check weight field */
+ len = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), read_pos);
+ if (len != sizeof(field16) || lwip_ntohs(field16) != SRV_WEIGHT) {
+ break;
+ }
+ read_pos += len;
+ /* Check port field */
+ len = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), read_pos);
+ if (len != sizeof(field16) || lwip_ntohs(field16) != service->port) {
+ break;
+ }
+ read_pos += len;
+ /* Check host field */
+ len = mdns_readname(pkt->pbuf, read_pos, &known_ans);
+ mdns_build_host_domain(&my_ans, mdns);
+ if (len == MDNS_READNAME_ERROR || !mdns_domain_eq(&known_ans, &my_ans)) {
+ break;
+ }
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: SRV\n"));
+ reply.serv_replies[i] &= ~REPLY_SERVICE_SRV;
+ } while (0);
+ } else if (match & REPLY_SERVICE_TXT) {
+ mdns_prepare_txtdata(service);
+ if (service->txtdata.length == ans.rd_length &&
+ pbuf_memcmp(pkt->pbuf, ans.rd_offset, service->txtdata.name, ans.rd_length) == 0) {
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: TXT\n"));
+ reply.serv_replies[i] &= ~REPLY_SERVICE_TXT;
+ }
+ }
+ }
+ }
+ }
+
+ mdns_send_outpacket(&reply, DNS_FLAG1_RESPONSE | DNS_FLAG1_AUTHORATIVE);
+
+cleanup:
+ if (reply.pbuf) {
+ /* This should only happen if we fail to alloc/write question for legacy query */
+ pbuf_free(reply.pbuf);
+ reply.pbuf = NULL;
+ }
+}
+
+/**
+ * Handle response MDNS packet
+ * Only prints debug for now. Will need more code to do conflict resolution.
+ */
+static void
+mdns_handle_response(struct mdns_packet *pkt)
+{
+ struct mdns_host* mdns = NETIF_TO_HOST(pkt->netif);
+
+ /* Ignore all questions */
+ while (pkt->questions_left) {
+ struct mdns_question q;
+ err_t res;
+
+ res = mdns_read_question(pkt, &q);
+ if (res != ERR_OK) {
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse question, skipping response packet\n"));
+ return;
+ }
+ }
+
+ while (pkt->answers_left) {
+ struct mdns_answer ans;
+ err_t res;
+
+ res = mdns_read_answer(pkt, &ans);
+ if (res != ERR_OK) {
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse answer, skipping response packet\n"));
+ return;
+ }
+
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Answer for domain "));
+ mdns_domain_debug_print(&ans.info.domain);
+ LWIP_DEBUGF(MDNS_DEBUG, (" type %d class %d\n", ans.info.type, ans.info.klass));
+
+ /*"Apparently conflicting Multicast DNS responses received *before* the first probe packet is sent MUST
+ be silently ignored" so drop answer if we haven't started probing yet*/
+ if ((mdns->probing_state == MDNS_PROBING_ONGOING) && (mdns->probes_sent > 0)) {
+ struct mdns_domain domain;
+ u8_t i;
+ u8_t conflict = 0;
+
+ res = mdns_build_host_domain(&domain, mdns);
+ if (res == ERR_OK && mdns_domain_eq(&ans.info.domain, &domain)) {
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Probe response matches host domain!"));
+ conflict = 1;
+ }
+
+ for (i = 0; i < MDNS_MAX_SERVICES; i++) {
+ struct mdns_service* service = mdns->services[i];
+ if (!service) {
+ continue;
+ }
+ res = mdns_build_service_domain(&domain, service, 1);
+ if ((res == ERR_OK) && mdns_domain_eq(&ans.info.domain, &domain)) {
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Probe response matches service domain!"));
+ conflict = 1;
+ }
+ }
+
+ if (conflict != 0) {
+ sys_untimeout(mdns_probe, pkt->netif);
+ if (mdns_name_result_cb != NULL) {
+ mdns_name_result_cb(pkt->netif, MDNS_PROBING_CONFLICT);
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Receive input function for MDNS packets.
+ * Handles both IPv4 and IPv6 UDP pcbs.
+ */
+static void
+mdns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
+{
+ struct dns_hdr hdr;
+ struct mdns_packet packet;
+ struct netif *recv_netif = ip_current_input_netif();
+ u16_t offset = 0;
+
+ LWIP_UNUSED_ARG(arg);
+ LWIP_UNUSED_ARG(pcb);
+
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Received IPv%d MDNS packet, len %d\n", IP_IS_V6(addr) ? 6 : 4, p->tot_len));
+
+ if (NETIF_TO_HOST(recv_netif) == NULL) {
+ /* From netif not configured for MDNS */
+ goto dealloc;
+ }
+
+ if (pbuf_copy_partial(p, &hdr, SIZEOF_DNS_HDR, offset) < SIZEOF_DNS_HDR) {
+ /* Too small */
+ goto dealloc;
+ }
+ offset += SIZEOF_DNS_HDR;
+
+ if (DNS_HDR_GET_OPCODE(&hdr)) {
+ /* Ignore non-standard queries in multicast packets (RFC 6762, section 18.3) */
+ goto dealloc;
+ }
+
+ memset(&packet, 0, sizeof(packet));
+ SMEMCPY(&packet.source_addr, addr, sizeof(packet.source_addr));
+ packet.source_port = port;
+ packet.netif = recv_netif;
+ packet.pbuf = p;
+ packet.parse_offset = offset;
+ packet.tx_id = lwip_ntohs(hdr.id);
+ packet.questions = packet.questions_left = lwip_ntohs(hdr.numquestions);
+ packet.answers = packet.answers_left = lwip_ntohs(hdr.numanswers) + lwip_ntohs(hdr.numauthrr) + lwip_ntohs(hdr.numextrarr);
+
+#if LWIP_IPV6
+ if (IP_IS_V6(ip_current_dest_addr())) {
+ /* instead of having one 'v6group' per netif, just compare zoneless here */
+ if (!ip_addr_cmp_zoneless(ip_current_dest_addr(), &v6group)) {
+ packet.recv_unicast = 1;
+ }
+ }
+#endif
+#if LWIP_IPV4
+ if (!IP_IS_V6(ip_current_dest_addr())) {
+ if (!ip_addr_cmp(ip_current_dest_addr(), &v4group)) {
+ packet.recv_unicast = 1;
+ }
+ }
+#endif
+
+ if (hdr.flags1 & DNS_FLAG1_RESPONSE) {
+ mdns_handle_response(&packet);
+ } else {
+ mdns_handle_question(&packet);
+ }
+
+dealloc:
+ pbuf_free(p);
+}
+
+#if LWIP_NETIF_EXT_STATUS_CALLBACK && MDNS_RESP_USENETIF_EXTCALLBACK
+static void
+mdns_netif_ext_status_callback(struct netif *netif, netif_nsc_reason_t reason, const netif_ext_callback_args_t *args)
+{
+ LWIP_UNUSED_ARG(args);
+
+ /* MDNS enabled on netif? */
+ if (NETIF_TO_HOST(netif) == NULL) {
+ return;
+ }
+
+ if (reason & LWIP_NSC_STATUS_CHANGED) {
+ if (args->status_changed.state != 0) {
+ mdns_resp_restart(netif);
+ }
+ /* TODO: send goodbye message */
+ }
+ if (reason & LWIP_NSC_LINK_CHANGED) {
+ if (args->link_changed.state != 0) {
+ mdns_resp_restart(netif);
+ }
+ }
+ if (reason & (LWIP_NSC_IPV4_ADDRESS_CHANGED | LWIP_NSC_IPV4_GATEWAY_CHANGED |
+ LWIP_NSC_IPV4_NETMASK_CHANGED | LWIP_NSC_IPV4_SETTINGS_CHANGED |
+ LWIP_NSC_IPV6_SET | LWIP_NSC_IPV6_ADDR_STATE_CHANGED)) {
+ mdns_resp_announce(netif);
+ }
+}
+#endif /* LWIP_NETIF_EXT_STATUS_CALLBACK && MDNS_RESP_USENETIF_EXTCALLBACK */
+
+static err_t
+mdns_send_probe(struct netif* netif, const ip_addr_t *destination)
+{
+ struct mdns_host* mdns;
+ struct mdns_outpacket pkt;
+ struct mdns_domain domain;
+ u8_t i;
+ err_t res;
+
+ mdns = NETIF_TO_HOST(netif);
+
+ memset(&pkt, 0, sizeof(pkt));
+ pkt.netif = netif;
+
+ /* Add unicast questions with rtype ANY for all our desired records */
+ mdns_build_host_domain(&domain, mdns);
+ res = mdns_add_question(&pkt, &domain, DNS_RRTYPE_ANY, DNS_RRCLASS_IN, 1);
+ if (res != ERR_OK) {
+ goto cleanup;
+ }
+ pkt.questions++;
+ for (i = 0; i < MDNS_MAX_SERVICES; i++) {
+ struct mdns_service* service = mdns->services[i];
+ if (!service) {
+ continue;
+ }
+ mdns_build_service_domain(&domain, service, 1);
+ res = mdns_add_question(&pkt, &domain, DNS_RRTYPE_ANY, DNS_RRCLASS_IN, 1);
+ if (res != ERR_OK) {
+ goto cleanup;
+ }
+ pkt.questions++;
+ }
+
+ /* Add answers to the questions above into the authority section for tiebreaking */
+#if LWIP_IPV4
+ if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
+ pkt.host_replies = REPLY_HOST_A;
+ }
+#endif
+#if LWIP_IPV6
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) {
+ pkt.host_replies |= REPLY_HOST_AAAA;
+ }
+ }
+#endif
+
+ for (i = 0; i < MDNS_MAX_SERVICES; i++) {
+ struct mdns_service *serv = mdns->services[i];
+ if (serv) {
+ pkt.serv_replies[i] = REPLY_SERVICE_SRV | REPLY_SERVICE_TXT;
+ }
+ }
+
+ pkt.tx_id = 0;
+ pkt.dest_port = LWIP_IANA_PORT_MDNS;
+ SMEMCPY(&pkt.dest_addr, destination, sizeof(pkt.dest_addr));
+ res = mdns_send_outpacket(&pkt, 0);
+
+cleanup:
+ if (pkt.pbuf) {
+ pbuf_free(pkt.pbuf);
+ pkt.pbuf = NULL;
+ }
+ return res;
+}
+
+/**
+ * Timer callback for probing network.
+ */
+static void
+mdns_probe(void* arg)
+{
+ struct netif *netif = (struct netif *)arg;
+ struct mdns_host* mdns = NETIF_TO_HOST(netif);
+
+ if(mdns->probes_sent >= MDNS_PROBE_COUNT) {
+ /* probing successful, announce the new name */
+ mdns->probing_state = MDNS_PROBING_COMPLETE;
+ mdns_resp_announce(netif);
+ if (mdns_name_result_cb != NULL) {
+ mdns_name_result_cb(netif, MDNS_PROBING_SUCCESSFUL);
+ }
+ } else {
+#if LWIP_IPV4
+ /*if ipv4 wait with probing until address is set*/
+ if (!ip4_addr_isany_val(*netif_ip4_addr(netif)) &&
+ mdns_send_probe(netif, IP4_ADDR_ANY) == ERR_OK)
+#endif
+ {
+#if LWIP_IPV6
+ if (mdns_send_probe(netif, IP6_ADDR_ANY) == ERR_OK)
+#endif
+ {
+ mdns->probes_sent++;
+ }
+ }
+ sys_timeout(MDNS_PROBE_DELAY_MS, mdns_probe, netif);
+ }
+}
+
+/**
+ * @ingroup mdns
+ * Activate MDNS responder for a network interface.
+ * @param netif The network interface to activate.
+ * @param hostname Name to use. Queries for <hostname>.local will be answered
+ * with the IP addresses of the netif. The hostname will be copied, the
+ * given pointer can be on the stack.
+ * @param dns_ttl Validity time in seconds to send out for IP address data in DNS replies
+ * @return ERR_OK if netif was added, an err_t otherwise
+ */
+err_t
+mdns_resp_add_netif(struct netif *netif, const char *hostname, u32_t dns_ttl)
+{
+ err_t res;
+ struct mdns_host *mdns;
+
+ LWIP_ASSERT_CORE_LOCKED();
+ LWIP_ERROR("mdns_resp_add_netif: netif != NULL", (netif != NULL), return ERR_VAL);
+ LWIP_ERROR("mdns_resp_add_netif: Hostname too long", (strlen(hostname) <= MDNS_LABEL_MAXLEN), return ERR_VAL);
+
+ LWIP_ASSERT("mdns_resp_add_netif: Double add", NETIF_TO_HOST(netif) == NULL);
+ mdns = (struct mdns_host *) mem_calloc(1, sizeof(struct mdns_host));
+ LWIP_ERROR("mdns_resp_add_netif: Alloc failed", (mdns != NULL), return ERR_MEM);
+
+ netif_set_client_data(netif, mdns_netif_client_id, mdns);
+
+ MEMCPY(&mdns->name, hostname, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(hostname)));
+ mdns->dns_ttl = dns_ttl;
+ mdns->probes_sent = 0;
+ mdns->probing_state = MDNS_PROBING_NOT_STARTED;
+
+ /* Join multicast groups */
+#if LWIP_IPV4
+ res = igmp_joingroup_netif(netif, ip_2_ip4(&v4group));
+ if (res != ERR_OK) {
+ goto cleanup;
+ }
+#endif
+#if LWIP_IPV6
+ res = mld6_joingroup_netif(netif, ip_2_ip6(&v6group));
+ if (res != ERR_OK) {
+ goto cleanup;
+ }
+#endif
+
+ mdns_resp_restart(netif);
+
+ return ERR_OK;
+
+cleanup:
+ mem_free(mdns);
+ netif_set_client_data(netif, mdns_netif_client_id, NULL);
+ return res;
+}
+
+/**
+ * @ingroup mdns
+ * Stop responding to MDNS queries on this interface, leave multicast groups,
+ * and free the helper structure and any of its services.
+ * @param netif The network interface to remove.
+ * @return ERR_OK if netif was removed, an err_t otherwise
+ */
+err_t
+mdns_resp_remove_netif(struct netif *netif)
+{
+ int i;
+ struct mdns_host *mdns;
+
+ LWIP_ASSERT_CORE_LOCKED();
+ LWIP_ASSERT("mdns_resp_remove_netif: Null pointer", netif);
+ mdns = NETIF_TO_HOST(netif);
+ LWIP_ERROR("mdns_resp_remove_netif: Not an active netif", (mdns != NULL), return ERR_VAL);
+
+ if (mdns->probing_state == MDNS_PROBING_ONGOING) {
+ sys_untimeout(mdns_probe, netif);
+ }
+
+ for (i = 0; i < MDNS_MAX_SERVICES; i++) {
+ struct mdns_service *service = mdns->services[i];
+ if (service) {
+ mem_free(service);
+ }
+ }
+
+ /* Leave multicast groups */
+#if LWIP_IPV4
+ igmp_leavegroup_netif(netif, ip_2_ip4(&v4group));
+#endif
+#if LWIP_IPV6
+ mld6_leavegroup_netif(netif, ip_2_ip6(&v6group));
+#endif
+
+ mem_free(mdns);
+ netif_set_client_data(netif, mdns_netif_client_id, NULL);
+ return ERR_OK;
+}
+
+/**
+ * @ingroup mdns
+ * Update MDNS hostname for a network interface.
+ * @param netif The network interface to activate.
+ * @param hostname Name to use. Queries for <hostname>.local will be answered
+ * with the IP addresses of the netif. The hostname will be copied, the
+ * given pointer can be on the stack.
+ * @return ERR_OK if name could be set on netif, an err_t otherwise
+ */
+err_t
+mdns_resp_rename_netif(struct netif *netif, const char *hostname)
+{
+ struct mdns_host *mdns;
+ size_t len;
+
+ LWIP_ASSERT_CORE_LOCKED();
+ len = strlen(hostname);
+ LWIP_ERROR("mdns_resp_rename_netif: netif != NULL", (netif != NULL), return ERR_VAL);
+ LWIP_ERROR("mdns_resp_rename_netif: Hostname too long", (len <= MDNS_LABEL_MAXLEN), return ERR_VAL);
+ mdns = NETIF_TO_HOST(netif);
+ LWIP_ERROR("mdns_resp_rename_netif: Not an mdns netif", (mdns != NULL), return ERR_VAL);
+
+ MEMCPY(&mdns->name, hostname, LWIP_MIN(MDNS_LABEL_MAXLEN, len));
+ mdns->name[len] = '\0'; /* null termination in case new name is shorter than previous */
+
+ mdns_resp_restart(netif);
+
+ return ERR_OK;
+}
+
+/**
+ * @ingroup mdns
+ * Add a service to the selected network interface.
+ * @param netif The network interface to publish this service on
+ * @param name The name of the service
+ * @param service The service type, like "_http"
+ * @param proto The service protocol, DNSSD_PROTO_TCP for TCP ("_tcp") and DNSSD_PROTO_UDP
+ * for others ("_udp")
+ * @param port The port the service listens to
+ * @param dns_ttl Validity time in seconds to send out for service data in DNS replies
+ * @param txt_fn Callback to get TXT data. Will be called each time a TXT reply is created to
+ * allow dynamic replies.
+ * @param txt_data Userdata pointer for txt_fn
+ * @return service_id if the service was added to the netif, an err_t otherwise
+ */
+s8_t
+mdns_resp_add_service(struct netif *netif, const char *name, const char *service, enum mdns_sd_proto proto, u16_t port, u32_t dns_ttl, service_get_txt_fn_t txt_fn, void *txt_data)
+{
+ s8_t i;
+ s8_t slot = -1;
+ struct mdns_service *srv;
+ struct mdns_host *mdns;
+
+ LWIP_ASSERT_CORE_LOCKED();
+ LWIP_ASSERT("mdns_resp_add_service: netif != NULL", netif);
+ mdns = NETIF_TO_HOST(netif);
+ LWIP_ERROR("mdns_resp_add_service: Not an mdns netif", (mdns != NULL), return ERR_VAL);
+
+ LWIP_ERROR("mdns_resp_add_service: Name too long", (strlen(name) <= MDNS_LABEL_MAXLEN), return ERR_VAL);
+ LWIP_ERROR("mdns_resp_add_service: Service too long", (strlen(service) <= MDNS_LABEL_MAXLEN), return ERR_VAL);
+ LWIP_ERROR("mdns_resp_add_service: Bad proto (need TCP or UDP)", (proto == DNSSD_PROTO_TCP || proto == DNSSD_PROTO_UDP), return ERR_VAL);
+
+ for (i = 0; i < MDNS_MAX_SERVICES; i++) {
+ if (mdns->services[i] == NULL) {
+ slot = i;
+ break;
+ }
+ }
+ LWIP_ERROR("mdns_resp_add_service: Service list full (increase MDNS_MAX_SERVICES)", (slot >= 0), return ERR_MEM);
+
+ srv = (struct mdns_service *)mem_calloc(1, sizeof(struct mdns_service));
+ LWIP_ERROR("mdns_resp_add_service: Alloc failed", (srv != NULL), return ERR_MEM);
+
+ MEMCPY(&srv->name, name, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(name)));
+ MEMCPY(&srv->service, service, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(service)));
+ srv->txt_fn = txt_fn;
+ srv->txt_userdata = txt_data;
+ srv->proto = (u16_t)proto;
+ srv->port = port;
+ srv->dns_ttl = dns_ttl;
+
+ mdns->services[slot] = srv;
+
+ mdns_resp_restart(netif);
+
+ return slot;
+}
+
+/**
+ * @ingroup mdns
+ * Delete a service on the selected network interface.
+ * @param netif The network interface on which service should be removed
+ * @param slot The service slot number returned by mdns_resp_add_service
+ * @return ERR_OK if the service was removed from the netif, an err_t otherwise
+ */
+err_t
+mdns_resp_del_service(struct netif *netif, s8_t slot)
+{
+ struct mdns_host *mdns;
+ struct mdns_service *srv;
+ LWIP_ASSERT("mdns_resp_del_service: netif != NULL", netif);
+ mdns = NETIF_TO_HOST(netif);
+ LWIP_ERROR("mdns_resp_del_service: Not an mdns netif", (mdns != NULL), return ERR_VAL);
+ LWIP_ERROR("mdns_resp_del_service: Invalid Service ID", (slot >= 0) && (slot < MDNS_MAX_SERVICES), return ERR_VAL);
+ LWIP_ERROR("mdns_resp_del_service: Invalid Service ID", (mdns->services[slot] != NULL), return ERR_VAL);
+
+ srv = mdns->services[slot];
+ mdns->services[slot] = NULL;
+ mem_free(srv);
+ return ERR_OK;
+}
+
+/**
+ * @ingroup mdns
+ * Update name for an MDNS service.
+ * @param netif The network interface to activate.
+ * @param slot The service slot number returned by mdns_resp_add_service
+ * @param name The new name for the service
+ * @return ERR_OK if name could be set on service, an err_t otherwise
+ */
+err_t
+mdns_resp_rename_service(struct netif *netif, s8_t slot, const char *name)
+{
+ struct mdns_service *srv;
+ struct mdns_host *mdns;
+ size_t len;
+
+ LWIP_ASSERT_CORE_LOCKED();
+ len = strlen(name);
+ LWIP_ASSERT("mdns_resp_rename_service: netif != NULL", netif);
+ mdns = NETIF_TO_HOST(netif);
+ LWIP_ERROR("mdns_resp_rename_service: Not an mdns netif", (mdns != NULL), return ERR_VAL);
+ LWIP_ERROR("mdns_resp_rename_service: Name too long", (len <= MDNS_LABEL_MAXLEN), return ERR_VAL);
+ LWIP_ERROR("mdns_resp_rename_service: Invalid Service ID", (slot >= 0) && (slot < MDNS_MAX_SERVICES), return ERR_VAL);
+ LWIP_ERROR("mdns_resp_rename_service: Invalid Service ID", (mdns->services[slot] != NULL), return ERR_VAL);
+
+ srv = mdns->services[slot];
+
+ MEMCPY(&srv->name, name, LWIP_MIN(MDNS_LABEL_MAXLEN, len));
+ srv->name[len] = '\0'; /* null termination in case new name is shorter than previous */
+
+ mdns_resp_restart(netif);
+
+ return ERR_OK;
+}
+
+/**
+ * @ingroup mdns
+ * Call this function from inside the service_get_txt_fn_t callback to add text data.
+ * Buffer for TXT data is 256 bytes, and each field is prefixed with a length byte.
+ * @param service The service provided to the get_txt callback
+ * @param txt String to add to the TXT field.
+ * @param txt_len Length of string
+ * @return ERR_OK if the string was added to the reply, an err_t otherwise
+ */
+err_t
+mdns_resp_add_service_txtitem(struct mdns_service *service, const char *txt, u8_t txt_len)
+{
+ LWIP_ASSERT_CORE_LOCKED();
+ LWIP_ASSERT("mdns_resp_add_service_txtitem: service != NULL", service);
+
+ /* Use a mdns_domain struct to store txt chunks since it is the same encoding */
+ return mdns_domain_add_label(&service->txtdata, txt, txt_len);
+}
+
+/**
+ * @ingroup mdns
+ * Send unsolicited answer containing all our known data
+ * @param netif The network interface to send on
+ */
+void
+mdns_resp_announce(struct netif *netif)
+{
+ struct mdns_host* mdns;
+ LWIP_ASSERT_CORE_LOCKED();
+ LWIP_ERROR("mdns_resp_announce: netif != NULL", (netif != NULL), return);
+
+ mdns = NETIF_TO_HOST(netif);
+ if (mdns == NULL) {
+ return;
+ }
+
+ if (mdns->probing_state == MDNS_PROBING_COMPLETE) {
+ /* Announce on IPv6 and IPv4 */
+#if LWIP_IPV6
+ mdns_announce(netif, IP6_ADDR_ANY);
+#endif
+#if LWIP_IPV4
+ if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
+ mdns_announce(netif, IP4_ADDR_ANY);
+ }
+#endif
+ } /* else: ip address changed while probing was ongoing? @todo reset counter to restart? */
+}
+
+/** Register a callback function that is called if probing is completed successfully
+ * or with a conflict. */
+void
+mdns_resp_register_name_result_cb(mdns_name_result_cb_t cb)
+{
+ mdns_name_result_cb = cb;
+}
+
+/**
+ * @ingroup mdns
+ * Restart mdns responder. Call this when cable is connected after being disconnected or
+ * administrative interface is set up after being down
+ * @param netif The network interface to send on
+ */
+void
+mdns_resp_restart(struct netif *netif)
+{
+ struct mdns_host* mdns;
+ LWIP_ASSERT_CORE_LOCKED();
+ LWIP_ERROR("mdns_resp_restart: netif != NULL", (netif != NULL), return);
+
+ mdns = NETIF_TO_HOST(netif);
+ if (mdns == NULL) {
+ return;
+ }
+
+ if (mdns->probing_state == MDNS_PROBING_ONGOING) {
+ sys_untimeout(mdns_probe, netif);
+ }
+ /* @todo if we've failed 15 times within a 10 second period we MUST wait 5 seconds (or wait 5 seconds every time except first)*/
+ mdns->probes_sent = 0;
+ mdns->probing_state = MDNS_PROBING_ONGOING;
+ sys_timeout(MDNS_INITIAL_PROBE_DELAY_MS, mdns_probe, netif);
+}
+
+/**
+ * @ingroup mdns
+ * Initiate MDNS responder. Will open UDP sockets on port 5353
+ */
+void
+mdns_resp_init(void)
+{
+ err_t res;
+
+ /* LWIP_ASSERT_CORE_LOCKED(); is checked by udp_new() */
+
+ mdns_pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
+ LWIP_ASSERT("Failed to allocate pcb", mdns_pcb != NULL);
+#if LWIP_MULTICAST_TX_OPTIONS
+ udp_set_multicast_ttl(mdns_pcb, MDNS_TTL);
+#else
+ mdns_pcb->ttl = MDNS_TTL;
+#endif
+ res = udp_bind(mdns_pcb, IP_ANY_TYPE, LWIP_IANA_PORT_MDNS);
+ LWIP_UNUSED_ARG(res); /* in case of LWIP_NOASSERT */
+ LWIP_ASSERT("Failed to bind pcb", res == ERR_OK);
+ udp_recv(mdns_pcb, mdns_recv, NULL);
+
+ mdns_netif_client_id = netif_alloc_client_data_id();
+
+#if MDNS_RESP_USENETIF_EXTCALLBACK
+ /* register for netif events when started on first netif */
+ netif_add_ext_callback(&netif_callback, mdns_netif_ext_status_callback);
+#endif
+}
+
+#endif /* LWIP_MDNS_RESPONDER */
diff --git a/resources/ethernet/LwIP/apps/mqtt/mqtt.c b/resources/ethernet/LwIP/apps/mqtt/mqtt.c
new file mode 100644
index 00000000..f785ebe9
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/mqtt/mqtt.c
@@ -0,0 +1,1463 @@
+/**
+ * @file
+ * MQTT client
+ *
+ * @defgroup mqtt MQTT client
+ * @ingroup apps
+ * @verbinclude mqtt_client.txt
+ */
+
+/*
+ * Copyright (c) 2016 Erik Andersson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack
+ *
+ * Author: Erik Andersson
+ *
+ *
+ * @todo:
+ * - Handle large outgoing payloads for PUBLISH messages
+ * - Fix restriction of a single topic in each (UN)SUBSCRIBE message (protocol has support for multiple topics)
+ * - Add support for legacy MQTT protocol version
+ *
+ * Please coordinate changes and requests with Erik Andersson
+ * Erik Andersson
+ *
+ */
+#include "lwip/apps/mqtt.h"
+#include "lwip/apps/mqtt_priv.h"
+#include "lwip/timeouts.h"
+#include "lwip/ip_addr.h"
+#include "lwip/mem.h"
+#include "lwip/err.h"
+#include "lwip/pbuf.h"
+#include "lwip/altcp.h"
+#include "lwip/altcp_tcp.h"
+#include "lwip/altcp_tls.h"
+#include
+
+#if LWIP_TCP && LWIP_CALLBACK_API
+
+/**
+ * MQTT_DEBUG: Default is off.
+ */
+#if !defined MQTT_DEBUG || defined __DOXYGEN__
+#define MQTT_DEBUG LWIP_DBG_OFF
+#endif
+
+#define MQTT_DEBUG_TRACE (MQTT_DEBUG | LWIP_DBG_TRACE)
+#define MQTT_DEBUG_STATE (MQTT_DEBUG | LWIP_DBG_STATE)
+#define MQTT_DEBUG_WARN (MQTT_DEBUG | LWIP_DBG_LEVEL_WARNING)
+#define MQTT_DEBUG_WARN_STATE (MQTT_DEBUG | LWIP_DBG_LEVEL_WARNING | LWIP_DBG_STATE)
+#define MQTT_DEBUG_SERIOUS (MQTT_DEBUG | LWIP_DBG_LEVEL_SERIOUS)
+
+
+
+/**
+ * MQTT client connection states
+ */
+enum {
+ TCP_DISCONNECTED,
+ TCP_CONNECTING,
+ MQTT_CONNECTING,
+ MQTT_CONNECTED
+};
+
+/**
+ * MQTT control message types
+ */
+enum mqtt_message_type {
+ MQTT_MSG_TYPE_CONNECT = 1,
+ MQTT_MSG_TYPE_CONNACK = 2,
+ MQTT_MSG_TYPE_PUBLISH = 3,
+ MQTT_MSG_TYPE_PUBACK = 4,
+ MQTT_MSG_TYPE_PUBREC = 5,
+ MQTT_MSG_TYPE_PUBREL = 6,
+ MQTT_MSG_TYPE_PUBCOMP = 7,
+ MQTT_MSG_TYPE_SUBSCRIBE = 8,
+ MQTT_MSG_TYPE_SUBACK = 9,
+ MQTT_MSG_TYPE_UNSUBSCRIBE = 10,
+ MQTT_MSG_TYPE_UNSUBACK = 11,
+ MQTT_MSG_TYPE_PINGREQ = 12,
+ MQTT_MSG_TYPE_PINGRESP = 13,
+ MQTT_MSG_TYPE_DISCONNECT = 14
+};
+
+/** Helpers to extract control packet type and qos from first byte in fixed header */
+#define MQTT_CTL_PACKET_TYPE(fixed_hdr_byte0) ((fixed_hdr_byte0 & 0xf0) >> 4)
+#define MQTT_CTL_PACKET_QOS(fixed_hdr_byte0) ((fixed_hdr_byte0 & 0x6) >> 1)
+
+/**
+ * MQTT connect flags, only used in CONNECT message
+ */
+enum mqtt_connect_flag {
+ MQTT_CONNECT_FLAG_USERNAME = 1 << 7,
+ MQTT_CONNECT_FLAG_PASSWORD = 1 << 6,
+ MQTT_CONNECT_FLAG_WILL_RETAIN = 1 << 5,
+ MQTT_CONNECT_FLAG_WILL = 1 << 2,
+ MQTT_CONNECT_FLAG_CLEAN_SESSION = 1 << 1
+};
+
+
+static void mqtt_cyclic_timer(void *arg);
+
+#if defined(LWIP_DEBUG)
+static const char *const mqtt_message_type_str[15] = {
+ "UNDEFINED",
+ "CONNECT",
+ "CONNACK",
+ "PUBLISH",
+ "PUBACK",
+ "PUBREC",
+ "PUBREL",
+ "PUBCOMP",
+ "SUBSCRIBE",
+ "SUBACK",
+ "UNSUBSCRIBE",
+ "UNSUBACK",
+ "PINGREQ",
+ "PINGRESP",
+ "DISCONNECT"
+};
+
+/**
+ * Message type value to string
+ * @param msg_type see enum mqtt_message_type
+ *
+ * @return Control message type text string
+ */
+static const char *
+mqtt_msg_type_to_str(u8_t msg_type)
+{
+ if (msg_type >= LWIP_ARRAYSIZE(mqtt_message_type_str)) {
+ msg_type = 0;
+ }
+ return mqtt_message_type_str[msg_type];
+}
+
+#endif
+
+
+/**
+ * Generate MQTT packet identifier
+ * @param client MQTT client
+ * @return New packet identifier, range 1 to 65535
+ */
+static u16_t
+msg_generate_packet_id(mqtt_client_t *client)
+{
+ client->pkt_id_seq++;
+ if (client->pkt_id_seq == 0) {
+ client->pkt_id_seq++;
+ }
+ return client->pkt_id_seq;
+}
+
+/*--------------------------------------------------------------------------------------------------------------------- */
+/* Output ring buffer */
+
+/** Add single item to ring buffer */
+static void
+mqtt_ringbuf_put(struct mqtt_ringbuf_t *rb, u8_t item)
+{
+ rb->buf[rb->put] = item;
+ rb->put++;
+ if (rb->put >= MQTT_OUTPUT_RINGBUF_SIZE) {
+ rb->put = 0;
+ }
+}
+
+/** Return pointer to ring buffer get position */
+static u8_t *
+mqtt_ringbuf_get_ptr(struct mqtt_ringbuf_t *rb)
+{
+ return &rb->buf[rb->get];
+}
+
+static void
+mqtt_ringbuf_advance_get_idx(struct mqtt_ringbuf_t *rb, u16_t len)
+{
+ LWIP_ASSERT("mqtt_ringbuf_advance_get_idx: len < MQTT_OUTPUT_RINGBUF_SIZE", len < MQTT_OUTPUT_RINGBUF_SIZE);
+
+ rb->get += len;
+ if (rb->get >= MQTT_OUTPUT_RINGBUF_SIZE) {
+ rb->get = rb->get - MQTT_OUTPUT_RINGBUF_SIZE;
+ }
+}
+
+/** Return number of bytes in ring buffer */
+static u16_t
+mqtt_ringbuf_len(struct mqtt_ringbuf_t *rb)
+{
+ u32_t len = rb->put - rb->get;
+ if (len > 0xFFFF) {
+ len += MQTT_OUTPUT_RINGBUF_SIZE;
+ }
+ return (u16_t)len;
+}
+
+/** Return number of bytes free in ring buffer */
+#define mqtt_ringbuf_free(rb) (MQTT_OUTPUT_RINGBUF_SIZE - mqtt_ringbuf_len(rb))
+
+/** Return number of bytes possible to read without wrapping around */
+#define mqtt_ringbuf_linear_read_length(rb) LWIP_MIN(mqtt_ringbuf_len(rb), (MQTT_OUTPUT_RINGBUF_SIZE - (rb)->get))
+
+/**
+ * Try send as many bytes as possible from output ring buffer
+ * @param rb Output ring buffer
+ * @param tpcb TCP connection handle
+ */
+static void
+mqtt_output_send(struct mqtt_ringbuf_t *rb, struct altcp_pcb *tpcb)
+{
+ err_t err;
+ u8_t wrap = 0;
+ u16_t ringbuf_lin_len = mqtt_ringbuf_linear_read_length(rb);
+ u16_t send_len = altcp_sndbuf(tpcb);
+ LWIP_ASSERT("mqtt_output_send: tpcb != NULL", tpcb != NULL);
+
+ if (send_len == 0 || ringbuf_lin_len == 0) {
+ return;
+ }
+
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_output_send: tcp_sndbuf: %d bytes, ringbuf_linear_available: %d, get %d, put %d\n",
+ send_len, ringbuf_lin_len, rb->get, rb->put));
+
+ if (send_len > ringbuf_lin_len) {
+ /* Space in TCP output buffer is larger than available in ring buffer linear portion */
+ send_len = ringbuf_lin_len;
+ /* Wrap around if more data in ring buffer after linear portion */
+ wrap = (mqtt_ringbuf_len(rb) > ringbuf_lin_len);
+ }
+ err = altcp_write(tpcb, mqtt_ringbuf_get_ptr(rb), send_len, TCP_WRITE_FLAG_COPY | (wrap ? TCP_WRITE_FLAG_MORE : 0));
+ if ((err == ERR_OK) && wrap) {
+ mqtt_ringbuf_advance_get_idx(rb, send_len);
+ /* Use the lesser one of ring buffer linear length and TCP send buffer size */
+ send_len = LWIP_MIN(altcp_sndbuf(tpcb), mqtt_ringbuf_linear_read_length(rb));
+ err = altcp_write(tpcb, mqtt_ringbuf_get_ptr(rb), send_len, TCP_WRITE_FLAG_COPY);
+ }
+
+ if (err == ERR_OK) {
+ mqtt_ringbuf_advance_get_idx(rb, send_len);
+ /* Flush */
+ altcp_output(tpcb);
+ } else {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_output_send: Send failed with err %d (\"%s\")\n", err, lwip_strerr(err)));
+ }
+}
+
+
+
+/*--------------------------------------------------------------------------------------------------------------------- */
+/* Request queue */
+
+/**
+ * Create request item
+ * @param r_objs Pointer to request objects
+ * @param r_objs_len Number of array entries
+ * @param pkt_id Packet identifier of request
+ * @param cb Packet callback to call when requests lifetime ends
+ * @param arg Parameter following callback
+ * @return Request or NULL if failed to create
+ */
+static struct mqtt_request_t *
+mqtt_create_request(struct mqtt_request_t *r_objs, size_t r_objs_len, u16_t pkt_id, mqtt_request_cb_t cb, void *arg)
+{
+ struct mqtt_request_t *r = NULL;
+ u8_t n;
+ LWIP_ASSERT("mqtt_create_request: r_objs != NULL", r_objs != NULL);
+ for (n = 0; n < r_objs_len; n++) {
+ /* Item point to itself if not in use */
+ if (r_objs[n].next == &r_objs[n]) {
+ r = &r_objs[n];
+ r->next = NULL;
+ r->cb = cb;
+ r->arg = arg;
+ r->pkt_id = pkt_id;
+ break;
+ }
+ }
+ return r;
+}
+
+
+/**
+ * Append request to pending request queue
+ * @param tail Pointer to request queue tail pointer
+ * @param r Request to append
+ */
+static void
+mqtt_append_request(struct mqtt_request_t **tail, struct mqtt_request_t *r)
+{
+ struct mqtt_request_t *head = NULL;
+ s16_t time_before = 0;
+ struct mqtt_request_t *iter;
+
+ LWIP_ASSERT("mqtt_append_request: tail != NULL", tail != NULL);
+
+ /* Iterate trough queue to find head, and count total timeout time */
+ for (iter = *tail; iter != NULL; iter = iter->next) {
+ time_before += iter->timeout_diff;
+ head = iter;
+ }
+
+ LWIP_ASSERT("mqtt_append_request: time_before <= MQTT_REQ_TIMEOUT", time_before <= MQTT_REQ_TIMEOUT);
+ r->timeout_diff = MQTT_REQ_TIMEOUT - time_before;
+ if (head == NULL) {
+ *tail = r;
+ } else {
+ head->next = r;
+ }
+}
+
+
+/**
+ * Delete request item
+ * @param r Request item to delete
+ */
+static void
+mqtt_delete_request(struct mqtt_request_t *r)
+{
+ if (r != NULL) {
+ r->next = r;
+ }
+}
+
+/**
+ * Remove a request item with a specific packet identifier from request queue
+ * @param tail Pointer to request queue tail pointer
+ * @param pkt_id Packet identifier of request to take
+ * @return Request item if found, NULL if not
+ */
+static struct mqtt_request_t *
+mqtt_take_request(struct mqtt_request_t **tail, u16_t pkt_id)
+{
+ struct mqtt_request_t *iter = NULL, *prev = NULL;
+ LWIP_ASSERT("mqtt_take_request: tail != NULL", tail != NULL);
+ /* Search all request for pkt_id */
+ for (iter = *tail; iter != NULL; iter = iter->next) {
+ if (iter->pkt_id == pkt_id) {
+ break;
+ }
+ prev = iter;
+ }
+
+ /* If request was found */
+ if (iter != NULL) {
+ /* unchain */
+ if (prev == NULL) {
+ *tail = iter->next;
+ } else {
+ prev->next = iter->next;
+ }
+ /* If exists, add remaining timeout time for the request to next */
+ if (iter->next != NULL) {
+ iter->next->timeout_diff += iter->timeout_diff;
+ }
+ iter->next = NULL;
+ }
+ return iter;
+}
+
+/**
+ * Handle requests timeout
+ * @param tail Pointer to request queue tail pointer
+ * @param t Time since last call in seconds
+ */
+static void
+mqtt_request_time_elapsed(struct mqtt_request_t **tail, u8_t t)
+{
+ struct mqtt_request_t *r;
+ LWIP_ASSERT("mqtt_request_time_elapsed: tail != NULL", tail != NULL);
+ r = *tail;
+ while (t > 0 && r != NULL) {
+ if (t >= r->timeout_diff) {
+ t -= (u8_t)r->timeout_diff;
+ /* Unchain */
+ *tail = r->next;
+ /* Notify upper layer about timeout */
+ if (r->cb != NULL) {
+ r->cb(r->arg, ERR_TIMEOUT);
+ }
+ mqtt_delete_request(r);
+ /* Tail might be be modified in callback, so re-read it in every iteration */
+ r = *(struct mqtt_request_t *const volatile *)tail;
+ } else {
+ r->timeout_diff -= t;
+ t = 0;
+ }
+ }
+}
+
+/**
+ * Free all request items
+ * @param tail Pointer to request queue tail pointer
+ */
+static void
+mqtt_clear_requests(struct mqtt_request_t **tail)
+{
+ struct mqtt_request_t *iter, *next;
+ LWIP_ASSERT("mqtt_clear_requests: tail != NULL", tail != NULL);
+ for (iter = *tail; iter != NULL; iter = next) {
+ next = iter->next;
+ mqtt_delete_request(iter);
+ }
+ *tail = NULL;
+}
+/**
+ * Initialize all request items
+ * @param r_objs Pointer to request objects
+ * @param r_objs_len Number of array entries
+ */
+static void
+mqtt_init_requests(struct mqtt_request_t *r_objs, size_t r_objs_len)
+{
+ u8_t n;
+ LWIP_ASSERT("mqtt_init_requests: r_objs != NULL", r_objs != NULL);
+ for (n = 0; n < r_objs_len; n++) {
+ /* Item pointing to itself indicates unused */
+ r_objs[n].next = &r_objs[n];
+ }
+}
+
+/*--------------------------------------------------------------------------------------------------------------------- */
+/* Output message build helpers */
+
+
+static void
+mqtt_output_append_u8(struct mqtt_ringbuf_t *rb, u8_t value)
+{
+ mqtt_ringbuf_put(rb, value);
+}
+
+static
+void mqtt_output_append_u16(struct mqtt_ringbuf_t *rb, u16_t value)
+{
+ mqtt_ringbuf_put(rb, value >> 8);
+ mqtt_ringbuf_put(rb, value & 0xff);
+}
+
+static void
+mqtt_output_append_buf(struct mqtt_ringbuf_t *rb, const void *data, u16_t length)
+{
+ u16_t n;
+ for (n = 0; n < length; n++) {
+ mqtt_ringbuf_put(rb, ((const u8_t *)data)[n]);
+ }
+}
+
+static void
+mqtt_output_append_string(struct mqtt_ringbuf_t *rb, const char *str, u16_t length)
+{
+ u16_t n;
+ mqtt_ringbuf_put(rb, length >> 8);
+ mqtt_ringbuf_put(rb, length & 0xff);
+ for (n = 0; n < length; n++) {
+ mqtt_ringbuf_put(rb, str[n]);
+ }
+}
+
+/**
+ * Append fixed header
+ * @param rb Output ring buffer
+ * @param msg_type see enum mqtt_message_type
+ * @param fdup MQTT DUP flag
+ * @param fqos MQTT QoS field
+ * @param fretain MQTT retain flag
+ * @param r_length Remaining length after fixed header
+ */
+
+static void
+mqtt_output_append_fixed_header(struct mqtt_ringbuf_t *rb, u8_t msg_type, u8_t fdup,
+ u8_t fqos, u8_t fretain, u16_t r_length)
+{
+ /* Start with control byte */
+ mqtt_output_append_u8(rb, (((msg_type & 0x0f) << 4) | ((fdup & 1) << 3) | ((fqos & 3) << 1) | (fretain & 1)));
+ /* Encode remaining length field */
+ do {
+ mqtt_output_append_u8(rb, (r_length & 0x7f) | (r_length >= 128 ? 0x80 : 0));
+ r_length >>= 7;
+ } while (r_length > 0);
+}
+
+
+/**
+ * Check output buffer space
+ * @param rb Output ring buffer
+ * @param r_length Remaining length after fixed header
+ * @return 1 if message will fit, 0 if not enough buffer space
+ */
+static u8_t
+mqtt_output_check_space(struct mqtt_ringbuf_t *rb, u16_t r_length)
+{
+ /* Start with length of type byte + remaining length */
+ u16_t total_len = 1 + r_length;
+
+ LWIP_ASSERT("mqtt_output_check_space: rb != NULL", rb != NULL);
+
+ /* Calculate number of required bytes to contain the remaining bytes field and add to total*/
+ do {
+ total_len++;
+ r_length >>= 7;
+ } while (r_length > 0);
+
+ return (total_len <= mqtt_ringbuf_free(rb));
+}
+
+
+/**
+ * Close connection to server
+ * @param client MQTT client
+ * @param reason Reason for disconnection
+ */
+static void
+mqtt_close(mqtt_client_t *client, mqtt_connection_status_t reason)
+{
+ LWIP_ASSERT("mqtt_close: client != NULL", client != NULL);
+
+ /* Bring down TCP connection if not already done */
+ if (client->conn != NULL) {
+ err_t res;
+ altcp_recv(client->conn, NULL);
+ altcp_err(client->conn, NULL);
+ altcp_sent(client->conn, NULL);
+ res = altcp_close(client->conn);
+ if (res != ERR_OK) {
+ altcp_abort(client->conn);
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_close: Close err=%s\n", lwip_strerr(res)));
+ }
+ client->conn = NULL;
+ }
+
+ /* Remove all pending requests */
+ mqtt_clear_requests(&client->pend_req_queue);
+ /* Stop cyclic timer */
+ sys_untimeout(mqtt_cyclic_timer, client);
+
+ /* Notify upper layer of disconnection if changed state */
+ if (client->conn_state != TCP_DISCONNECTED) {
+
+ client->conn_state = TCP_DISCONNECTED;
+ if (client->connect_cb != NULL) {
+ client->connect_cb(client, client->connect_arg, reason);
+ }
+ }
+}
+
+
+/**
+ * Interval timer, called every MQTT_CYCLIC_TIMER_INTERVAL seconds in MQTT_CONNECTING and MQTT_CONNECTED states
+ * @param arg MQTT client
+ */
+static void
+mqtt_cyclic_timer(void *arg)
+{
+ u8_t restart_timer = 1;
+ mqtt_client_t *client = (mqtt_client_t *)arg;
+ LWIP_ASSERT("mqtt_cyclic_timer: client != NULL", client != NULL);
+
+ if (client->conn_state == MQTT_CONNECTING) {
+ client->cyclic_tick++;
+ if ((client->cyclic_tick * MQTT_CYCLIC_TIMER_INTERVAL) >= MQTT_CONNECT_TIMOUT) {
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_cyclic_timer: CONNECT attempt to server timed out\n"));
+ /* Disconnect TCP */
+ mqtt_close(client, MQTT_CONNECT_TIMEOUT);
+ restart_timer = 0;
+ }
+ } else if (client->conn_state == MQTT_CONNECTED) {
+ /* Handle timeout for pending requests */
+ mqtt_request_time_elapsed(&client->pend_req_queue, MQTT_CYCLIC_TIMER_INTERVAL);
+
+ /* keep_alive > 0 means keep alive functionality shall be used */
+ if (client->keep_alive > 0) {
+
+ client->server_watchdog++;
+ /* If reception from server has been idle for 1.5*keep_alive time, server is considered unresponsive */
+ if ((client->server_watchdog * MQTT_CYCLIC_TIMER_INTERVAL) > (client->keep_alive + client->keep_alive / 2)) {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_cyclic_timer: Server incoming keep-alive timeout\n"));
+ mqtt_close(client, MQTT_CONNECT_TIMEOUT);
+ restart_timer = 0;
+ }
+
+ /* If time for a keep alive message to be sent, transmission has been idle for keep_alive time */
+ if ((client->cyclic_tick * MQTT_CYCLIC_TIMER_INTERVAL) >= client->keep_alive) {
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_cyclic_timer: Sending keep-alive message to server\n"));
+ if (mqtt_output_check_space(&client->output, 0) != 0) {
+ mqtt_output_append_fixed_header(&client->output, MQTT_MSG_TYPE_PINGREQ, 0, 0, 0, 0);
+ client->cyclic_tick = 0;
+ }
+ } else {
+ client->cyclic_tick++;
+ }
+ }
+ } else {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_cyclic_timer: Timer should not be running in state %d\n", client->conn_state));
+ restart_timer = 0;
+ }
+ if (restart_timer) {
+ sys_timeout(MQTT_CYCLIC_TIMER_INTERVAL * 1000, mqtt_cyclic_timer, arg);
+ }
+}
+
+
+/**
+ * Send PUBACK, PUBREC or PUBREL response message
+ * @param client MQTT client
+ * @param msg PUBACK, PUBREC or PUBREL
+ * @param pkt_id Packet identifier
+ * @param qos QoS value
+ * @return ERR_OK if successful, ERR_MEM if out of memory
+ */
+static err_t
+pub_ack_rec_rel_response(mqtt_client_t *client, u8_t msg, u16_t pkt_id, u8_t qos)
+{
+ err_t err = ERR_OK;
+ if (mqtt_output_check_space(&client->output, 2)) {
+ mqtt_output_append_fixed_header(&client->output, msg, 0, qos, 0, 2);
+ mqtt_output_append_u16(&client->output, pkt_id);
+ mqtt_output_send(&client->output, client->conn);
+ } else {
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("pub_ack_rec_rel_response: OOM creating response: %s with pkt_id: %d\n",
+ mqtt_msg_type_to_str(msg), pkt_id));
+ err = ERR_MEM;
+ }
+ return err;
+}
+
+/**
+ * Subscribe response from server
+ * @param r Matching request
+ * @param result Result code from server
+ */
+static void
+mqtt_incomming_suback(struct mqtt_request_t *r, u8_t result)
+{
+ if (r->cb != NULL) {
+ r->cb(r->arg, result < 3 ? ERR_OK : ERR_ABRT);
+ }
+}
+
+
+/**
+ * Complete MQTT message received or buffer full
+ * @param client MQTT client
+ * @param fixed_hdr_idx header index
+ * @param length length received part
+ * @param remaining_length Remaining length of complete message
+ */
+static mqtt_connection_status_t
+mqtt_message_received(mqtt_client_t *client, u8_t fixed_hdr_idx, u16_t length, u32_t remaining_length)
+{
+ mqtt_connection_status_t res = MQTT_CONNECT_ACCEPTED;
+
+ u8_t *var_hdr_payload = client->rx_buffer + fixed_hdr_idx;
+ size_t var_hdr_payload_bufsize = sizeof(client->rx_buffer) - fixed_hdr_idx;
+
+ /* Control packet type */
+ u8_t pkt_type = MQTT_CTL_PACKET_TYPE(client->rx_buffer[0]);
+ u16_t pkt_id = 0;
+
+ LWIP_ASSERT("client->msg_idx < MQTT_VAR_HEADER_BUFFER_LEN", client->msg_idx < MQTT_VAR_HEADER_BUFFER_LEN);
+ LWIP_ASSERT("fixed_hdr_idx <= client->msg_idx", fixed_hdr_idx <= client->msg_idx);
+ LWIP_ERROR("buffer length mismatch", fixed_hdr_idx + length <= MQTT_VAR_HEADER_BUFFER_LEN,
+ return MQTT_CONNECT_DISCONNECTED);
+
+ if (pkt_type == MQTT_MSG_TYPE_CONNACK) {
+ if (client->conn_state == MQTT_CONNECTING) {
+ if (length < 2) {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN,( "mqtt_message_received: Received short CONNACK message\n"));
+ goto out_disconnect;
+ }
+ /* Get result code from CONNACK */
+ res = (mqtt_connection_status_t)var_hdr_payload[1];
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_message_received: Connect response code %d\n", res));
+ if (res == MQTT_CONNECT_ACCEPTED) {
+ /* Reset cyclic_tick when changing to connected state */
+ client->cyclic_tick = 0;
+ client->conn_state = MQTT_CONNECTED;
+ /* Notify upper layer */
+ if (client->connect_cb != 0) {
+ client->connect_cb(client, client->connect_arg, res);
+ }
+ }
+ } else {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_message_received: Received CONNACK in connected state\n"));
+ }
+ } else if (pkt_type == MQTT_MSG_TYPE_PINGRESP) {
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE, ( "mqtt_message_received: Received PINGRESP from server\n"));
+
+ } else if (pkt_type == MQTT_MSG_TYPE_PUBLISH) {
+ u16_t payload_offset = 0;
+ u16_t payload_length = length;
+ u8_t qos = MQTT_CTL_PACKET_QOS(client->rx_buffer[0]);
+
+ if (client->msg_idx <= MQTT_VAR_HEADER_BUFFER_LEN) {
+ /* Should have topic and pkt id*/
+ u8_t *topic;
+ u16_t after_topic;
+ u8_t bkp;
+ u16_t topic_len;
+ u16_t qos_len = (qos ? 2U : 0U);
+ if (length < 2 + qos_len) {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN,( "mqtt_message_received: Received short PUBLISH packet\n"));
+ goto out_disconnect;
+ }
+ topic_len = var_hdr_payload[0];
+ topic_len = (topic_len << 8) + (u16_t)(var_hdr_payload[1]);
+ if ((topic_len > length - (2 + qos_len)) ||
+ (topic_len > var_hdr_payload_bufsize - (2 + qos_len))) {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN,( "mqtt_message_received: Received short PUBLISH packet (topic)\n"));
+ goto out_disconnect;
+ }
+
+ topic = var_hdr_payload + 2;
+ after_topic = 2 + topic_len;
+ /* Check buffer length, add one byte even for QoS 0 so that zero termination will fit */
+ if ((after_topic + (qos ? 2U : 1U)) > var_hdr_payload_bufsize) {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_message_received: Receive buffer can not fit topic + pkt_id\n"));
+ goto out_disconnect;
+ }
+
+ /* id for QoS 1 and 2 */
+ if (qos > 0) {
+ if (length < after_topic + 2U) {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN,( "mqtt_message_received: Received short PUBLISH packet (after_topic)\n"));
+ goto out_disconnect;
+ }
+ client->inpub_pkt_id = ((u16_t)var_hdr_payload[after_topic] << 8) + (u16_t)var_hdr_payload[after_topic + 1];
+ after_topic += 2;
+ } else {
+ client->inpub_pkt_id = 0;
+ }
+ /* Take backup of byte after topic */
+ bkp = topic[topic_len];
+ /* Zero terminate string */
+ topic[topic_len] = 0;
+ /* Payload data remaining in receive buffer */
+ payload_length = length - after_topic;
+ payload_offset = after_topic;
+
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_incomming_publish: Received message with QoS %d at topic: %s, payload length %"U32_F"\n",
+ qos, topic, remaining_length + payload_length));
+ if (client->pub_cb != NULL) {
+ client->pub_cb(client->inpub_arg, (const char *)topic, remaining_length + payload_length);
+ }
+ /* Restore byte after topic */
+ topic[topic_len] = bkp;
+ }
+ if (payload_length > 0 || remaining_length == 0) {
+ if (length < (size_t)(payload_offset + payload_length)) {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN,( "mqtt_message_received: Received short packet (payload)\n"));
+ goto out_disconnect;
+ }
+ client->data_cb(client->inpub_arg, var_hdr_payload + payload_offset, payload_length, remaining_length == 0 ? MQTT_DATA_FLAG_LAST : 0);
+ /* Reply if QoS > 0 */
+ if (remaining_length == 0 && qos > 0) {
+ /* Send PUBACK for QoS 1 or PUBREC for QoS 2 */
+ u8_t resp_msg = (qos == 1) ? MQTT_MSG_TYPE_PUBACK : MQTT_MSG_TYPE_PUBREC;
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_incomming_publish: Sending publish response: %s with pkt_id: %d\n",
+ mqtt_msg_type_to_str(resp_msg), client->inpub_pkt_id));
+ pub_ack_rec_rel_response(client, resp_msg, client->inpub_pkt_id, 0);
+ }
+ }
+ } else {
+ /* Get packet identifier */
+ pkt_id = (u16_t)var_hdr_payload[0] << 8;
+ pkt_id |= (u16_t)var_hdr_payload[1];
+ if (pkt_id == 0) {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_message_received: Got message with illegal packet identifier: 0\n"));
+ goto out_disconnect;
+ }
+ if (pkt_type == MQTT_MSG_TYPE_PUBREC) {
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_message_received: PUBREC, sending PUBREL with pkt_id: %d\n", pkt_id));
+ pub_ack_rec_rel_response(client, MQTT_MSG_TYPE_PUBREL, pkt_id, 1);
+
+ } else if (pkt_type == MQTT_MSG_TYPE_PUBREL) {
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_message_received: PUBREL, sending PUBCOMP response with pkt_id: %d\n", pkt_id));
+ pub_ack_rec_rel_response(client, MQTT_MSG_TYPE_PUBCOMP, pkt_id, 0);
+
+ } else if (pkt_type == MQTT_MSG_TYPE_SUBACK || pkt_type == MQTT_MSG_TYPE_UNSUBACK ||
+ pkt_type == MQTT_MSG_TYPE_PUBCOMP || pkt_type == MQTT_MSG_TYPE_PUBACK) {
+ struct mqtt_request_t *r = mqtt_take_request(&client->pend_req_queue, pkt_id);
+ if (r != NULL) {
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_message_received: %s response with id %d\n", mqtt_msg_type_to_str(pkt_type), pkt_id));
+ if (pkt_type == MQTT_MSG_TYPE_SUBACK) {
+ if (length < 3) {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_message_received: To small SUBACK packet\n"));
+ goto out_disconnect;
+ } else {
+ mqtt_incomming_suback(r, var_hdr_payload[2]);
+ }
+ } else if (r->cb != NULL) {
+ r->cb(r->arg, ERR_OK);
+ }
+ mqtt_delete_request(r);
+ } else {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN, ( "mqtt_message_received: Received %s reply, with wrong pkt_id: %d\n", mqtt_msg_type_to_str(pkt_type), pkt_id));
+ }
+ } else {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN, ( "mqtt_message_received: Received unknown message type: %d\n", pkt_type));
+ goto out_disconnect;
+ }
+ }
+ return res;
+out_disconnect:
+ return MQTT_CONNECT_DISCONNECTED;
+}
+
+
+/**
+ * MQTT incoming message parser
+ * @param client MQTT client
+ * @param p PBUF chain of received data
+ * @return Connection status
+ */
+static mqtt_connection_status_t
+mqtt_parse_incoming(mqtt_client_t *client, struct pbuf *p)
+{
+ u16_t in_offset = 0;
+ u32_t msg_rem_len = 0;
+ u8_t fixed_hdr_idx = 0;
+ u8_t b = 0;
+
+ while (p->tot_len > in_offset) {
+ /* We ALWAYS parse the header here first. Even if the header was not
+ included in this segment, we re-parse it here by buffering it in
+ client->rx_buffer. client->msg_idx keeps track of this. */
+ if ((fixed_hdr_idx < 2) || ((b & 0x80) != 0)) {
+
+ if (fixed_hdr_idx < client->msg_idx) {
+ /* parse header from old pbuf (buffered in client->rx_buffer) */
+ b = client->rx_buffer[fixed_hdr_idx];
+ } else {
+ /* parse header from this pbuf and save it in client->rx_buffer in case
+ it comes in segmented */
+ b = pbuf_get_at(p, in_offset++);
+ client->rx_buffer[client->msg_idx++] = b;
+ }
+ fixed_hdr_idx++;
+
+ if (fixed_hdr_idx >= 2) {
+ /* fixed header contains at least 2 bytes but can contain more, depending on
+ 'remaining length'. All bytes but the last of this have 0x80 set to
+ indicate more bytes are coming. */
+ msg_rem_len |= (u32_t)(b & 0x7f) << ((fixed_hdr_idx - 2) * 7);
+ if ((b & 0x80) == 0) {
+ /* fixed header is done */
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_parse_incoming: Remaining length after fixed header: %"U32_F"\n", msg_rem_len));
+ if (msg_rem_len == 0) {
+ /* Complete message with no extra headers of payload received */
+ mqtt_message_received(client, fixed_hdr_idx, 0, 0);
+ client->msg_idx = 0;
+ fixed_hdr_idx = 0;
+ } else {
+ /* Bytes remaining in message (changes remaining length if this is
+ not the first segment of this message) */
+ msg_rem_len = (msg_rem_len + fixed_hdr_idx) - client->msg_idx;
+ }
+ }
+ }
+ } else {
+ /* Fixed header has been parsed, parse variable header */
+ u16_t cpy_len, cpy_start, buffer_space;
+
+ cpy_start = (client->msg_idx - fixed_hdr_idx) % (MQTT_VAR_HEADER_BUFFER_LEN - fixed_hdr_idx) + fixed_hdr_idx;
+
+ /* Allow to copy the lesser one of available length in input data or bytes remaining in message */
+ cpy_len = (u16_t)LWIP_MIN((u16_t)(p->tot_len - in_offset), msg_rem_len);
+
+ /* Limit to available space in buffer */
+ buffer_space = MQTT_VAR_HEADER_BUFFER_LEN - cpy_start;
+ if (cpy_len > buffer_space) {
+ cpy_len = buffer_space;
+ }
+ pbuf_copy_partial(p, client->rx_buffer + cpy_start, cpy_len, in_offset);
+
+ /* Advance get and put indexes */
+ client->msg_idx += cpy_len;
+ in_offset += cpy_len;
+ msg_rem_len -= cpy_len;
+
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_parse_incoming: msg_idx: %"U32_F", cpy_len: %"U16_F", remaining %"U32_F"\n", client->msg_idx, cpy_len, msg_rem_len));
+ if ((msg_rem_len == 0) || (cpy_len == buffer_space)) {
+ /* Whole message received or buffer is full */
+ mqtt_connection_status_t res = mqtt_message_received(client, fixed_hdr_idx, (cpy_start + cpy_len) - fixed_hdr_idx, msg_rem_len);
+ if (res != MQTT_CONNECT_ACCEPTED) {
+ return res;
+ }
+ if (msg_rem_len == 0) {
+ /* Reset parser state */
+ client->msg_idx = 0;
+ /* msg_tot_len = 0; */
+ fixed_hdr_idx = 0;
+ }
+ }
+ }
+ }
+ return MQTT_CONNECT_ACCEPTED;
+}
+
+
+/**
+ * TCP received callback function. @see tcp_recv_fn
+ * @param arg MQTT client
+ * @param p PBUF chain of received data
+ * @param err Passed as return value if not ERR_OK
+ * @return ERR_OK or err passed into callback
+ */
+static err_t
+mqtt_tcp_recv_cb(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err)
+{
+ mqtt_client_t *client = (mqtt_client_t *)arg;
+ LWIP_ASSERT("mqtt_tcp_recv_cb: client != NULL", client != NULL);
+ LWIP_ASSERT("mqtt_tcp_recv_cb: client->conn == pcb", client->conn == pcb);
+
+ if (p == NULL) {
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_tcp_recv_cb: Recv pbuf=NULL, remote has closed connection\n"));
+ mqtt_close(client, MQTT_CONNECT_DISCONNECTED);
+ } else {
+ mqtt_connection_status_t res;
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_tcp_recv_cb: Recv err=%d\n", err));
+ pbuf_free(p);
+ return err;
+ }
+
+ /* Tell remote that data has been received */
+ altcp_recved(pcb, p->tot_len);
+ res = mqtt_parse_incoming(client, p);
+ pbuf_free(p);
+
+ if (res != MQTT_CONNECT_ACCEPTED) {
+ mqtt_close(client, res);
+ }
+ /* If keep alive functionality is used */
+ if (client->keep_alive != 0) {
+ /* Reset server alive watchdog */
+ client->server_watchdog = 0;
+ }
+
+ }
+ return ERR_OK;
+}
+
+
+/**
+ * TCP data sent callback function. @see tcp_sent_fn
+ * @param arg MQTT client
+ * @param tpcb TCP connection handle
+ * @param len Number of bytes sent
+ * @return ERR_OK
+ */
+static err_t
+mqtt_tcp_sent_cb(void *arg, struct altcp_pcb *tpcb, u16_t len)
+{
+ mqtt_client_t *client = (mqtt_client_t *)arg;
+
+ LWIP_UNUSED_ARG(tpcb);
+ LWIP_UNUSED_ARG(len);
+
+ if (client->conn_state == MQTT_CONNECTED) {
+ struct mqtt_request_t *r;
+
+ /* Reset keep-alive send timer and server watchdog */
+ client->cyclic_tick = 0;
+ client->server_watchdog = 0;
+ /* QoS 0 publish has no response from server, so call its callbacks here */
+ while ((r = mqtt_take_request(&client->pend_req_queue, 0)) != NULL) {
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_tcp_sent_cb: Calling QoS 0 publish complete callback\n"));
+ if (r->cb != NULL) {
+ r->cb(r->arg, ERR_OK);
+ }
+ mqtt_delete_request(r);
+ }
+ /* Try send any remaining buffers from output queue */
+ mqtt_output_send(&client->output, client->conn);
+ }
+ return ERR_OK;
+}
+
+/**
+ * TCP error callback function. @see tcp_err_fn
+ * @param arg MQTT client
+ * @param err Error encountered
+ */
+static void
+mqtt_tcp_err_cb(void *arg, err_t err)
+{
+ mqtt_client_t *client = (mqtt_client_t *)arg;
+ LWIP_UNUSED_ARG(err); /* only used for debug output */
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_tcp_err_cb: TCP error callback: error %d, arg: %p\n", err, arg));
+ LWIP_ASSERT("mqtt_tcp_err_cb: client != NULL", client != NULL);
+ /* Set conn to null before calling close as pcb is already deallocated*/
+ client->conn = 0;
+ mqtt_close(client, MQTT_CONNECT_DISCONNECTED);
+}
+
+/**
+ * TCP poll callback function. @see tcp_poll_fn
+ * @param arg MQTT client
+ * @param tpcb TCP connection handle
+ * @return err ERR_OK
+ */
+static err_t
+mqtt_tcp_poll_cb(void *arg, struct altcp_pcb *tpcb)
+{
+ mqtt_client_t *client = (mqtt_client_t *)arg;
+ if (client->conn_state == MQTT_CONNECTED) {
+ /* Try send any remaining buffers from output queue */
+ mqtt_output_send(&client->output, tpcb);
+ }
+ return ERR_OK;
+}
+
+/**
+ * TCP connect callback function. @see tcp_connected_fn
+ * @param arg MQTT client
+ * @param err Always ERR_OK, mqtt_tcp_err_cb is called in case of error
+ * @return ERR_OK
+ */
+static err_t
+mqtt_tcp_connect_cb(void *arg, struct altcp_pcb *tpcb, err_t err)
+{
+ mqtt_client_t *client = (mqtt_client_t *)arg;
+
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_tcp_connect_cb: TCP connect error %d\n", err));
+ return err;
+ }
+
+ /* Initiate receiver state */
+ client->msg_idx = 0;
+
+ /* Setup TCP callbacks */
+ altcp_recv(tpcb, mqtt_tcp_recv_cb);
+ altcp_sent(tpcb, mqtt_tcp_sent_cb);
+ altcp_poll(tpcb, mqtt_tcp_poll_cb, 2);
+
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_tcp_connect_cb: TCP connection established to server\n"));
+ /* Enter MQTT connect state */
+ client->conn_state = MQTT_CONNECTING;
+
+ /* Start cyclic timer */
+ sys_timeout(MQTT_CYCLIC_TIMER_INTERVAL * 1000, mqtt_cyclic_timer, client);
+ client->cyclic_tick = 0;
+
+ /* Start transmission from output queue, connect message is the first one out*/
+ mqtt_output_send(&client->output, client->conn);
+
+ return ERR_OK;
+}
+
+
+
+/*---------------------------------------------------------------------------------------------------- */
+/* Public API */
+
+
+/**
+ * @ingroup mqtt
+ * MQTT publish function.
+ * @param client MQTT client
+ * @param topic Publish topic string
+ * @param payload Data to publish (NULL is allowed)
+ * @param payload_length Length of payload (0 is allowed)
+ * @param qos Quality of service, 0 1 or 2
+ * @param retain MQTT retain flag
+ * @param cb Callback to call when publish is complete or has timed out
+ * @param arg User supplied argument to publish callback
+ * @return ERR_OK if successful
+ * ERR_CONN if client is disconnected
+ * ERR_MEM if short on memory
+ */
+err_t
+mqtt_publish(mqtt_client_t *client, const char *topic, const void *payload, u16_t payload_length, u8_t qos, u8_t retain,
+ mqtt_request_cb_t cb, void *arg)
+{
+ struct mqtt_request_t *r;
+ u16_t pkt_id;
+ size_t topic_strlen;
+ size_t total_len;
+ u16_t topic_len;
+ u16_t remaining_length;
+
+ LWIP_ASSERT_CORE_LOCKED();
+ LWIP_ASSERT("mqtt_publish: client != NULL", client);
+ LWIP_ASSERT("mqtt_publish: topic != NULL", topic);
+ LWIP_ERROR("mqtt_publish: TCP disconnected", (client->conn_state != TCP_DISCONNECTED), return ERR_CONN);
+
+ topic_strlen = strlen(topic);
+ LWIP_ERROR("mqtt_publish: topic length overflow", (topic_strlen <= (0xFFFF - 2)), return ERR_ARG);
+ topic_len = (u16_t)topic_strlen;
+ total_len = 2 + topic_len + payload_length;
+
+ if (qos > 0) {
+ total_len += 2;
+ /* Generate pkt_id id for QoS1 and 2 */
+ pkt_id = msg_generate_packet_id(client);
+ } else {
+ /* Use reserved value pkt_id 0 for QoS 0 in request handle */
+ pkt_id = 0;
+ }
+ LWIP_ERROR("mqtt_publish: total length overflow", (total_len <= 0xFFFF), return ERR_ARG);
+ remaining_length = (u16_t)total_len;
+
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_publish: Publish with payload length %d to topic \"%s\"\n", payload_length, topic));
+
+ r = mqtt_create_request(client->req_list, LWIP_ARRAYSIZE(client->req_list), pkt_id, cb, arg);
+ if (r == NULL) {
+ return ERR_MEM;
+ }
+
+ if (mqtt_output_check_space(&client->output, remaining_length) == 0) {
+ mqtt_delete_request(r);
+ return ERR_MEM;
+ }
+ /* Append fixed header */
+ mqtt_output_append_fixed_header(&client->output, MQTT_MSG_TYPE_PUBLISH, 0, qos, retain, remaining_length);
+
+ /* Append Topic */
+ mqtt_output_append_string(&client->output, topic, topic_len);
+
+ /* Append packet if for QoS 1 and 2*/
+ if (qos > 0) {
+ mqtt_output_append_u16(&client->output, pkt_id);
+ }
+
+ /* Append optional publish payload */
+ if ((payload != NULL) && (payload_length > 0)) {
+ mqtt_output_append_buf(&client->output, payload, payload_length);
+ }
+
+ mqtt_append_request(&client->pend_req_queue, r);
+ mqtt_output_send(&client->output, client->conn);
+ return ERR_OK;
+}
+
+
+/**
+ * @ingroup mqtt
+ * MQTT subscribe/unsubscribe function.
+ * @param client MQTT client
+ * @param topic topic to subscribe to
+ * @param qos Quality of service, 0 1 or 2 (only used for subscribe)
+ * @param cb Callback to call when subscribe/unsubscribe reponse is received
+ * @param arg User supplied argument to publish callback
+ * @param sub 1 for subscribe, 0 for unsubscribe
+ * @return ERR_OK if successful, @see err_t enum for other results
+ */
+err_t
+mqtt_sub_unsub(mqtt_client_t *client, const char *topic, u8_t qos, mqtt_request_cb_t cb, void *arg, u8_t sub)
+{
+ size_t topic_strlen;
+ size_t total_len;
+ u16_t topic_len;
+ u16_t remaining_length;
+ u16_t pkt_id;
+ struct mqtt_request_t *r;
+
+ LWIP_ASSERT_CORE_LOCKED();
+ LWIP_ASSERT("mqtt_sub_unsub: client != NULL", client);
+ LWIP_ASSERT("mqtt_sub_unsub: topic != NULL", topic);
+
+ topic_strlen = strlen(topic);
+ LWIP_ERROR("mqtt_sub_unsub: topic length overflow", (topic_strlen <= (0xFFFF - 2)), return ERR_ARG);
+ topic_len = (u16_t)topic_strlen;
+ /* Topic string, pkt_id, qos for subscribe */
+ total_len = topic_len + 2 + 2 + (sub != 0);
+ LWIP_ERROR("mqtt_sub_unsub: total length overflow", (total_len <= 0xFFFF), return ERR_ARG);
+ remaining_length = (u16_t)total_len;
+
+ LWIP_ASSERT("mqtt_sub_unsub: qos < 3", qos < 3);
+ if (client->conn_state == TCP_DISCONNECTED) {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_sub_unsub: Can not (un)subscribe in disconnected state\n"));
+ return ERR_CONN;
+ }
+
+ pkt_id = msg_generate_packet_id(client);
+ r = mqtt_create_request(client->req_list, LWIP_ARRAYSIZE(client->req_list), pkt_id, cb, arg);
+ if (r == NULL) {
+ return ERR_MEM;
+ }
+
+ if (mqtt_output_check_space(&client->output, remaining_length) == 0) {
+ mqtt_delete_request(r);
+ return ERR_MEM;
+ }
+
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_sub_unsub: Client (un)subscribe to topic \"%s\", id: %d\n", topic, pkt_id));
+
+ mqtt_output_append_fixed_header(&client->output, sub ? MQTT_MSG_TYPE_SUBSCRIBE : MQTT_MSG_TYPE_UNSUBSCRIBE, 0, 1, 0, remaining_length);
+ /* Packet id */
+ mqtt_output_append_u16(&client->output, pkt_id);
+ /* Topic */
+ mqtt_output_append_string(&client->output, topic, topic_len);
+ /* QoS */
+ if (sub != 0) {
+ mqtt_output_append_u8(&client->output, LWIP_MIN(qos, 2));
+ }
+
+ mqtt_append_request(&client->pend_req_queue, r);
+ mqtt_output_send(&client->output, client->conn);
+ return ERR_OK;
+}
+
+
+/**
+ * @ingroup mqtt
+ * Set callback to handle incoming publish requests from server
+ * @param client MQTT client
+ * @param pub_cb Callback invoked when publish starts, contain topic and total length of payload
+ * @param data_cb Callback for each fragment of payload that arrives
+ * @param arg User supplied argument to both callbacks
+ */
+void
+mqtt_set_inpub_callback(mqtt_client_t *client, mqtt_incoming_publish_cb_t pub_cb,
+ mqtt_incoming_data_cb_t data_cb, void *arg)
+{
+ LWIP_ASSERT_CORE_LOCKED();
+ LWIP_ASSERT("mqtt_set_inpub_callback: client != NULL", client != NULL);
+ client->data_cb = data_cb;
+ client->pub_cb = pub_cb;
+ client->inpub_arg = arg;
+}
+
+/**
+ * @ingroup mqtt
+ * Create a new MQTT client instance
+ * @return Pointer to instance on success, NULL otherwise
+ */
+mqtt_client_t *
+mqtt_client_new(void)
+{
+ LWIP_ASSERT_CORE_LOCKED();
+ return (mqtt_client_t *)mem_calloc(1, sizeof(mqtt_client_t));
+}
+
+/**
+ * @ingroup mqtt
+ * Free MQTT client instance
+ * @param client Pointer to instance to be freed
+ */
+void
+mqtt_client_free(mqtt_client_t *client)
+{
+ mem_free(client);
+}
+
+/**
+ * @ingroup mqtt
+ * Connect to MQTT server
+ * @param client MQTT client
+ * @param ip_addr Server IP
+ * @param port Server port
+ * @param cb Connection state change callback
+ * @param arg User supplied argument to connection callback
+ * @param client_info Client identification and connection options
+ * @return ERR_OK if successful, @see err_t enum for other results
+ */
+err_t
+mqtt_client_connect(mqtt_client_t *client, const ip_addr_t *ip_addr, u16_t port, mqtt_connection_cb_t cb, void *arg,
+ const struct mqtt_connect_client_info_t *client_info)
+{
+ err_t err;
+ size_t len;
+ u16_t client_id_length;
+ /* Length is the sum of 2+"MQTT", protocol level, flags and keep alive */
+ u16_t remaining_length = 2 + 4 + 1 + 1 + 2;
+ u8_t flags = 0, will_topic_len = 0, will_msg_len = 0;
+ u16_t client_user_len = 0, client_pass_len = 0;
+
+ LWIP_ASSERT_CORE_LOCKED();
+ LWIP_ASSERT("mqtt_client_connect: client != NULL", client != NULL);
+ LWIP_ASSERT("mqtt_client_connect: ip_addr != NULL", ip_addr != NULL);
+ LWIP_ASSERT("mqtt_client_connect: client_info != NULL", client_info != NULL);
+ LWIP_ASSERT("mqtt_client_connect: client_info->client_id != NULL", client_info->client_id != NULL);
+
+ if (client->conn_state != TCP_DISCONNECTED) {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_client_connect: Already connected\n"));
+ return ERR_ISCONN;
+ }
+
+ /* Wipe clean */
+ memset(client, 0, sizeof(mqtt_client_t));
+ client->connect_arg = arg;
+ client->connect_cb = cb;
+ client->keep_alive = client_info->keep_alive;
+ mqtt_init_requests(client->req_list, LWIP_ARRAYSIZE(client->req_list));
+
+ /* Build connect message */
+ if (client_info->will_topic != NULL && client_info->will_msg != NULL) {
+ flags |= MQTT_CONNECT_FLAG_WILL;
+ flags |= (client_info->will_qos & 3) << 3;
+ if (client_info->will_retain) {
+ flags |= MQTT_CONNECT_FLAG_WILL_RETAIN;
+ }
+ len = strlen(client_info->will_topic);
+ LWIP_ERROR("mqtt_client_connect: client_info->will_topic length overflow", len <= 0xFF, return ERR_VAL);
+ LWIP_ERROR("mqtt_client_connect: client_info->will_topic length must be > 0", len > 0, return ERR_VAL);
+ will_topic_len = (u8_t)len;
+ len = strlen(client_info->will_msg);
+ LWIP_ERROR("mqtt_client_connect: client_info->will_msg length overflow", len <= 0xFF, return ERR_VAL);
+ will_msg_len = (u8_t)len;
+ len = remaining_length + 2 + will_topic_len + 2 + will_msg_len;
+ LWIP_ERROR("mqtt_client_connect: remaining_length overflow", len <= 0xFFFF, return ERR_VAL);
+ remaining_length = (u16_t)len;
+ }
+ if (client_info->client_user != NULL) {
+ flags |= MQTT_CONNECT_FLAG_USERNAME;
+ len = strlen(client_info->client_user);
+ LWIP_ERROR("mqtt_client_connect: client_info->client_user length overflow", len <= 0xFFFF, return ERR_VAL);
+ LWIP_ERROR("mqtt_client_connect: client_info->client_user length must be > 0", len > 0, return ERR_VAL);
+ client_user_len = (u16_t)len;
+ len = remaining_length + 2 + client_user_len;
+ LWIP_ERROR("mqtt_client_connect: remaining_length overflow", len <= 0xFFFF, return ERR_VAL);
+ remaining_length = (u16_t)len;
+ }
+ if (client_info->client_pass != NULL) {
+ flags |= MQTT_CONNECT_FLAG_PASSWORD;
+ len = strlen(client_info->client_pass);
+ LWIP_ERROR("mqtt_client_connect: client_info->client_pass length overflow", len <= 0xFFFF, return ERR_VAL);
+ LWIP_ERROR("mqtt_client_connect: client_info->client_pass length must be > 0", len > 0, return ERR_VAL);
+ client_pass_len = (u16_t)len;
+ len = remaining_length + 2 + client_pass_len;
+ LWIP_ERROR("mqtt_client_connect: remaining_length overflow", len <= 0xFFFF, return ERR_VAL);
+ remaining_length = (u16_t)len;
+ }
+
+ /* Don't complicate things, always connect using clean session */
+ flags |= MQTT_CONNECT_FLAG_CLEAN_SESSION;
+
+ len = strlen(client_info->client_id);
+ LWIP_ERROR("mqtt_client_connect: client_info->client_id length overflow", len <= 0xFFFF, return ERR_VAL);
+ client_id_length = (u16_t)len;
+ len = remaining_length + 2 + client_id_length;
+ LWIP_ERROR("mqtt_client_connect: remaining_length overflow", len <= 0xFFFF, return ERR_VAL);
+ remaining_length = (u16_t)len;
+
+ if (mqtt_output_check_space(&client->output, remaining_length) == 0) {
+ return ERR_MEM;
+ }
+
+#if LWIP_ALTCP && LWIP_ALTCP_TLS
+ if (client_info->tls_config) {
+ client->conn = altcp_tls_new(client_info->tls_config, IP_GET_TYPE(ip_addr));
+ } else
+#endif
+ {
+ client->conn = altcp_tcp_new_ip_type(IP_GET_TYPE(ip_addr));
+ }
+ if (client->conn == NULL) {
+ return ERR_MEM;
+ }
+
+ /* Set arg pointer for callbacks */
+ altcp_arg(client->conn, client);
+ /* Any local address, pick random local port number */
+ err = altcp_bind(client->conn, IP_ADDR_ANY, 0);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_client_connect: Error binding to local ip/port, %d\n", err));
+ goto tcp_fail;
+ }
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_client_connect: Connecting to host: %s at port:%"U16_F"\n", ipaddr_ntoa(ip_addr), port));
+
+ /* Connect to server */
+ err = altcp_connect(client->conn, ip_addr, port, mqtt_tcp_connect_cb);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE, ("mqtt_client_connect: Error connecting to remote ip/port, %d\n", err));
+ goto tcp_fail;
+ }
+ /* Set error callback */
+ altcp_err(client->conn, mqtt_tcp_err_cb);
+ client->conn_state = TCP_CONNECTING;
+
+ /* Append fixed header */
+ mqtt_output_append_fixed_header(&client->output, MQTT_MSG_TYPE_CONNECT, 0, 0, 0, remaining_length);
+ /* Append Protocol string */
+ mqtt_output_append_string(&client->output, "MQTT", 4);
+ /* Append Protocol level */
+ mqtt_output_append_u8(&client->output, 4);
+ /* Append connect flags */
+ mqtt_output_append_u8(&client->output, flags);
+ /* Append keep-alive */
+ mqtt_output_append_u16(&client->output, client_info->keep_alive);
+ /* Append client id */
+ mqtt_output_append_string(&client->output, client_info->client_id, client_id_length);
+ /* Append will message if used */
+ if ((flags & MQTT_CONNECT_FLAG_WILL) != 0) {
+ mqtt_output_append_string(&client->output, client_info->will_topic, will_topic_len);
+ mqtt_output_append_string(&client->output, client_info->will_msg, will_msg_len);
+ }
+ /* Append user name if given */
+ if ((flags & MQTT_CONNECT_FLAG_USERNAME) != 0) {
+ mqtt_output_append_string(&client->output, client_info->client_user, client_user_len);
+ }
+ /* Append password if given */
+ if ((flags & MQTT_CONNECT_FLAG_PASSWORD) != 0) {
+ mqtt_output_append_string(&client->output, client_info->client_pass, client_pass_len);
+ }
+ return ERR_OK;
+
+tcp_fail:
+ altcp_abort(client->conn);
+ client->conn = NULL;
+ return err;
+}
+
+
+/**
+ * @ingroup mqtt
+ * Disconnect from MQTT server
+ * @param client MQTT client
+ */
+void
+mqtt_disconnect(mqtt_client_t *client)
+{
+ LWIP_ASSERT_CORE_LOCKED();
+ LWIP_ASSERT("mqtt_disconnect: client != NULL", client);
+ /* If connection in not already closed */
+ if (client->conn_state != TCP_DISCONNECTED) {
+ /* Set conn_state before calling mqtt_close to prevent callback from being called */
+ client->conn_state = TCP_DISCONNECTED;
+ mqtt_close(client, (mqtt_connection_status_t)0);
+ }
+}
+
+/**
+ * @ingroup mqtt
+ * Check connection with server
+ * @param client MQTT client
+ * @return 1 if connected to server, 0 otherwise
+ */
+u8_t
+mqtt_client_is_connected(mqtt_client_t *client)
+{
+ LWIP_ASSERT_CORE_LOCKED();
+ LWIP_ASSERT("mqtt_client_is_connected: client != NULL", client);
+ return client->conn_state == MQTT_CONNECTED;
+}
+
+#endif /* LWIP_TCP && LWIP_CALLBACK_API */
diff --git a/resources/ethernet/LwIP/apps/netbiosns/netbiosns.c b/resources/ethernet/LwIP/apps/netbiosns/netbiosns.c
new file mode 100644
index 00000000..6a605c82
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/netbiosns/netbiosns.c
@@ -0,0 +1,533 @@
+ /**
+ * @file
+ * NetBIOS name service responder
+ */
+
+/**
+ * @defgroup netbiosns NETBIOS responder
+ * @ingroup apps
+ *
+ * This is an example implementation of a NetBIOS name server.
+ * It responds to name queries for a configurable name.
+ * Name resolving is not supported.
+ *
+ * Note that the device doesn't broadcast it's own name so can't
+ * detect duplicate names!
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Modifications by Ray Abram to respond to NetBIOS name requests when Incoming name = *
+ * - based on code from "https://github.com/esp8266/Arduino/commit/1f7989b31d26d7df9776a08f36d685eae7ac8f99"
+ * - with permission to relicense to BSD from original author:
+ * http://www.xpablo.cz/?p=751#more-751
+ */
+
+#include "lwip/apps/netbiosns.h"
+
+#if LWIP_IPV4 && LWIP_UDP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/udp.h"
+#include "lwip/ip.h"
+#include "lwip/netif.h"
+#include "lwip/prot/iana.h"
+
+#include
+
+/** size of a NetBIOS name */
+#define NETBIOS_NAME_LEN 16
+
+/** The Time-To-Live for NetBIOS name responds (in seconds)
+ * Default is 300000 seconds (3 days, 11 hours, 20 minutes) */
+#define NETBIOS_NAME_TTL 300000u
+
+/** NetBIOS header flags */
+#define NETB_HFLAG_RESPONSE 0x8000U
+#define NETB_HFLAG_OPCODE 0x7800U
+#define NETB_HFLAG_OPCODE_NAME_QUERY 0x0000U
+#define NETB_HFLAG_AUTHORATIVE 0x0400U
+#define NETB_HFLAG_TRUNCATED 0x0200U
+#define NETB_HFLAG_RECURS_DESIRED 0x0100U
+#define NETB_HFLAG_RECURS_AVAILABLE 0x0080U
+#define NETB_HFLAG_BROADCAST 0x0010U
+#define NETB_HFLAG_REPLYCODE 0x0008U
+#define NETB_HFLAG_REPLYCODE_NOERROR 0x0000U
+
+/* NetBIOS question types */
+#define NETB_QTYPE_NB 0x0020U
+#define NETB_QTYPE_NBSTAT 0x0021U
+
+/** NetBIOS name flags */
+#define NETB_NFLAG_UNIQUE 0x8000U
+#define NETB_NFLAG_NODETYPE 0x6000U
+#define NETB_NFLAG_NODETYPE_HNODE 0x6000U
+#define NETB_NFLAG_NODETYPE_MNODE 0x4000U
+#define NETB_NFLAG_NODETYPE_PNODE 0x2000U
+#define NETB_NFLAG_NODETYPE_BNODE 0x0000U
+
+#define NETB_NFLAG_NAME_IN_CONFLICT 0x0800U /* 1=Yes, 0=No */
+#define NETB_NFLAG_NAME_IS_ACTIVE 0x0400U /* 1=Yes, 0=No */
+#define NETB_NFLAG_NAME_IS_PERMANENT 0x0200U /* 1=Yes (Name is Permanent Node Name), 0=No */
+
+/** NetBIOS message header */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct netbios_hdr {
+ PACK_STRUCT_FIELD(u16_t trans_id);
+ PACK_STRUCT_FIELD(u16_t flags);
+ PACK_STRUCT_FIELD(u16_t questions);
+ PACK_STRUCT_FIELD(u16_t answerRRs);
+ PACK_STRUCT_FIELD(u16_t authorityRRs);
+ PACK_STRUCT_FIELD(u16_t additionalRRs);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/** NetBIOS message question part */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct netbios_question_hdr {
+ PACK_STRUCT_FLD_8(u8_t nametype);
+ PACK_STRUCT_FLD_8(u8_t encname[(NETBIOS_NAME_LEN * 2) + 1]);
+ PACK_STRUCT_FIELD(u16_t type);
+ PACK_STRUCT_FIELD(u16_t cls);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/** NetBIOS message name part */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct netbios_name_hdr {
+ PACK_STRUCT_FLD_8(u8_t nametype);
+ PACK_STRUCT_FLD_8(u8_t encname[(NETBIOS_NAME_LEN * 2) + 1]);
+ PACK_STRUCT_FIELD(u16_t type);
+ PACK_STRUCT_FIELD(u16_t cls);
+ PACK_STRUCT_FIELD(u32_t ttl);
+ PACK_STRUCT_FIELD(u16_t datalen);
+ PACK_STRUCT_FIELD(u16_t flags);
+ PACK_STRUCT_FLD_S(ip4_addr_p_t addr);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/** NetBIOS message */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct netbios_resp {
+ struct netbios_hdr resp_hdr;
+ struct netbios_name_hdr resp_name;
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/** The NBNS Structure Responds to a Name Query */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct netbios_answer {
+ struct netbios_hdr answer_hdr;
+ /** the length of the next string */
+ PACK_STRUCT_FIELD(u8_t name_size);
+ /** WARNING!!! this item may be of a different length (we use this struct for transmission) */
+ PACK_STRUCT_FLD_8(u8_t query_name[(NETBIOS_NAME_LEN * 2) + 1]);
+ PACK_STRUCT_FIELD(u16_t packet_type);
+ PACK_STRUCT_FIELD(u16_t cls);
+ PACK_STRUCT_FIELD(u32_t ttl);
+ PACK_STRUCT_FIELD(u16_t data_length);
+#define OFFSETOF_STRUCT_NETBIOS_ANSWER_NUMBER_OF_NAMES 56
+ /** number of names */
+ PACK_STRUCT_FLD_8(u8_t number_of_names);
+ /** node name */
+ PACK_STRUCT_FLD_8(u8_t answer_name[NETBIOS_NAME_LEN]);
+ /** node flags */
+ PACK_STRUCT_FIELD(u16_t answer_name_flags);
+ /** Unit ID */
+ PACK_STRUCT_FLD_8(u8_t unit_id[6]);
+ /** Jumpers */
+ PACK_STRUCT_FLD_8(u8_t jumpers);
+ /** Test result */
+ PACK_STRUCT_FLD_8(u8_t test_result);
+ /** Version number */
+ PACK_STRUCT_FIELD(u16_t version_number);
+ /** Period of statistics */
+ PACK_STRUCT_FIELD(u16_t period_of_statistics);
+ /** Statistics */
+ PACK_STRUCT_FIELD(u16_t number_of_crcs);
+ /** Statistics */
+ PACK_STRUCT_FIELD(u16_t number_of_alignment_errors);
+ /** Statistics */
+ PACK_STRUCT_FIELD(u16_t number_of_collisions);
+ /** Statistics */
+ PACK_STRUCT_FIELD(u16_t number_of_send_aborts);
+ /** Statistics */
+ PACK_STRUCT_FIELD(u32_t number_of_good_sends);
+ /** Statistics */
+ PACK_STRUCT_FIELD(u32_t number_of_good_receives);
+ /** Statistics */
+ PACK_STRUCT_FIELD(u16_t number_of_retransmits);
+ /** Statistics */
+ PACK_STRUCT_FIELD(u16_t number_of_no_resource_condition);
+ /** Statistics */
+ PACK_STRUCT_FIELD(u16_t number_of_free_command_blocks);
+ /** Statistics */
+ PACK_STRUCT_FIELD(u16_t total_number_of_command_blocks);
+ /** Statistics */
+ PACK_STRUCT_FIELD(u16_t max_total_number_of_command_blocks);
+ /** Statistics */
+ PACK_STRUCT_FIELD(u16_t number_of_pending_sessions);
+ /** Statistics */
+ PACK_STRUCT_FIELD(u16_t max_number_of_pending_sessions);
+ /** Statistics */
+ PACK_STRUCT_FIELD(u16_t max_total_sessions_possible);
+ /** Statistics */
+ PACK_STRUCT_FIELD(u16_t session_data_packet_size);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+#ifdef NETBIOS_LWIP_NAME
+#define NETBIOS_LOCAL_NAME NETBIOS_LWIP_NAME
+#else
+static char netbiosns_local_name[NETBIOS_NAME_LEN];
+#define NETBIOS_LOCAL_NAME netbiosns_local_name
+#endif
+
+static struct udp_pcb *netbiosns_pcb;
+
+/** Decode a NetBIOS name (from packet to string) */
+static int
+netbiosns_name_decode(char *name_enc, char *name_dec, int name_dec_len)
+{
+ char *pname;
+ char cname;
+ char cnbname;
+ int idx = 0;
+
+ LWIP_UNUSED_ARG(name_dec_len);
+
+ /* Start decoding netbios name. */
+ pname = name_enc;
+ for (;;) {
+ /* Every two characters of the first level-encoded name
+ * turn into one character in the decoded name. */
+ cname = *pname;
+ if (cname == '\0') {
+ break; /* no more characters */
+ }
+ if (cname == '.') {
+ break; /* scope ID follows */
+ }
+ if (!lwip_isupper(cname)) {
+ /* Not legal. */
+ return -1;
+ }
+ cname -= 'A';
+ cnbname = cname << 4;
+ pname++;
+
+ cname = *pname;
+ if (!lwip_isupper(cname)) {
+ /* Not legal. */
+ return -1;
+ }
+ cname -= 'A';
+ cnbname |= cname;
+ pname++;
+
+ /* Do we have room to store the character? */
+ if (idx < NETBIOS_NAME_LEN) {
+ /* Yes - store the character. */
+ name_dec[idx++] = (cnbname != ' ' ? cnbname : '\0');
+ }
+ }
+
+ return 0;
+}
+
+#if 0 /* function currently unused */
+/** Encode a NetBIOS name (from string to packet) - currently unused because
+ we don't ask for names. */
+static int
+netbiosns_name_encode(char *name_enc, char *name_dec, int name_dec_len)
+{
+ char *pname;
+ char cname;
+ unsigned char ucname;
+ int idx = 0;
+
+ /* Start encoding netbios name. */
+ pname = name_enc;
+
+ for (;;) {
+ /* Every two characters of the first level-encoded name
+ * turn into one character in the decoded name. */
+ cname = *pname;
+ if (cname == '\0') {
+ break; /* no more characters */
+ }
+ if (cname == '.') {
+ break; /* scope ID follows */
+ }
+ if ((cname < 'A' || cname > 'Z') && (cname < '0' || cname > '9')) {
+ /* Not legal. */
+ return -1;
+ }
+
+ /* Do we have room to store the character? */
+ if (idx >= name_dec_len) {
+ return -1;
+ }
+
+ /* Yes - store the character. */
+ ucname = cname;
+ name_dec[idx++] = ('A' + ((ucname >> 4) & 0x0F));
+ name_dec[idx++] = ('A' + ( ucname & 0x0F));
+ pname++;
+ }
+
+ /* Fill with "space" coding */
+ for (; idx < name_dec_len - 1;) {
+ name_dec[idx++] = 'C';
+ name_dec[idx++] = 'A';
+ }
+
+ /* Terminate string */
+ name_dec[idx] = '\0';
+
+ return 0;
+}
+#endif /* 0 */
+
+/** NetBIOS Name service recv callback */
+static void
+netbiosns_recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
+{
+ LWIP_UNUSED_ARG(arg);
+
+ /* if packet is valid */
+ if (p != NULL) {
+ char netbios_name[NETBIOS_NAME_LEN + 1];
+ struct netbios_hdr *netbios_hdr = (struct netbios_hdr *)p->payload;
+ struct netbios_question_hdr *netbios_question_hdr = (struct netbios_question_hdr *)(netbios_hdr + 1);
+
+ /* is the packet long enough (we need the header in one piece) */
+ if (p->len < (sizeof(struct netbios_hdr) + sizeof(struct netbios_question_hdr))) {
+ /* packet too short */
+ pbuf_free(p);
+ return;
+ }
+ /* we only answer if we got a default interface */
+ if (netif_default != NULL) {
+ /* @todo: do we need to check answerRRs/authorityRRs/additionalRRs? */
+ /* if the packet is a NetBIOS name query question */
+ if (((netbios_hdr->flags & PP_NTOHS(NETB_HFLAG_OPCODE)) == PP_NTOHS(NETB_HFLAG_OPCODE_NAME_QUERY)) &&
+ ((netbios_hdr->flags & PP_NTOHS(NETB_HFLAG_RESPONSE)) == 0) &&
+ (netbios_hdr->questions == PP_NTOHS(1))) {
+ /* decode the NetBIOS name */
+ netbiosns_name_decode((char *)(netbios_question_hdr->encname), netbios_name, sizeof(netbios_name));
+ /* check the request type */
+ if (netbios_question_hdr->type == PP_HTONS(NETB_QTYPE_NB)) {
+ /* if the packet is for us */
+ if (lwip_strnicmp(netbios_name, NETBIOS_LOCAL_NAME, sizeof(NETBIOS_LOCAL_NAME)) == 0) {
+ struct pbuf *q;
+ struct netbios_resp *resp;
+
+ q = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct netbios_resp), PBUF_RAM);
+ if (q != NULL) {
+ resp = (struct netbios_resp *)q->payload;
+
+ /* prepare NetBIOS header response */
+ resp->resp_hdr.trans_id = netbios_hdr->trans_id;
+ resp->resp_hdr.flags = PP_HTONS(NETB_HFLAG_RESPONSE |
+ NETB_HFLAG_OPCODE_NAME_QUERY |
+ NETB_HFLAG_AUTHORATIVE |
+ NETB_HFLAG_RECURS_DESIRED);
+ resp->resp_hdr.questions = 0;
+ resp->resp_hdr.answerRRs = PP_HTONS(1);
+ resp->resp_hdr.authorityRRs = 0;
+ resp->resp_hdr.additionalRRs = 0;
+
+ /* prepare NetBIOS header datas */
+ MEMCPY( resp->resp_name.encname, netbios_question_hdr->encname, sizeof(netbios_question_hdr->encname));
+ resp->resp_name.nametype = netbios_question_hdr->nametype;
+ resp->resp_name.type = netbios_question_hdr->type;
+ resp->resp_name.cls = netbios_question_hdr->cls;
+ resp->resp_name.ttl = PP_HTONL(NETBIOS_NAME_TTL);
+ resp->resp_name.datalen = PP_HTONS(sizeof(resp->resp_name.flags) + sizeof(resp->resp_name.addr));
+ resp->resp_name.flags = PP_HTONS(NETB_NFLAG_NODETYPE_BNODE);
+ ip4_addr_copy(resp->resp_name.addr, *netif_ip4_addr(netif_default));
+
+ /* send the NetBIOS response */
+ udp_sendto(upcb, q, addr, port);
+
+ /* free the "reference" pbuf */
+ pbuf_free(q);
+ }
+ }
+#if LWIP_NETBIOS_RESPOND_NAME_QUERY
+ } else if (netbios_question_hdr->type == PP_HTONS(NETB_QTYPE_NBSTAT)) {
+ /* if the packet is for us or general query */
+ if (!lwip_strnicmp(netbios_name, NETBIOS_LOCAL_NAME, sizeof(NETBIOS_LOCAL_NAME)) ||
+ !lwip_strnicmp(netbios_name, "*", sizeof(NETBIOS_LOCAL_NAME))) {
+ /* general query - ask for our IP address */
+ struct pbuf *q;
+ struct netbios_answer *resp;
+
+ q = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct netbios_answer), PBUF_RAM);
+ if (q != NULL) {
+ /* buffer to which a response is compiled */
+ resp = (struct netbios_answer *) q->payload;
+
+ /* Init response to zero, especially the statistics fields */
+ memset(resp, 0, sizeof(*resp));
+
+ /* copy the query to the response ID */
+ resp->answer_hdr.trans_id = netbios_hdr->trans_id;
+ /* acknowledgment of termination */
+ resp->answer_hdr.flags = PP_HTONS(NETB_HFLAG_RESPONSE | NETB_HFLAG_OPCODE_NAME_QUERY | NETB_HFLAG_AUTHORATIVE);
+ /* resp->answer_hdr.questions = PP_HTONS(0); done by memset() */
+ /* serial number of the answer */
+ resp->answer_hdr.answerRRs = PP_HTONS(1);
+ /* resp->answer_hdr.authorityRRs = PP_HTONS(0); done by memset() */
+ /* resp->answer_hdr.additionalRRs = PP_HTONS(0); done by memset() */
+ /* we will copy the length of the station name */
+ resp->name_size = netbios_question_hdr->nametype;
+ /* we will copy the queried name */
+ MEMCPY(resp->query_name, netbios_question_hdr->encname, (NETBIOS_NAME_LEN * 2) + 1);
+ /* NBSTAT */
+ resp->packet_type = PP_HTONS(0x21);
+ /* Internet name */
+ resp->cls = PP_HTONS(1);
+ /* resp->ttl = PP_HTONL(0); done by memset() */
+ resp->data_length = PP_HTONS(sizeof(struct netbios_answer) - offsetof(struct netbios_answer, number_of_names));
+ resp->number_of_names = 1;
+
+ /* make windows see us as workstation, not as a server */
+ memset(resp->answer_name, 0x20, NETBIOS_NAME_LEN - 1);
+ /* strlen is checked to be < NETBIOS_NAME_LEN during initialization */
+ MEMCPY(resp->answer_name, NETBIOS_LOCAL_NAME, strlen(NETBIOS_LOCAL_NAME));
+
+ /* b-node, unique, active */
+ resp->answer_name_flags = PP_HTONS(NETB_NFLAG_NAME_IS_ACTIVE);
+
+ /* Set responder netif MAC address */
+ SMEMCPY(resp->unit_id, ip_current_input_netif()->hwaddr, sizeof(resp->unit_id));
+
+ udp_sendto(upcb, q, addr, port);
+ pbuf_free(q);
+ }
+ }
+#endif /* LWIP_NETBIOS_RESPOND_NAME_QUERY */
+ }
+ }
+ }
+ /* free the pbuf */
+ pbuf_free(p);
+ }
+}
+
+/**
+ * @ingroup netbiosns
+ * Init netbios responder
+ */
+void
+netbiosns_init(void)
+{
+ /* LWIP_ASSERT_CORE_LOCKED(); is checked by udp_new() */
+#ifdef NETBIOS_LWIP_NAME
+ LWIP_ASSERT("NetBIOS name is too long!", strlen(NETBIOS_LWIP_NAME) < NETBIOS_NAME_LEN);
+#endif
+
+ netbiosns_pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
+ if (netbiosns_pcb != NULL) {
+ /* we have to be allowed to send broadcast packets! */
+ ip_set_option(netbiosns_pcb, SOF_BROADCAST);
+ udp_bind(netbiosns_pcb, IP_ANY_TYPE, LWIP_IANA_PORT_NETBIOS);
+ udp_recv(netbiosns_pcb, netbiosns_recv, netbiosns_pcb);
+ }
+}
+
+#ifndef NETBIOS_LWIP_NAME
+/**
+ * @ingroup netbiosns
+ * Set netbios name. ATTENTION: the hostname must be less than 15 characters!
+ * the NetBIOS name spec says the name MUST be upper case, so incoming name is forced into uppercase :-)
+ */
+void
+netbiosns_set_name(const char *hostname)
+{
+ size_t i;
+ size_t copy_len = strlen(hostname);
+ LWIP_ASSERT_CORE_LOCKED();
+ LWIP_ASSERT("NetBIOS name is too long!", copy_len < NETBIOS_NAME_LEN);
+ if (copy_len >= NETBIOS_NAME_LEN) {
+ copy_len = NETBIOS_NAME_LEN - 1;
+ }
+
+ /* make name into upper case */
+ for (i = 0; i < copy_len; i++ ) {
+ netbiosns_local_name[i] = (char)lwip_toupper(hostname[i]);
+ }
+ netbiosns_local_name[copy_len] = '\0';
+}
+#endif /* NETBIOS_LWIP_NAME */
+
+/**
+ * @ingroup netbiosns
+ * Stop netbios responder
+ */
+void
+netbiosns_stop(void)
+{
+ LWIP_ASSERT_CORE_LOCKED();
+ if (netbiosns_pcb != NULL) {
+ udp_remove(netbiosns_pcb);
+ netbiosns_pcb = NULL;
+ }
+}
+
+#endif /* LWIP_IPV4 && LWIP_UDP */
diff --git a/resources/ethernet/LwIP/apps/smtp/smtp.c b/resources/ethernet/LwIP/apps/smtp/smtp.c
new file mode 100644
index 00000000..af2f053a
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/smtp/smtp.c
@@ -0,0 +1,1555 @@
+/**
+ * @file
+ * SMTP client module
+ *
+ * Author: Simon Goldschmidt
+ *
+ * @defgroup smtp SMTP client
+ * @ingroup apps
+ *
+ * This is simple SMTP client for raw API.
+ * It is a minimal implementation of SMTP as specified in RFC 5321.
+ *
+ * Example usage:
+@code{.c}
+ void my_smtp_result_fn(void *arg, u8_t smtp_result, u16_t srv_err, err_t err)
+ {
+ printf("mail (%p) sent with results: 0x%02x, 0x%04x, 0x%08x\n", arg,
+ smtp_result, srv_err, err);
+ }
+ static void my_smtp_test(void)
+ {
+ smtp_set_server_addr("mymailserver.org");
+ -> set both username and password as NULL if no auth needed
+ smtp_set_auth("username", "password");
+ smtp_send_mail("sender", "recipient", "subject", "body", my_smtp_result_fn,
+ some_argument);
+ }
+@endcode
+
+ * When using from any other thread than the tcpip_thread (for NO_SYS==0), use
+ * smtp_send_mail_int()!
+ *
+ * SMTP_BODYDH usage:
+@code{.c}
+ int my_smtp_bodydh_fn(void *arg, struct smtp_bodydh *bdh)
+ {
+ if(bdh->state >= 10) {
+ return BDH_DONE;
+ }
+ sprintf(bdh->buffer,"Line #%2d\r\n",bdh->state);
+ bdh->length = strlen(bdh->buffer);
+ ++bdh->state;
+ return BDH_WORKING;
+ }
+
+ smtp_send_mail_bodycback("sender", "recipient", "subject",
+ my_smtp_bodydh_fn, my_smtp_result_fn, some_argument);
+@endcode
+ *
+ * @todo:
+ * - attachments (the main difficulty here is streaming base64-encoding to
+ * prevent having to allocate a buffer for the whole encoded file at once)
+ * - test with more mail servers...
+ *
+ */
+
+#include "lwip/apps/smtp.h"
+
+#if LWIP_TCP && LWIP_CALLBACK_API
+#include "lwip/sys.h"
+#include "lwip/sockets.h"
+#include "lwip/altcp.h"
+#include "lwip/dns.h"
+#include "lwip/mem.h"
+#include "lwip/altcp_tcp.h"
+#include "lwip/altcp_tls.h"
+
+#include /* strlen, memcpy */
+#include
+
+/** TCP poll interval. Unit is 0.5 sec. */
+#define SMTP_POLL_INTERVAL 4
+/** TCP poll timeout while sending message body, reset after every
+ * successful write. 3 minutes */
+#define SMTP_TIMEOUT_DATABLOCK ( 3 * 60 * SMTP_POLL_INTERVAL / 2)
+/** TCP poll timeout while waiting for confirmation after sending the body.
+ * 10 minutes */
+#define SMTP_TIMEOUT_DATATERM (10 * 60 * SMTP_POLL_INTERVAL / 2)
+/** TCP poll timeout while not sending the body.
+ * This is somewhat lower than the RFC states (5 minutes for initial, MAIL
+ * and RCPT) but still OK for us here.
+ * 2 minutes */
+#define SMTP_TIMEOUT ( 2 * 60 * SMTP_POLL_INTERVAL / 2)
+
+/* the various debug levels for this file */
+#define SMTP_DEBUG_TRACE (SMTP_DEBUG | LWIP_DBG_TRACE)
+#define SMTP_DEBUG_STATE (SMTP_DEBUG | LWIP_DBG_STATE)
+#define SMTP_DEBUG_WARN (SMTP_DEBUG | LWIP_DBG_LEVEL_WARNING)
+#define SMTP_DEBUG_WARN_STATE (SMTP_DEBUG | LWIP_DBG_LEVEL_WARNING | LWIP_DBG_STATE)
+#define SMTP_DEBUG_SERIOUS (SMTP_DEBUG | LWIP_DBG_LEVEL_SERIOUS)
+
+
+#define SMTP_RX_BUF_LEN 255
+#define SMTP_TX_BUF_LEN 255
+#define SMTP_CRLF "\r\n"
+#define SMTP_CRLF_LEN 2
+
+#define SMTP_RESP_220 "220"
+#define SMTP_RESP_235 "235"
+#define SMTP_RESP_250 "250"
+#define SMTP_RESP_334 "334"
+#define SMTP_RESP_354 "354"
+#define SMTP_RESP_LOGIN_UNAME "VXNlcm5hbWU6"
+#define SMTP_RESP_LOGIN_PASS "UGFzc3dvcmQ6"
+
+#define SMTP_KEYWORD_AUTH_SP "AUTH "
+#define SMTP_KEYWORD_AUTH_EQ "AUTH="
+#define SMTP_KEYWORD_AUTH_LEN 5
+#define SMTP_AUTH_PARAM_PLAIN "PLAIN"
+#define SMTP_AUTH_PARAM_LOGIN "LOGIN"
+
+#define SMTP_CMD_EHLO_1 "EHLO ["
+#define SMTP_CMD_EHLO_1_LEN 6
+#define SMTP_CMD_EHLO_2 "]\r\n"
+#define SMTP_CMD_EHLO_2_LEN 3
+#define SMTP_CMD_AUTHPLAIN_1 "AUTH PLAIN "
+#define SMTP_CMD_AUTHPLAIN_1_LEN 11
+#define SMTP_CMD_AUTHPLAIN_2 "\r\n"
+#define SMTP_CMD_AUTHPLAIN_2_LEN 2
+#define SMTP_CMD_AUTHLOGIN "AUTH LOGIN\r\n"
+#define SMTP_CMD_AUTHLOGIN_LEN 12
+#define SMTP_CMD_MAIL_1 "MAIL FROM: <"
+#define SMTP_CMD_MAIL_1_LEN 12
+#define SMTP_CMD_MAIL_2 ">\r\n"
+#define SMTP_CMD_MAIL_2_LEN 3
+#define SMTP_CMD_RCPT_1 "RCPT TO: <"
+#define SMTP_CMD_RCPT_1_LEN 10
+#define SMTP_CMD_RCPT_2 ">\r\n"
+#define SMTP_CMD_RCPT_2_LEN 3
+#define SMTP_CMD_DATA "DATA\r\n"
+#define SMTP_CMD_DATA_LEN 6
+#define SMTP_CMD_HEADER_1 "From: <"
+#define SMTP_CMD_HEADER_1_LEN 7
+#define SMTP_CMD_HEADER_2 ">\r\nTo: <"
+#define SMTP_CMD_HEADER_2_LEN 8
+#define SMTP_CMD_HEADER_3 ">\r\nSubject: "
+#define SMTP_CMD_HEADER_3_LEN 12
+#define SMTP_CMD_HEADER_4 "\r\n\r\n"
+#define SMTP_CMD_HEADER_4_LEN 4
+#define SMTP_CMD_BODY_FINISHED "\r\n.\r\n"
+#define SMTP_CMD_BODY_FINISHED_LEN 5
+#define SMTP_CMD_QUIT "QUIT\r\n"
+#define SMTP_CMD_QUIT_LEN 6
+
+#if defined(SMTP_STAT_TX_BUF_MAX) && SMTP_STAT_TX_BUF_MAX
+#define SMTP_TX_BUF_MAX(len) LWIP_MACRO(if((len) > smtp_tx_buf_len_max) smtp_tx_buf_len_max = (len);)
+#else /* SMTP_STAT_TX_BUF_MAX */
+#define SMTP_TX_BUF_MAX(len)
+#endif /* SMTP_STAT_TX_BUF_MAX */
+
+#if SMTP_COPY_AUTHDATA
+#define SMTP_USERNAME(session) (session)->username
+#define SMTP_PASS(session) (session)->pass
+#define SMTP_AUTH_PLAIN_DATA(session) (session)->auth_plain
+#define SMTP_AUTH_PLAIN_LEN(session) (session)->auth_plain_len
+#else /* SMTP_COPY_AUTHDATA */
+#define SMTP_USERNAME(session) smtp_username
+#define SMTP_PASS(session) smtp_pass
+#define SMTP_AUTH_PLAIN_DATA(session) smtp_auth_plain
+#define SMTP_AUTH_PLAIN_LEN(session) smtp_auth_plain_len
+#endif /* SMTP_COPY_AUTHDATA */
+
+#if SMTP_BODYDH
+#ifndef SMTP_BODYDH_MALLOC
+#define SMTP_BODYDH_MALLOC(size) mem_malloc(size)
+#define SMTP_BODYDH_FREE(ptr) mem_free(ptr)
+#endif
+
+/* Some internal state return values */
+#define BDHALLDATASENT 2
+#define BDHSOMEDATASENT 1
+
+enum bdh_handler_state {
+ BDH_SENDING, /* Serving the user function generating body content */
+ BDH_STOP /* User function stopped, closing */
+};
+#endif
+
+/** State for SMTP client state machine */
+enum smtp_session_state {
+ SMTP_NULL,
+ SMTP_HELO,
+ SMTP_AUTH_PLAIN,
+ SMTP_AUTH_LOGIN_UNAME,
+ SMTP_AUTH_LOGIN_PASS,
+ SMTP_AUTH_LOGIN,
+ SMTP_MAIL,
+ SMTP_RCPT,
+ SMTP_DATA,
+ SMTP_BODY,
+ SMTP_QUIT,
+ SMTP_CLOSED
+};
+
+#ifdef LWIP_DEBUG
+/** State-to-string table for debugging */
+static const char *smtp_state_str[] = {
+ "SMTP_NULL",
+ "SMTP_HELO",
+ "SMTP_AUTH_PLAIN",
+ "SMTP_AUTH_LOGIN_UNAME",
+ "SMTP_AUTH_LOGIN_PASS",
+ "SMTP_AUTH_LOGIN",
+ "SMTP_MAIL",
+ "SMTP_RCPT",
+ "SMTP_DATA",
+ "SMTP_BODY",
+ "SMTP_QUIT",
+ "SMTP_CLOSED",
+};
+
+static const char *smtp_result_strs[] = {
+ "SMTP_RESULT_OK",
+ "SMTP_RESULT_ERR_UNKNOWN",
+ "SMTP_RESULT_ERR_CONNECT",
+ "SMTP_RESULT_ERR_HOSTNAME",
+ "SMTP_RESULT_ERR_CLOSED",
+ "SMTP_RESULT_ERR_TIMEOUT",
+ "SMTP_RESULT_ERR_SVR_RESP",
+ "SMTP_RESULT_ERR_MEM"
+};
+#endif /* LWIP_DEBUG */
+
+#if SMTP_BODYDH
+struct smtp_bodydh_state {
+ smtp_bodycback_fn callback_fn; /* The function to call (again) */
+ u16_t state;
+ struct smtp_bodydh exposed; /* the user function structure */
+};
+#endif /* SMTP_BODYDH */
+
+/** struct keeping the body and state of an smtp session */
+struct smtp_session {
+ /** keeping the state of the smtp session */
+ enum smtp_session_state state;
+ /** timeout handling, if this reaches 0, the connection is closed */
+ u16_t timer;
+ /** helper buffer for transmit, not used for sending body */
+ char tx_buf[SMTP_TX_BUF_LEN + 1];
+ struct pbuf* p;
+ /** source email address */
+ const char* from;
+ /** size of the sourceemail address */
+ u16_t from_len;
+ /** target email address */
+ const char* to;
+ /** size of the target email address */
+ u16_t to_len;
+ /** subject of the email */
+ const char *subject;
+ /** length of the subject string */
+ u16_t subject_len;
+ /** this is the body of the mail to be sent */
+ const char* body;
+ /** this is the length of the body to be sent */
+ u16_t body_len;
+ /** amount of data from body already sent */
+ u16_t body_sent;
+ /** callback function to call when closed */
+ smtp_result_fn callback_fn;
+ /** argument for callback function */
+ void *callback_arg;
+#if SMTP_COPY_AUTHDATA
+ /** Username to use for this request */
+ char *username;
+ /** Password to use for this request */
+ char *pass;
+ /** Username and password combined as necessary for PLAIN authentication */
+ char auth_plain[SMTP_MAX_USERNAME_LEN + SMTP_MAX_PASS_LEN + 3];
+ /** Length of smtp_auth_plain string (cannot use strlen since it includes \0) */
+ size_t auth_plain_len;
+#endif /* SMTP_COPY_AUTHDATA */
+#if SMTP_BODYDH
+ struct smtp_bodydh_state *bodydh;
+#endif /* SMTP_BODYDH */
+};
+
+/** IP address or DNS name of the server to use for next SMTP request */
+static char smtp_server[SMTP_MAX_SERVERNAME_LEN + 1];
+/** TCP port of the server to use for next SMTP request */
+static u16_t smtp_server_port = SMTP_DEFAULT_PORT;
+#if LWIP_ALTCP && LWIP_ALTCP_TLS
+/** If this is set, mail is sent using SMTPS */
+static struct altcp_tls_config *smtp_server_tls_config;
+#endif
+/** Username to use for the next SMTP request */
+static char *smtp_username;
+/** Password to use for the next SMTP request */
+static char *smtp_pass;
+/** Username and password combined as necessary for PLAIN authentication */
+static char smtp_auth_plain[SMTP_MAX_USERNAME_LEN + SMTP_MAX_PASS_LEN + 3];
+/** Length of smtp_auth_plain string (cannot use strlen since it includes \0) */
+static size_t smtp_auth_plain_len;
+
+#if SMTP_CHECK_DATA
+static err_t smtp_verify(const char *data, size_t data_len, u8_t linebreaks_allowed);
+#endif /* SMTP_CHECK_DATA */
+static err_t smtp_tcp_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err);
+static void smtp_tcp_err(void *arg, err_t err);
+static err_t smtp_tcp_poll(void *arg, struct altcp_pcb *pcb);
+static err_t smtp_tcp_sent(void *arg, struct altcp_pcb *pcb, u16_t len);
+static err_t smtp_tcp_connected(void *arg, struct altcp_pcb *pcb, err_t err);
+#if LWIP_DNS
+static void smtp_dns_found(const char* hostname, const ip_addr_t *ipaddr, void *arg);
+#endif /* LWIP_DNS */
+#if SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN
+static size_t smtp_base64_encode(char* target, size_t target_len, const char* source, size_t source_len);
+#endif /* SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN */
+static enum smtp_session_state smtp_prepare_mail(struct smtp_session *s, u16_t *tx_buf_len);
+static void smtp_send_body(struct smtp_session *s, struct altcp_pcb *pcb);
+static void smtp_process(void *arg, struct altcp_pcb *pcb, struct pbuf *p);
+#if SMTP_BODYDH
+static void smtp_send_body_data_handler(struct smtp_session *s, struct altcp_pcb *pcb);
+#endif /* SMTP_BODYDH */
+
+
+#ifdef LWIP_DEBUG
+/** Convert an smtp result to a string */
+const char*
+smtp_result_str(u8_t smtp_result)
+{
+ if (smtp_result >= LWIP_ARRAYSIZE(smtp_result_strs)) {
+ return "UNKNOWN";
+ }
+ return smtp_result_strs[smtp_result];
+}
+
+/** Null-terminates the payload of p for printing out messages.
+ * WARNING: use this only if p is not needed any more as the last byte of
+ * payload is deleted!
+ */
+static const char*
+smtp_pbuf_str(struct pbuf* p)
+{
+ if ((p == NULL) || (p->len == 0)) {
+ return "";
+ }
+ ((char*)p->payload)[p->len] = 0;
+ return (const char*)p->payload;
+}
+#endif /* LWIP_DEBUG */
+
+/** @ingroup smtp
+ * Set IP address or DNS name for next SMTP connection
+ *
+ * @param server IP address (in ASCII representation) or DNS name of the server
+ */
+err_t
+smtp_set_server_addr(const char* server)
+{
+ size_t len = 0;
+
+ LWIP_ASSERT_CORE_LOCKED();
+
+ if (server != NULL) {
+ /* strlen: returns length WITHOUT terminating 0 byte */
+ len = strlen(server);
+ }
+ if (len > SMTP_MAX_SERVERNAME_LEN) {
+ return ERR_MEM;
+ }
+ if (len != 0) {
+ MEMCPY(smtp_server, server, len);
+ }
+ smtp_server[len] = 0; /* always OK because of smtp_server[SMTP_MAX_SERVERNAME_LEN + 1] */
+ return ERR_OK;
+}
+
+/** @ingroup smtp
+ * Set TCP port for next SMTP connection
+ *
+ * @param port TCP port
+ */
+void
+smtp_set_server_port(u16_t port)
+{
+ LWIP_ASSERT_CORE_LOCKED();
+ smtp_server_port = port;
+}
+
+#if LWIP_ALTCP && LWIP_ALTCP_TLS
+/** @ingroup smtp
+ * Set TLS configuration for next SMTP connection
+ *
+ * @param tls_config TLS configuration
+ */
+void
+smtp_set_tls_config(struct altcp_tls_config *tls_config)
+{
+ LWIP_ASSERT_CORE_LOCKED();
+ smtp_server_tls_config = tls_config;
+}
+#endif
+
+/** @ingroup smtp
+ * Set authentication parameters for next SMTP connection
+ *
+ * @param username login name as passed to the server
+ * @param pass password passed to the server together with username
+ */
+err_t
+smtp_set_auth(const char* username, const char* pass)
+{
+ size_t uname_len = 0;
+ size_t pass_len = 0;
+
+ LWIP_ASSERT_CORE_LOCKED();
+
+ memset(smtp_auth_plain, 0xfa, 64);
+ if (username != NULL) {
+ uname_len = strlen(username);
+ if (uname_len > SMTP_MAX_USERNAME_LEN) {
+ LWIP_DEBUGF(SMTP_DEBUG_SERIOUS, ("Username is too long, %d instead of %d\n",
+ (int)uname_len, SMTP_MAX_USERNAME_LEN));
+ return ERR_ARG;
+ }
+ }
+ if (pass != NULL) {
+#if SMTP_SUPPORT_AUTH_LOGIN || SMTP_SUPPORT_AUTH_PLAIN
+ pass_len = strlen(pass);
+ if (pass_len > SMTP_MAX_PASS_LEN) {
+ LWIP_DEBUGF(SMTP_DEBUG_SERIOUS, ("Password is too long, %d instead of %d\n",
+ (int)uname_len, SMTP_MAX_USERNAME_LEN));
+ return ERR_ARG;
+ }
+#else /* SMTP_SUPPORT_AUTH_LOGIN || SMTP_SUPPORT_AUTH_PLAIN */
+ LWIP_DEBUGF(SMTP_DEBUG_WARN, ("Password not supported as no authentication methods are activated\n"));
+#endif /* SMTP_SUPPORT_AUTH_LOGIN || SMTP_SUPPORT_AUTH_PLAIN */
+ }
+ *smtp_auth_plain = 0;
+ if (username != NULL) {
+ smtp_username = smtp_auth_plain + 1;
+ strcpy(smtp_username, username);
+ }
+ if (pass != NULL) {
+ smtp_pass = smtp_auth_plain + uname_len + 2;
+ strcpy(smtp_pass, pass);
+ }
+ smtp_auth_plain_len = uname_len + pass_len + 2;
+
+ return ERR_OK;
+}
+
+#if SMTP_BODYDH
+static void smtp_free_struct(struct smtp_session *s)
+{
+ if (s->bodydh != NULL) {
+ SMTP_BODYDH_FREE(s->bodydh);
+ }
+ SMTP_STATE_FREE(s);
+}
+#else /* SMTP_BODYDH */
+#define smtp_free_struct(x) SMTP_STATE_FREE(x)
+#endif /* SMTP_BODYDH */
+
+static struct altcp_pcb*
+smtp_setup_pcb(struct smtp_session *s, const ip_addr_t* remote_ip)
+{
+ struct altcp_pcb* pcb;
+ LWIP_UNUSED_ARG(remote_ip);
+
+#if LWIP_ALTCP && LWIP_ALTCP_TLS
+ if (smtp_server_tls_config) {
+ pcb = altcp_tls_new(smtp_server_tls_config, IP_GET_TYPE(remote_ip));
+ } else
+#endif
+ {
+ pcb = altcp_tcp_new_ip_type(IP_GET_TYPE(remote_ip));
+ }
+ if (pcb != NULL) {
+ altcp_arg(pcb, s);
+ altcp_recv(pcb, smtp_tcp_recv);
+ altcp_err(pcb, smtp_tcp_err);
+ altcp_poll(pcb, smtp_tcp_poll, SMTP_POLL_INTERVAL);
+ altcp_sent(pcb, smtp_tcp_sent);
+ }
+ return pcb;
+}
+
+/** The actual mail-sending function, called by smtp_send_mail and
+ * smtp_send_mail_static after setting up the struct smtp_session.
+ */
+static err_t
+smtp_send_mail_alloced(struct smtp_session *s)
+{
+ err_t err;
+ struct altcp_pcb* pcb = NULL;
+ ip_addr_t addr;
+
+ LWIP_ASSERT("no smtp_session supplied", s != NULL);
+
+#if SMTP_CHECK_DATA
+ /* check that body conforms to RFC:
+ * - convert all single-CR or -LF in body to CRLF
+ * - only 7-bit ASCII is allowed
+ */
+ if (smtp_verify(s->to, s->to_len, 0) != ERR_OK) {
+ err = ERR_ARG;
+ goto leave;
+ }
+ if (smtp_verify(s->from, s->from_len, 0) != ERR_OK) {
+ err = ERR_ARG;
+ goto leave;
+ }
+ if (smtp_verify(s->subject, s->subject_len, 0) != ERR_OK) {
+ err = ERR_ARG;
+ goto leave;
+ }
+#if SMTP_BODYDH
+ if (s->bodydh == NULL)
+#endif /* SMTP_BODYDH */
+ {
+ if (smtp_verify(s->body, s->body_len, 0) != ERR_OK) {
+ err = ERR_ARG;
+ goto leave;
+ }
+ }
+#endif /* SMTP_CHECK_DATA */
+
+#if SMTP_COPY_AUTHDATA
+ /* copy auth data, ensuring the first byte is always zero */
+ MEMCPY(s->auth_plain + 1, smtp_auth_plain + 1, smtp_auth_plain_len - 1);
+ s->auth_plain_len = smtp_auth_plain_len;
+ /* default username and pass is empty string */
+ s->username = s->auth_plain;
+ s->pass = s->auth_plain;
+ if (smtp_username != NULL) {
+ s->username += smtp_username - smtp_auth_plain;
+ }
+ if (smtp_pass != NULL) {
+ s->pass += smtp_pass - smtp_auth_plain;
+ }
+#endif /* SMTP_COPY_AUTHDATA */
+
+ s->state = SMTP_NULL;
+ s->timer = SMTP_TIMEOUT;
+
+#if LWIP_DNS
+ err = dns_gethostbyname(smtp_server, &addr, smtp_dns_found, s);
+#else /* LWIP_DNS */
+ err = ipaddr_aton(smtp_server, &addr) ? ERR_OK : ERR_ARG;
+#endif /* LWIP_DNS */
+ if (err == ERR_OK) {
+ pcb = smtp_setup_pcb(s, &addr);
+ if (pcb == NULL) {
+ err = ERR_MEM;
+ goto leave;
+ }
+ err = altcp_connect(pcb, &addr, smtp_server_port, smtp_tcp_connected);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("tcp_connect failed: %d\n", (int)err));
+ goto deallocate_and_leave;
+ }
+ } else if (err != ERR_INPROGRESS) {
+ LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("dns_gethostbyname failed: %d\n", (int)err));
+ goto deallocate_and_leave;
+ }
+ return ERR_OK;
+
+deallocate_and_leave:
+ if (pcb != NULL) {
+ altcp_arg(pcb, NULL);
+ altcp_close(pcb);
+ }
+leave:
+ smtp_free_struct(s);
+ /* no need to call the callback here since we return != ERR_OK */
+ return err;
+}
+
+/** @ingroup smtp
+ * Send an email via the currently selected server, username and password.
+ *
+ * @param from source email address (must be NULL-terminated)
+ * @param to target email address (must be NULL-terminated)
+ * @param subject email subject (must be NULL-terminated)
+ * @param body email body (must be NULL-terminated)
+ * @param callback_fn callback function
+ * @param callback_arg user argument to callback_fn
+ * @returns - ERR_OK if structures were allocated and no error occured starting the connection
+ * (this does not mean the email has been successfully sent!)
+ * - another err_t on error.
+ */
+err_t
+smtp_send_mail(const char* from, const char* to, const char* subject, const char* body,
+ smtp_result_fn callback_fn, void* callback_arg)
+{
+ struct smtp_session* s;
+ size_t from_len = strlen(from);
+ size_t to_len = strlen(to);
+ size_t subject_len = strlen(subject);
+ size_t body_len = strlen(body);
+ size_t mem_len = sizeof(struct smtp_session);
+ char *sfrom, *sto, *ssubject, *sbody;
+
+ LWIP_ASSERT_CORE_LOCKED();
+
+ mem_len += from_len + to_len + subject_len + body_len + 4;
+ if (mem_len > 0xffff) {
+ /* too long! */
+ return ERR_MEM;
+ }
+
+ /* Allocate memory to keep this email's session state */
+ s = (struct smtp_session *)SMTP_STATE_MALLOC((mem_size_t)mem_len);
+ if (s == NULL) {
+ return ERR_MEM;
+ }
+ /* initialize the structure */
+ memset(s, 0, mem_len);
+ s->from = sfrom = (char*)s + sizeof(struct smtp_session);
+ s->from_len = (u16_t)from_len;
+ s->to = sto = sfrom + from_len + 1;
+ s->to_len = (u16_t)to_len;
+ s->subject = ssubject = sto + to_len + 1;
+ s->subject_len = (u16_t)subject_len;
+ s->body = sbody = ssubject + subject_len + 1;
+ s->body_len = (u16_t)body_len;
+ /* copy source and target email address */
+ /* cast to size_t is a hack to cast away constness */
+ MEMCPY(sfrom, from, from_len + 1);
+ MEMCPY(sto, to, to_len + 1);
+ MEMCPY(ssubject, subject, subject_len + 1);
+ MEMCPY(sbody, body, body_len + 1);
+
+ s->callback_fn = callback_fn;
+ s->callback_arg = callback_arg;
+
+ /* call the actual implementation of this function */
+ return smtp_send_mail_alloced(s);
+}
+
+/** @ingroup smtp
+ * Same as smtp_send_mail, but doesn't copy from, to, subject and body into
+ * an internal buffer to save memory.
+ * WARNING: the above data must stay untouched until the callback function is
+ * called (unless the function returns != ERR_OK)
+ */
+err_t
+smtp_send_mail_static(const char *from, const char* to, const char* subject,
+ const char* body, smtp_result_fn callback_fn, void* callback_arg)
+{
+ struct smtp_session* s;
+ size_t len;
+
+ LWIP_ASSERT_CORE_LOCKED();
+
+ s = (struct smtp_session*)SMTP_STATE_MALLOC(sizeof(struct smtp_session));
+ if (s == NULL) {
+ return ERR_MEM;
+ }
+ memset(s, 0, sizeof(struct smtp_session));
+ /* initialize the structure */
+ s->from = from;
+ len = strlen(from);
+ LWIP_ASSERT("string is too long", len <= 0xffff);
+ s->from_len = (u16_t)len;
+ s->to = to;
+ len = strlen(to);
+ LWIP_ASSERT("string is too long", len <= 0xffff);
+ s->to_len = (u16_t)len;
+ s->subject = subject;
+ len = strlen(subject);
+ LWIP_ASSERT("string is too long", len <= 0xffff);
+ s->subject_len = (u16_t)len;
+ s->body = body;
+ len = strlen(body);
+ LWIP_ASSERT("string is too long", len <= 0xffff);
+ s->body_len = (u16_t)len;
+ s->callback_fn = callback_fn;
+ s->callback_arg = callback_arg;
+ /* call the actual implementation of this function */
+ return smtp_send_mail_alloced(s);
+}
+
+
+/** @ingroup smtp
+ * Same as smtp_send_mail but takes a struct smtp_send_request as single
+ * parameter which contains all the other parameters.
+ * To be used with tcpip_callback to send mail from interrupt context or from
+ * another thread.
+ *
+ * WARNING: server and authentication must stay untouched until this function has run!
+ *
+ * Usage example:
+ * - allocate a struct smtp_send_request (in a way that is allowed in interrupt context)
+ * - fill the members of the struct as if calling smtp_send_mail
+ * - specify a callback_function
+ * - set callback_arg to the structure itself
+ * - call this function
+ * - wait for the callback function to be called
+ * - in the callback function, deallocate the structure (passed as arg)
+ */
+void
+smtp_send_mail_int(void *arg)
+{
+ struct smtp_send_request *req = (struct smtp_send_request*)arg;
+ err_t err;
+
+ LWIP_ASSERT_CORE_LOCKED();
+ LWIP_ASSERT("smtp_send_mail_int: no argument given", arg != NULL);
+
+ if (req->static_data) {
+ err = smtp_send_mail_static(req->from, req->to, req->subject, req->body,
+ req->callback_fn, req->callback_arg);
+ } else {
+ err = smtp_send_mail(req->from, req->to, req->subject, req->body,
+ req->callback_fn, req->callback_arg);
+ }
+ if ((err != ERR_OK) && (req->callback_fn != NULL)) {
+ req->callback_fn(req->callback_arg, SMTP_RESULT_ERR_UNKNOWN, 0, err);
+ }
+}
+
+#if SMTP_CHECK_DATA
+/** Verify that a given string conforms to the SMTP rules
+ * (7-bit only, no single CR or LF,
+ * @todo: no line consisting of a single dot only)
+ */
+static err_t
+smtp_verify(const char *data, size_t data_len, u8_t linebreaks_allowed)
+{
+ size_t i;
+ u8_t last_was_cr = 0;
+ for (i = 0; i < data_len; i++) {
+ char current = data[i];
+ if ((current & 0x80) != 0) {
+ LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_verify: no 8-bit data supported: %s\n", data));
+ return ERR_ARG;
+ }
+ if (current == '\r') {
+ if (!linebreaks_allowed) {
+ LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_verify: found CR where no linebreaks allowed: %s\n", data));
+ return ERR_ARG;
+ }
+ if (last_was_cr) {
+ LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_verify: found double CR: %s\n", data));
+ return ERR_ARG;
+ }
+ last_was_cr = 1;
+ } else {
+ if (current == '\n') {
+ if (!last_was_cr) {
+ LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_verify: found LF without CR before: %s\n", data));
+ return ERR_ARG;
+ }
+ }
+ last_was_cr = 0;
+ }
+ }
+ return ERR_OK;
+}
+#endif /* SMTP_CHECK_DATA */
+
+/** Frees the smtp_session and calls the callback function */
+static void
+smtp_free(struct smtp_session *s, u8_t result, u16_t srv_err, err_t err)
+{
+ smtp_result_fn fn = s->callback_fn;
+ void *arg = s->callback_arg;
+ if (s->p != NULL) {
+ pbuf_free(s->p);
+ }
+ smtp_free_struct(s);
+ if (fn != NULL) {
+ fn(arg, result, srv_err, err);
+ }
+}
+
+/** Try to close a pcb and free the arg if successful */
+static void
+smtp_close(struct smtp_session *s, struct altcp_pcb *pcb, u8_t result,
+ u16_t srv_err, err_t err)
+{
+ if (pcb != NULL) {
+ altcp_arg(pcb, NULL);
+ if (altcp_close(pcb) == ERR_OK) {
+ if (s != NULL) {
+ smtp_free(s, result, srv_err, err);
+ }
+ } else {
+ /* close failed, set back arg */
+ altcp_arg(pcb, s);
+ }
+ } else {
+ if (s != NULL) {
+ smtp_free(s, result, srv_err, err);
+ }
+ }
+}
+
+/** Raw API TCP err callback: pcb is already deallocated */
+static void
+smtp_tcp_err(void *arg, err_t err)
+{
+ LWIP_UNUSED_ARG(err);
+ if (arg != NULL) {
+ LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_tcp_err: connection reset by remote host\n"));
+ smtp_free((struct smtp_session*)arg, SMTP_RESULT_ERR_CLOSED, 0, err);
+ }
+}
+
+/** Raw API TCP poll callback */
+static err_t
+smtp_tcp_poll(void *arg, struct altcp_pcb *pcb)
+{
+ if (arg != NULL) {
+ struct smtp_session *s = (struct smtp_session*)arg;
+ if (s->timer != 0) {
+ s->timer--;
+ }
+ }
+ smtp_process(arg, pcb, NULL);
+ return ERR_OK;
+}
+
+/** Raw API TCP sent callback */
+static err_t
+smtp_tcp_sent(void *arg, struct altcp_pcb *pcb, u16_t len)
+{
+ LWIP_UNUSED_ARG(len);
+
+ smtp_process(arg, pcb, NULL);
+
+ return ERR_OK;
+}
+
+/** Raw API TCP recv callback */
+static err_t
+smtp_tcp_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err)
+{
+ LWIP_UNUSED_ARG(err);
+ if (p != NULL) {
+ altcp_recved(pcb, p->tot_len);
+ smtp_process(arg, pcb, p);
+ } else {
+ LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_tcp_recv: connection closed by remote host\n"));
+ smtp_close((struct smtp_session*)arg, pcb, SMTP_RESULT_ERR_CLOSED, 0, err);
+ }
+ return ERR_OK;
+}
+
+static err_t
+smtp_tcp_connected(void *arg, struct altcp_pcb *pcb, err_t err)
+{
+ LWIP_UNUSED_ARG(arg);
+
+ if (err == ERR_OK) {
+ LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_connected: Waiting for 220\n"));
+ } else {
+ /* shouldn't happen, but we still check 'err', only to be sure */
+ LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_connected: %d\n", (int)err));
+ smtp_close((struct smtp_session*)arg, pcb, SMTP_RESULT_ERR_CONNECT, 0, err);
+ }
+ return ERR_OK;
+}
+
+#if LWIP_DNS
+/** DNS callback
+ * If ipaddr is non-NULL, resolving succeeded, otherwise it failed.
+ */
+static void
+smtp_dns_found(const char* hostname, const ip_addr_t *ipaddr, void *arg)
+{
+ struct smtp_session *s = (struct smtp_session*)arg;
+ struct altcp_pcb *pcb;
+ err_t err;
+ u8_t result;
+
+ LWIP_UNUSED_ARG(hostname);
+
+ if (ipaddr != NULL) {
+ pcb = smtp_setup_pcb(s, ipaddr);
+ if (pcb != NULL) {
+ LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_dns_found: hostname resolved, connecting\n"));
+ err = altcp_connect(pcb, ipaddr, smtp_server_port, smtp_tcp_connected);
+ if (err == ERR_OK) {
+ return;
+ }
+ LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("tcp_connect failed: %d\n", (int)err));
+ result = SMTP_RESULT_ERR_CONNECT;
+ } else {
+ LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_dns_found: failed to allocate tcp pcb\n"));
+ result = SMTP_RESULT_ERR_MEM;
+ err = ERR_MEM;
+ }
+ } else {
+ LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_dns_found: failed to resolve hostname: %s\n",
+ hostname));
+ pcb = NULL;
+ result = SMTP_RESULT_ERR_HOSTNAME;
+ err = ERR_ARG;
+ }
+ smtp_close(s, pcb, result, 0, err);
+}
+#endif /* LWIP_DNS */
+
+#if SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN
+
+/** Table 6-bit-index-to-ASCII used for base64-encoding */
+static const char base64_table[] = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+ 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+ 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
+ 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
+ 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '+', '/'
+};
+
+/** Base64 encoding */
+static size_t
+smtp_base64_encode(char* target, size_t target_len, const char* source, size_t source_len)
+{
+ size_t i;
+ s8_t j;
+ size_t target_idx = 0;
+ size_t longer = (source_len % 3) ? (3 - (source_len % 3)) : 0;
+ size_t source_len_b64 = source_len + longer;
+ size_t len = (((source_len_b64) * 4) / 3);
+ u8_t x = 5;
+ u8_t current = 0;
+ LWIP_UNUSED_ARG(target_len);
+
+ LWIP_ASSERT("target_len is too short", target_len >= len);
+
+ for (i = 0; i < source_len_b64; i++) {
+ u8_t b = (i < source_len ? (u8_t)source[i] : 0);
+ for (j = 7; j >= 0; j--, x--) {
+ if ((b & (1 << j)) != 0) {
+ current = (u8_t)(current | (1U << x));
+ }
+ if (x == 0) {
+ target[target_idx++] = base64_table[current];
+ x = 6;
+ current = 0;
+ }
+ }
+ }
+ for (i = len - longer; i < len; i++) {
+ target[i] = '=';
+ }
+ return len;
+}
+#endif /* SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN */
+
+/** Parse pbuf to see if it contains the beginning of an answer.
+ * If so, it returns the contained response code as number between 1 and 999.
+ * If not, zero is returned.
+ *
+ * @param s smtp session struct
+ */
+static u16_t
+smtp_is_response(struct smtp_session *s)
+{
+ char digits[4];
+ long num;
+
+ if (s->p == NULL) {
+ return 0;
+ }
+ /* copy three digits and convert them to int */
+ if (pbuf_copy_partial(s->p, digits, 3, 0) != 3) {
+ /* pbuf was too short */
+ return 0;
+ }
+ digits[3] = 0;
+ num = strtol(digits, NULL, 10);
+ if ((num <= 0) || (num >= 1000)) {
+ /* failed to find response code at start of line */
+ return 0;
+ }
+ return (u16_t)num;
+}
+
+/** Parse pbuf to see if it contains a fully received answer.
+ * If one is found, ERR_OK is returned.
+ * If none is found, ERR_VAL is returned.
+ *
+ * A fully received answer is a 3-digit number followed by a space,
+ * some string and a CRLF as line ending.
+ *
+ * @param s smtp session struct
+ */
+static err_t
+smtp_is_response_finished(struct smtp_session *s)
+{
+ u8_t sp;
+ u16_t crlf;
+ u16_t offset;
+
+ if (s->p == NULL) {
+ return ERR_VAL;
+ }
+ offset = 0;
+again:
+ /* We could check the response number here, but we trust the
+ * protocol definition which says the client can rely on it being
+ * the same on every line. */
+
+ /* find CRLF */
+ if (offset > 0xFFFF - 4) {
+ /* would overflow */
+ return ERR_VAL;
+ }
+ crlf = pbuf_memfind(s->p, SMTP_CRLF, SMTP_CRLF_LEN, (u16_t)(offset + 4));
+ if (crlf == 0xFFFF) {
+ /* no CRLF found */
+ return ERR_VAL;
+ }
+ sp = pbuf_get_at(s->p, (u16_t)(offset + 3));
+ if (sp == '-') {
+ /* no space after response code -> try next line */
+ offset = (u16_t)(crlf + 2);
+ if (offset < crlf) {
+ /* overflow */
+ return ERR_VAL;
+ }
+ goto again;
+ } else if (sp == ' ') {
+ /* CRLF found after response code + space -> valid response */
+ return ERR_OK;
+ }
+ /* sp contains invalid character */
+ return ERR_VAL;
+}
+
+/** Prepare HELO/EHLO message */
+static enum smtp_session_state
+smtp_prepare_helo(struct smtp_session *s, u16_t *tx_buf_len, struct altcp_pcb *pcb)
+{
+ size_t ipa_len;
+ const char *ipa = ipaddr_ntoa(altcp_get_ip(pcb, 1));
+ LWIP_ASSERT("ipaddr_ntoa returned NULL", ipa != NULL);
+ ipa_len = strlen(ipa);
+ LWIP_ASSERT("string too long", ipa_len <= (SMTP_TX_BUF_LEN-SMTP_CMD_EHLO_1_LEN-SMTP_CMD_EHLO_2_LEN));
+
+ *tx_buf_len = (u16_t)(SMTP_CMD_EHLO_1_LEN + (u16_t)ipa_len + SMTP_CMD_EHLO_2_LEN);
+ LWIP_ASSERT("tx_buf overflow detected", *tx_buf_len <= SMTP_TX_BUF_LEN);
+
+ SMEMCPY(s->tx_buf, SMTP_CMD_EHLO_1, SMTP_CMD_EHLO_1_LEN);
+ MEMCPY(&s->tx_buf[SMTP_CMD_EHLO_1_LEN], ipa, ipa_len);
+ SMEMCPY(&s->tx_buf[SMTP_CMD_EHLO_1_LEN + ipa_len], SMTP_CMD_EHLO_2, SMTP_CMD_EHLO_2_LEN);
+ return SMTP_HELO;
+}
+
+#if SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN
+/** Parse last server response (in rx_buf) for supported authentication method,
+ * create data to send out (to tx_buf), set tx_data_len correctly
+ * and return the next state.
+ */
+static enum smtp_session_state
+smtp_prepare_auth_or_mail(struct smtp_session *s, u16_t *tx_buf_len)
+{
+ /* check response for supported authentication method */
+ u16_t auth = pbuf_strstr(s->p, SMTP_KEYWORD_AUTH_SP);
+ if (auth == 0xFFFF) {
+ auth = pbuf_strstr(s->p, SMTP_KEYWORD_AUTH_EQ);
+ }
+ if (auth != 0xFFFF) {
+ u16_t crlf = pbuf_memfind(s->p, SMTP_CRLF, SMTP_CRLF_LEN, auth);
+ if ((crlf != 0xFFFF) && (crlf > auth)) {
+ /* use tx_buf temporarily */
+ u16_t copied = pbuf_copy_partial(s->p, s->tx_buf, (u16_t)(crlf - auth), auth);
+ if (copied != 0) {
+ char *sep = s->tx_buf + SMTP_KEYWORD_AUTH_LEN;
+ s->tx_buf[copied] = 0;
+#if SMTP_SUPPORT_AUTH_PLAIN
+ /* favour PLAIN over LOGIN since it involves less requests */
+ if (strstr(sep, SMTP_AUTH_PARAM_PLAIN) != NULL) {
+ size_t auth_len;
+ /* server supports AUTH PLAIN */
+ SMEMCPY(s->tx_buf, SMTP_CMD_AUTHPLAIN_1, SMTP_CMD_AUTHPLAIN_1_LEN);
+
+ /* add base64-encoded string "\0username\0password" */
+ auth_len = smtp_base64_encode(&s->tx_buf[SMTP_CMD_AUTHPLAIN_1_LEN],
+ SMTP_TX_BUF_LEN - SMTP_CMD_AUTHPLAIN_1_LEN, SMTP_AUTH_PLAIN_DATA(s),
+ SMTP_AUTH_PLAIN_LEN(s));
+ LWIP_ASSERT("string too long", auth_len <= (SMTP_TX_BUF_LEN-SMTP_CMD_AUTHPLAIN_1_LEN-SMTP_CMD_AUTHPLAIN_2_LEN));
+ *tx_buf_len = (u16_t)(SMTP_CMD_AUTHPLAIN_1_LEN + SMTP_CMD_AUTHPLAIN_2_LEN + (u16_t)auth_len);
+ SMEMCPY(&s->tx_buf[SMTP_CMD_AUTHPLAIN_1_LEN + auth_len], SMTP_CMD_AUTHPLAIN_2,
+ SMTP_CMD_AUTHPLAIN_2_LEN);
+ return SMTP_AUTH_PLAIN;
+ } else
+#endif /* SMTP_SUPPORT_AUTH_PLAIN */
+ {
+#if SMTP_SUPPORT_AUTH_LOGIN
+ if (strstr(sep, SMTP_AUTH_PARAM_LOGIN) != NULL) {
+ /* server supports AUTH LOGIN */
+ *tx_buf_len = SMTP_CMD_AUTHLOGIN_LEN;
+ SMEMCPY(s->tx_buf, SMTP_CMD_AUTHLOGIN, SMTP_CMD_AUTHLOGIN_LEN);
+ return SMTP_AUTH_LOGIN_UNAME;
+ }
+#endif /* SMTP_SUPPORT_AUTH_LOGIN */
+ }
+ }
+ }
+ }
+ /* server didnt's send correct keywords for AUTH, try sending directly */
+ return smtp_prepare_mail(s, tx_buf_len);
+}
+#endif /* SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN */
+
+#if SMTP_SUPPORT_AUTH_LOGIN
+/** Send base64-encoded username */
+static enum smtp_session_state
+smtp_prepare_auth_login_uname(struct smtp_session *s, u16_t *tx_buf_len)
+{
+ size_t base64_len = smtp_base64_encode(s->tx_buf, SMTP_TX_BUF_LEN,
+ SMTP_USERNAME(s), strlen(SMTP_USERNAME(s)));
+ /* @todo: support base64-encoded longer than 64k */
+ LWIP_ASSERT("string too long", base64_len <= 0xffff);
+ LWIP_ASSERT("tx_buf overflow detected", base64_len <= SMTP_TX_BUF_LEN - SMTP_CRLF_LEN);
+ *tx_buf_len = (u16_t)(base64_len + SMTP_CRLF_LEN);
+
+ SMEMCPY(&s->tx_buf[base64_len], SMTP_CRLF, SMTP_CRLF_LEN);
+ s->tx_buf[*tx_buf_len] = 0;
+ return SMTP_AUTH_LOGIN_PASS;
+}
+
+/** Send base64-encoded password */
+static enum smtp_session_state
+smtp_prepare_auth_login_pass(struct smtp_session *s, u16_t *tx_buf_len)
+{
+ size_t base64_len = smtp_base64_encode(s->tx_buf, SMTP_TX_BUF_LEN,
+ SMTP_PASS(s), strlen(SMTP_PASS(s)));
+ /* @todo: support base64-encoded longer than 64k */
+ LWIP_ASSERT("string too long", base64_len <= 0xffff);
+ LWIP_ASSERT("tx_buf overflow detected", base64_len <= SMTP_TX_BUF_LEN - SMTP_CRLF_LEN);
+ *tx_buf_len = (u16_t)(base64_len + SMTP_CRLF_LEN);
+
+ SMEMCPY(&s->tx_buf[base64_len], SMTP_CRLF, SMTP_CRLF_LEN);
+ s->tx_buf[*tx_buf_len] = 0;
+ return SMTP_AUTH_LOGIN;
+}
+#endif /* SMTP_SUPPORT_AUTH_LOGIN */
+
+/** Prepare MAIL message */
+static enum smtp_session_state
+smtp_prepare_mail(struct smtp_session *s, u16_t *tx_buf_len)
+{
+ char *target = s->tx_buf;
+ LWIP_ASSERT("tx_buf overflow detected", s->from_len <= (SMTP_TX_BUF_LEN - SMTP_CMD_MAIL_1_LEN - SMTP_CMD_MAIL_2_LEN));
+ *tx_buf_len = (u16_t)(SMTP_CMD_MAIL_1_LEN + SMTP_CMD_MAIL_2_LEN + s->from_len);
+ target[*tx_buf_len] = 0;
+
+ SMEMCPY(target, SMTP_CMD_MAIL_1, SMTP_CMD_MAIL_1_LEN);
+ target += SMTP_CMD_MAIL_1_LEN;
+ MEMCPY(target, s->from, s->from_len);
+ target += s->from_len;
+ SMEMCPY(target, SMTP_CMD_MAIL_2, SMTP_CMD_MAIL_2_LEN);
+ return SMTP_MAIL;
+}
+
+/** Prepare RCPT message */
+static enum smtp_session_state
+smtp_prepare_rcpt(struct smtp_session *s, u16_t *tx_buf_len)
+{
+ char *target = s->tx_buf;
+ LWIP_ASSERT("tx_buf overflow detected", s->to_len <= (SMTP_TX_BUF_LEN - SMTP_CMD_RCPT_1_LEN - SMTP_CMD_RCPT_2_LEN));
+ *tx_buf_len = (u16_t)(SMTP_CMD_RCPT_1_LEN + SMTP_CMD_RCPT_2_LEN + s->to_len);
+ target[*tx_buf_len] = 0;
+
+ SMEMCPY(target, SMTP_CMD_RCPT_1, SMTP_CMD_RCPT_1_LEN);
+ target += SMTP_CMD_RCPT_1_LEN;
+ MEMCPY(target, s->to, s->to_len);
+ target += s->to_len;
+ SMEMCPY(target, SMTP_CMD_RCPT_2, SMTP_CMD_RCPT_2_LEN);
+ return SMTP_RCPT;
+}
+
+/** Prepare header of body */
+static enum smtp_session_state
+smtp_prepare_header(struct smtp_session *s, u16_t *tx_buf_len)
+{
+ char *target = s->tx_buf;
+ int len = SMTP_CMD_HEADER_1_LEN + SMTP_CMD_HEADER_2_LEN +
+ SMTP_CMD_HEADER_3_LEN + SMTP_CMD_HEADER_4_LEN + s->from_len + s->to_len +
+ s->subject_len;
+ LWIP_ASSERT("tx_buf overflow detected", len > 0 && len <= SMTP_TX_BUF_LEN);
+ *tx_buf_len = (u16_t)len;
+ target[*tx_buf_len] = 0;
+
+ SMEMCPY(target, SMTP_CMD_HEADER_1, SMTP_CMD_HEADER_1_LEN);
+ target += SMTP_CMD_HEADER_1_LEN;
+ MEMCPY(target, s->from, s->from_len);
+ target += s->from_len;
+ SMEMCPY(target, SMTP_CMD_HEADER_2, SMTP_CMD_HEADER_2_LEN);
+ target += SMTP_CMD_HEADER_2_LEN;
+ MEMCPY(target, s->to, s->to_len);
+ target += s->to_len;
+ SMEMCPY(target, SMTP_CMD_HEADER_3, SMTP_CMD_HEADER_3_LEN);
+ target += SMTP_CMD_HEADER_3_LEN;
+ MEMCPY(target, s->subject, s->subject_len);
+ target += s->subject_len;
+ SMEMCPY(target, SMTP_CMD_HEADER_4, SMTP_CMD_HEADER_4_LEN);
+
+ return SMTP_BODY;
+}
+
+/** Prepare QUIT message */
+static enum smtp_session_state
+smtp_prepare_quit(struct smtp_session *s, u16_t *tx_buf_len)
+{
+ *tx_buf_len = SMTP_CMD_QUIT_LEN;
+ s->tx_buf[*tx_buf_len] = 0;
+ SMEMCPY(s->tx_buf, SMTP_CMD_QUIT, SMTP_CMD_QUIT_LEN);
+ LWIP_ASSERT("tx_buf overflow detected", *tx_buf_len <= SMTP_TX_BUF_LEN);
+ return SMTP_CLOSED;
+}
+
+/** If in state SMTP_BODY, try to send more body data */
+static void
+smtp_send_body(struct smtp_session *s, struct altcp_pcb *pcb)
+{
+ err_t err;
+
+ if (s->state == SMTP_BODY) {
+#if SMTP_BODYDH
+ if (s->bodydh) {
+ smtp_send_body_data_handler(s, pcb);
+ } else
+#endif /* SMTP_BODYDH */
+ {
+ u16_t send_len = (u16_t)(s->body_len - s->body_sent);
+ if (send_len > 0) {
+ u16_t snd_buf = altcp_sndbuf(pcb);
+ if (send_len > snd_buf) {
+ send_len = snd_buf;
+ }
+ if (send_len > 0) {
+ /* try to send something out */
+ err = altcp_write(pcb, &s->body[s->body_sent], (u16_t)send_len, TCP_WRITE_FLAG_COPY);
+ if (err == ERR_OK) {
+ s->timer = SMTP_TIMEOUT_DATABLOCK;
+ s->body_sent = (u16_t)(s->body_sent + send_len);
+ if (s->body_sent < s->body_len) {
+ LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_send_body: %d of %d bytes written\n",
+ s->body_sent, s->body_len));
+ }
+ }
+ }
+ }
+ }
+ if (s->body_sent == s->body_len) {
+ /* the whole body has been written, write last line */
+ LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_send_body: body completely written (%d bytes), appending end-of-body\n",
+ s->body_len));
+ err = altcp_write(pcb, SMTP_CMD_BODY_FINISHED, SMTP_CMD_BODY_FINISHED_LEN, 0);
+ if (err == ERR_OK) {
+ s->timer = SMTP_TIMEOUT_DATATERM;
+ LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_send_body: end-of-body written, changing state to %s\n",
+ smtp_state_str[SMTP_QUIT]));
+ /* last line written, change state, wait for confirmation */
+ s->state = SMTP_QUIT;
+ }
+ }
+ }
+}
+
+/** State machine-like implementation of an SMTP client.
+ */
+static void
+smtp_process(void *arg, struct altcp_pcb *pcb, struct pbuf *p)
+{
+ struct smtp_session* s = (struct smtp_session*)arg;
+ u16_t response_code = 0;
+ u16_t tx_buf_len = 0;
+ enum smtp_session_state next_state;
+
+ if (arg == NULL) {
+ /* already closed SMTP connection */
+ if (p != NULL) {
+ LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("Received %d bytes after closing: %s\n",
+ p->tot_len, smtp_pbuf_str(p)));
+ pbuf_free(p);
+ }
+ return;
+ }
+
+ next_state = s->state;
+
+ if (p != NULL) {
+ /* received data */
+ if (s->p == NULL) {
+ s->p = p;
+ } else {
+ pbuf_cat(s->p, p);
+ }
+ } else {
+ /* idle timer, close connection if timed out */
+ if (s->timer == 0) {
+ LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_process: connection timed out, closing\n"));
+ smtp_close(s, pcb, SMTP_RESULT_ERR_TIMEOUT, 0, ERR_TIMEOUT);
+ return;
+ }
+ if (s->state == SMTP_BODY) {
+ smtp_send_body(s, pcb);
+ return;
+ }
+ }
+ response_code = smtp_is_response(s);
+ if (response_code) {
+ LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process: received response code: %d\n", response_code));
+ if (smtp_is_response_finished(s) != ERR_OK) {
+ LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process: partly received response code: %d\n", response_code));
+ /* wait for next packet to complete the respone */
+ return;
+ }
+ } else {
+ if (s->p != NULL) {
+ LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_process: unknown data received (%s)\n",
+ smtp_pbuf_str(s->p)));
+ pbuf_free(s->p);
+ s->p = NULL;
+ }
+ return;
+ }
+
+ switch(s->state)
+ {
+ case(SMTP_NULL):
+ /* wait for 220 */
+ if (response_code == 220) {
+ /* then send EHLO */
+ next_state = smtp_prepare_helo(s, &tx_buf_len, pcb);
+ }
+ break;
+ case(SMTP_HELO):
+ /* wait for 250 */
+ if (response_code == 250) {
+#if SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN
+ /* then send AUTH or MAIL */
+ next_state = smtp_prepare_auth_or_mail(s, &tx_buf_len);
+ }
+ break;
+ case(SMTP_AUTH_LOGIN):
+ case(SMTP_AUTH_PLAIN):
+ /* wait for 235 */
+ if (response_code == 235) {
+#endif /* SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN */
+ /* send MAIL */
+ next_state = smtp_prepare_mail(s, &tx_buf_len);
+ }
+ break;
+#if SMTP_SUPPORT_AUTH_LOGIN
+ case(SMTP_AUTH_LOGIN_UNAME):
+ /* wait for 334 Username */
+ if (response_code == 334) {
+ if (pbuf_strstr(s->p, SMTP_RESP_LOGIN_UNAME) != 0xFFFF) {
+ /* send username */
+ next_state = smtp_prepare_auth_login_uname(s, &tx_buf_len);
+ }
+ }
+ break;
+ case(SMTP_AUTH_LOGIN_PASS):
+ /* wait for 334 Password */
+ if (response_code == 334) {
+ if (pbuf_strstr(s->p, SMTP_RESP_LOGIN_PASS) != 0xFFFF) {
+ /* send username */
+ next_state = smtp_prepare_auth_login_pass(s, &tx_buf_len);
+ }
+ }
+ break;
+#endif /* SMTP_SUPPORT_AUTH_LOGIN */
+ case(SMTP_MAIL):
+ /* wait for 250 */
+ if (response_code == 250) {
+ /* send RCPT */
+ next_state = smtp_prepare_rcpt(s, &tx_buf_len);
+ }
+ break;
+ case(SMTP_RCPT):
+ /* wait for 250 */
+ if (response_code == 250) {
+ /* send DATA */
+ SMEMCPY(s->tx_buf, SMTP_CMD_DATA, SMTP_CMD_DATA_LEN);
+ tx_buf_len = SMTP_CMD_DATA_LEN;
+ next_state = SMTP_DATA;
+ }
+ break;
+ case(SMTP_DATA):
+ /* wait for 354 */
+ if (response_code == 354) {
+ /* send email header */
+ next_state = smtp_prepare_header(s, &tx_buf_len);
+ }
+ break;
+ case(SMTP_BODY):
+ /* nothing to be done here, handled somewhere else */
+ break;
+ case(SMTP_QUIT):
+ /* wait for 250 */
+ if (response_code == 250) {
+ /* send QUIT */
+ next_state = smtp_prepare_quit(s, &tx_buf_len);
+ }
+ break;
+ case(SMTP_CLOSED):
+ /* nothing to do, wait for connection closed from server */
+ return;
+ default:
+ LWIP_DEBUGF(SMTP_DEBUG_SERIOUS, ("Invalid state: %d/%s\n", (int)s->state,
+ smtp_state_str[s->state]));
+ break;
+ }
+ if (s->state == next_state) {
+ LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_process[%s]: unexpected response_code, closing: %d (%s)\n",
+ smtp_state_str[s->state], response_code, smtp_pbuf_str(s->p)));
+ /* close connection */
+ smtp_close(s, pcb, SMTP_RESULT_ERR_SVR_RESP, response_code, ERR_OK);
+ return;
+ }
+ if (tx_buf_len > 0) {
+ SMTP_TX_BUF_MAX(tx_buf_len);
+ if (altcp_write(pcb, s->tx_buf, tx_buf_len, TCP_WRITE_FLAG_COPY) == ERR_OK) {
+ LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process[%s]: received command %d (%s)\n",
+ smtp_state_str[s->state], response_code, smtp_pbuf_str(s->p)));
+ LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process[%s]: sent %"U16_F" bytes: \"%s\"\n",
+ smtp_state_str[s->state], tx_buf_len, s->tx_buf));
+ s->timer = SMTP_TIMEOUT;
+ pbuf_free(s->p);
+ s->p = NULL;
+ LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_process: changing state from %s to %s\n",
+ smtp_state_str[s->state], smtp_state_str[next_state]));
+ s->state = next_state;
+ if (next_state == SMTP_BODY) {
+ /* try to stream-send body data right now */
+ smtp_send_body(s, pcb);
+ } else if (next_state == SMTP_CLOSED) {
+ /* sent out all data, delete structure */
+ altcp_arg(pcb, NULL);
+ smtp_free(s, SMTP_RESULT_OK, 0, ERR_OK);
+ }
+ }
+ }
+}
+
+#if SMTP_BODYDH
+/** Elementary sub-function to send data
+ *
+ * @returns: BDHALLDATASENT all data has been written
+ * BDHSOMEDATASENT some data has been written
+ * 0 no data has been written
+ */
+static int
+smtp_send_bodyh_data(struct altcp_pcb *pcb, const char **from, u16_t *howmany)
+{
+ err_t err;
+ u16_t len = *howmany;
+
+ len = (u16_t)LWIP_MIN(len, altcp_sndbuf(pcb));
+ err = altcp_write(pcb, *from, len, TCP_WRITE_FLAG_COPY);
+ if (err == ERR_OK) {
+ *from += len;
+ if ((*howmany -= len) > 0) {
+ return BDHSOMEDATASENT;
+ }
+ return BDHALLDATASENT;
+ }
+ return 0;
+}
+
+/** Same as smtp_send_mail_static, but uses a callback function to send body data
+ */
+err_t
+smtp_send_mail_bodycback(const char *from, const char* to, const char* subject,
+ smtp_bodycback_fn bodycback_fn, smtp_result_fn callback_fn, void* callback_arg)
+{
+ struct smtp_session* s;
+ size_t len;
+
+ LWIP_ASSERT_CORE_LOCKED();
+
+ s = (struct smtp_session*)SMTP_STATE_MALLOC(sizeof(struct smtp_session));
+ if (s == NULL) {
+ return ERR_MEM;
+ }
+ memset(s, 0, sizeof(struct smtp_session));
+ s->bodydh = (struct smtp_bodydh_state*)SMTP_BODYDH_MALLOC(sizeof(struct smtp_bodydh_state));
+ if (s->bodydh == NULL) {
+ SMTP_STATE_FREE(s);
+ return ERR_MEM;
+ }
+ memset(s->bodydh, 0, sizeof(struct smtp_bodydh_state));
+ /* initialize the structure */
+ s->from = from;
+ len = strlen(from);
+ LWIP_ASSERT("string is too long", len <= 0xffff);
+ s->from_len = (u16_t)len;
+ s->to = to;
+ len = strlen(to);
+ LWIP_ASSERT("string is too long", len <= 0xffff);
+ s->to_len = (u16_t)len;
+ s->subject = subject;
+ len = strlen(subject);
+ LWIP_ASSERT("string is too long", len <= 0xffff);
+ s->subject_len = (u16_t)len;
+ s->body = NULL;
+ LWIP_ASSERT("string is too long", len <= 0xffff);
+ s->callback_fn = callback_fn;
+ s->callback_arg = callback_arg;
+ s->bodydh->callback_fn = bodycback_fn;
+ s->bodydh->state = BDH_SENDING;
+ /* call the actual implementation of this function */
+ return smtp_send_mail_alloced(s);
+}
+
+static void
+smtp_send_body_data_handler(struct smtp_session *s, struct altcp_pcb *pcb)
+{
+ struct smtp_bodydh_state *bdh;
+ int res = 0, ret;
+ LWIP_ASSERT("s != NULL", s != NULL);
+ bdh = s->bodydh;
+ LWIP_ASSERT("bodydh != NULL", bdh != NULL);
+
+ /* resume any leftovers from prior memory constraints */
+ if (s->body_len) {
+ LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: resume\n"));
+ if((res = smtp_send_bodyh_data(pcb, (const char **)&s->body, &s->body_len))
+ != BDHALLDATASENT) {
+ s->body_sent = s->body_len - 1;
+ return;
+ }
+ }
+ ret = res;
+ /* all data on buffer has been queued, resume execution */
+ if (bdh->state == BDH_SENDING) {
+ LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: run\n"));
+ do {
+ ret |= res; /* remember if we once queued something to send */
+ bdh->exposed.length = 0;
+ if (bdh->callback_fn(s->callback_arg, &bdh->exposed) == BDH_DONE) {
+ bdh->state = BDH_STOP;
+ }
+ s->body = bdh->exposed.buffer;
+ s->body_len = bdh->exposed.length;
+ LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: trying to send %u bytes\n", (unsigned int)s->body_len));
+ } while (s->body_len &&
+ ((res = smtp_send_bodyh_data(pcb, (const char **)&s->body, &s->body_len)) == BDHALLDATASENT)
+ && (bdh->state != BDH_STOP));
+ }
+ if ((bdh->state != BDH_SENDING) && (ret != BDHSOMEDATASENT)) {
+ LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: stop\n"));
+ s->body_sent = s->body_len;
+ } else {
+ LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: pause\n"));
+ s->body_sent = s->body_len - 1;
+ }
+}
+#endif /* SMTP_BODYDH */
+
+#endif /* LWIP_TCP && LWIP_CALLBACK_API */
diff --git a/resources/ethernet/LwIP/apps/snmp/snmp_asn1.c b/resources/ethernet/LwIP/apps/snmp/snmp_asn1.c
new file mode 100644
index 00000000..8e91e758
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/snmp/snmp_asn1.c
@@ -0,0 +1,704 @@
+/**
+ * @file
+ * Abstract Syntax Notation One (ISO 8824, 8825) encoding
+ *
+ * @todo not optimised (yet), favor correctness over speed, favor speed over size
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons
+ * Martin Hentschel
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "snmp_asn1.h"
+
+#define PBUF_OP_EXEC(code) \
+ if ((code) != ERR_OK) { \
+ return ERR_BUF; \
+ }
+
+/**
+ * Encodes a TLV into a pbuf stream.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param tlv TLV to encode
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
+ */
+err_t
+snmp_ans1_enc_tlv(struct snmp_pbuf_stream *pbuf_stream, struct snmp_asn1_tlv *tlv)
+{
+ u8_t data;
+ u8_t length_bytes_required;
+
+ /* write type */
+ if ((tlv->type & SNMP_ASN1_DATATYPE_MASK) == SNMP_ASN1_DATATYPE_EXTENDED) {
+ /* extended format is not used by SNMP so we do not accept those values */
+ return ERR_ARG;
+ }
+ if (tlv->type_len != 0) {
+ /* any other value as auto is not accepted for type (we always use one byte because extended syntax is prohibited) */
+ return ERR_ARG;
+ }
+
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, tlv->type));
+ tlv->type_len = 1;
+
+ /* write length */
+ if (tlv->value_len <= 127) {
+ length_bytes_required = 1;
+ } else if (tlv->value_len <= 255) {
+ length_bytes_required = 2;
+ } else {
+ length_bytes_required = 3;
+ }
+
+ /* check for forced min length */
+ if (tlv->length_len > 0) {
+ if (tlv->length_len < length_bytes_required) {
+ /* unable to code requested length in requested number of bytes */
+ return ERR_ARG;
+ }
+
+ length_bytes_required = tlv->length_len;
+ } else {
+ tlv->length_len = length_bytes_required;
+ }
+
+ if (length_bytes_required > 1) {
+ /* multi byte representation required */
+ length_bytes_required--;
+ data = 0x80 | length_bytes_required; /* extended length definition, 1 length byte follows */
+
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
+
+ while (length_bytes_required > 1) {
+ if (length_bytes_required == 2) {
+ /* append high byte */
+ data = (u8_t)(tlv->value_len >> 8);
+ } else {
+ /* append leading 0x00 */
+ data = 0x00;
+ }
+
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
+ length_bytes_required--;
+ }
+ }
+
+ /* append low byte */
+ data = (u8_t)(tlv->value_len & 0xFF);
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
+
+ return ERR_OK;
+}
+
+/**
+ * Encodes raw data (octet string, opaque) into a pbuf chained ASN1 msg.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param raw_len raw data length
+ * @param raw points raw data
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
+ */
+err_t
+snmp_asn1_enc_raw(struct snmp_pbuf_stream *pbuf_stream, const u8_t *raw, u16_t raw_len)
+{
+ PBUF_OP_EXEC(snmp_pbuf_stream_writebuf(pbuf_stream, raw, raw_len));
+
+ return ERR_OK;
+}
+
+/**
+ * Encodes u32_t (counter, gauge, timeticks) into a pbuf chained ASN1 msg.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt())
+ * @param value is the host order u32_t value to be encoded
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
+ *
+ * @see snmp_asn1_enc_u32t_cnt()
+ */
+err_t
+snmp_asn1_enc_u32t(struct snmp_pbuf_stream *pbuf_stream, u16_t octets_needed, u32_t value)
+{
+ if (octets_needed > 5) {
+ return ERR_ARG;
+ }
+ if (octets_needed == 5) {
+ /* not enough bits in 'value' add leading 0x00 */
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, 0x00));
+ octets_needed--;
+ }
+
+ while (octets_needed > 1) {
+ octets_needed--;
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3))));
+ }
+
+ /* (only) one least significant octet */
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)value));
+
+ return ERR_OK;
+}
+/**
+ * Encodes s32_t integer into a pbuf chained ASN1 msg.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param octets_needed encoding length (from snmp_asn1_enc_s32t_cnt())
+ * @param value is the host order s32_t value to be encoded
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
+ *
+ * @see snmp_asn1_enc_s32t_cnt()
+ */
+err_t
+snmp_asn1_enc_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t octets_needed, s32_t value)
+{
+ while (octets_needed > 1) {
+ octets_needed--;
+
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3))));
+ }
+
+ /* (only) one least significant octet */
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)value));
+
+ return ERR_OK;
+}
+
+/**
+ * Encodes object identifier into a pbuf chained ASN1 msg.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param oid points to object identifier array
+ * @param oid_len object identifier array length
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
+ */
+err_t
+snmp_asn1_enc_oid(struct snmp_pbuf_stream *pbuf_stream, const u32_t *oid, u16_t oid_len)
+{
+ if (oid_len > 1) {
+ /* write compressed first two sub id's */
+ u32_t compressed_byte = ((oid[0] * 40) + oid[1]);
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)compressed_byte));
+ oid_len -= 2;
+ oid += 2;
+ } else {
+ /* @bug: allow empty varbinds for symmetry (we must decode them for getnext), allow partial compression?? */
+ /* ident_len <= 1, at least we need zeroDotZero (0.0) (ident_len == 2) */
+ return ERR_ARG;
+ }
+
+ while (oid_len > 0) {
+ u32_t sub_id;
+ u8_t shift, tail;
+
+ oid_len--;
+ sub_id = *oid;
+ tail = 0;
+ shift = 28;
+ while (shift > 0) {
+ u8_t code;
+
+ code = (u8_t)(sub_id >> shift);
+ if ((code != 0) || (tail != 0)) {
+ tail = 1;
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, code | 0x80));
+ }
+ shift -= 7;
+ }
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)sub_id & 0x7F));
+
+ /* proceed to next sub-identifier */
+ oid++;
+ }
+ return ERR_OK;
+}
+
+/**
+ * Returns octet count for length.
+ *
+ * @param length parameter length
+ * @param octets_needed points to the return value
+ */
+void
+snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed)
+{
+ if (length < 0x80U) {
+ *octets_needed = 1;
+ } else if (length < 0x100U) {
+ *octets_needed = 2;
+ } else {
+ *octets_needed = 3;
+ }
+}
+
+/**
+ * Returns octet count for an u32_t.
+ *
+ * @param value value to be encoded
+ * @param octets_needed points to the return value
+ *
+ * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
+ * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
+ * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
+ */
+void
+snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed)
+{
+ if (value < 0x80UL) {
+ *octets_needed = 1;
+ } else if (value < 0x8000UL) {
+ *octets_needed = 2;
+ } else if (value < 0x800000UL) {
+ *octets_needed = 3;
+ } else if (value < 0x80000000UL) {
+ *octets_needed = 4;
+ } else {
+ *octets_needed = 5;
+ }
+}
+
+/**
+ * Returns octet count for an s32_t.
+ *
+ * @param value value to be encoded
+ * @param octets_needed points to the return value
+ *
+ * @note ASN coded integers are _always_ signed.
+ */
+void
+snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed)
+{
+ if (value < 0) {
+ value = ~value;
+ }
+ if (value < 0x80L) {
+ *octets_needed = 1;
+ } else if (value < 0x8000L) {
+ *octets_needed = 2;
+ } else if (value < 0x800000L) {
+ *octets_needed = 3;
+ } else {
+ *octets_needed = 4;
+ }
+}
+
+/**
+ * Returns octet count for an object identifier.
+ *
+ * @param oid points to object identifier array
+ * @param oid_len object identifier array length
+ * @param octets_needed points to the return value
+ */
+void
+snmp_asn1_enc_oid_cnt(const u32_t *oid, u16_t oid_len, u16_t *octets_needed)
+{
+ u32_t sub_id;
+
+ *octets_needed = 0;
+ if (oid_len > 1) {
+ /* compressed prefix in one octet */
+ (*octets_needed)++;
+ oid_len -= 2;
+ oid += 2;
+ }
+ while (oid_len > 0) {
+ oid_len--;
+ sub_id = *oid;
+
+ sub_id >>= 7;
+ (*octets_needed)++;
+ while (sub_id > 0) {
+ sub_id >>= 7;
+ (*octets_needed)++;
+ }
+ oid++;
+ }
+}
+
+/**
+ * Decodes a TLV from a pbuf stream.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param tlv returns decoded TLV
+ * @return ERR_OK if successful, ERR_VAL if we can't decode
+ */
+err_t
+snmp_asn1_dec_tlv(struct snmp_pbuf_stream *pbuf_stream, struct snmp_asn1_tlv *tlv)
+{
+ u8_t data;
+
+ /* decode type first */
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+ tlv->type = data;
+
+ if ((tlv->type & SNMP_ASN1_DATATYPE_MASK) == SNMP_ASN1_DATATYPE_EXTENDED) {
+ /* extended format is not used by SNMP so we do not accept those values */
+ return ERR_VAL;
+ }
+ tlv->type_len = 1;
+
+ /* now, decode length */
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+
+ if (data < 0x80) { /* short form */
+ tlv->length_len = 1;
+ tlv->value_len = data;
+ } else if (data > 0x80) { /* long form */
+ u8_t length_bytes = data - 0x80;
+ if (length_bytes > pbuf_stream->length) {
+ return ERR_VAL;
+ }
+ tlv->length_len = length_bytes + 1; /* this byte + defined number of length bytes following */
+ tlv->value_len = 0;
+
+ while (length_bytes > 0) {
+ /* we only support up to u16.maxvalue-1 (2 bytes) but have to accept leading zero bytes */
+ if (tlv->value_len > 0xFF) {
+ return ERR_VAL;
+ }
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+ tlv->value_len <<= 8;
+ tlv->value_len |= data;
+
+ /* take care for special value used for indefinite length */
+ if (tlv->value_len == 0xFFFF) {
+ return ERR_VAL;
+ }
+
+ length_bytes--;
+ }
+ } else { /* data == 0x80 indefinite length form */
+ /* (not allowed for SNMP; RFC 1157, 3.2.2) */
+ return ERR_VAL;
+ }
+
+ return ERR_OK;
+}
+
+/**
+ * Decodes positive integer (counter, gauge, timeticks) into u32_t.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param len length of the coded integer field
+ * @param value return host order integer
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
+ *
+ * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
+ * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
+ * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
+ */
+err_t
+snmp_asn1_dec_u32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value)
+{
+ u8_t data;
+
+ if ((len > 0) && (len <= 5)) {
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+
+ /* expecting sign bit to be zero, only unsigned please! */
+ if (((len == 5) && (data == 0x00)) || ((len < 5) && ((data & 0x80) == 0))) {
+ *value = data;
+ len--;
+
+ while (len > 0) {
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+ len--;
+
+ *value <<= 8;
+ *value |= data;
+ }
+
+ return ERR_OK;
+ }
+ }
+
+ return ERR_VAL;
+}
+
+/**
+ * Decodes integer into s32_t.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param len length of the coded integer field
+ * @param value return host order integer
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
+ *
+ * @note ASN coded integers are _always_ signed!
+ */
+err_t
+snmp_asn1_dec_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, s32_t *value)
+{
+ u8_t data;
+
+ if ((len > 0) && (len < 5)) {
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+
+ if (data & 0x80) {
+ /* negative, start from -1 */
+ *value = -1;
+ *value = (*value << 8) | data;
+ } else {
+ /* positive, start from 0 */
+ *value = data;
+ }
+ len--;
+ /* shift in the remaining value */
+ while (len > 0) {
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+ *value = (*value << 8) | data;
+ len--;
+ }
+ return ERR_OK;
+ }
+
+ return ERR_VAL;
+}
+
+/**
+ * Decodes object identifier from incoming message into array of u32_t.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param len length of the coded object identifier
+ * @param oid return decoded object identifier
+ * @param oid_len return decoded object identifier length
+ * @param oid_max_len size of oid buffer
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
+ */
+err_t
+snmp_asn1_dec_oid(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *oid, u8_t *oid_len, u8_t oid_max_len)
+{
+ u32_t *oid_ptr;
+ u8_t data;
+
+ *oid_len = 0;
+ oid_ptr = oid;
+ if (len > 0) {
+ if (oid_max_len < 2) {
+ return ERR_MEM;
+ }
+
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+ len--;
+
+ /* first compressed octet */
+ if (data == 0x2B) {
+ /* (most) common case 1.3 (iso.org) */
+ *oid_ptr = 1;
+ oid_ptr++;
+ *oid_ptr = 3;
+ oid_ptr++;
+ } else if (data < 40) {
+ *oid_ptr = 0;
+ oid_ptr++;
+ *oid_ptr = data;
+ oid_ptr++;
+ } else if (data < 80) {
+ *oid_ptr = 1;
+ oid_ptr++;
+ *oid_ptr = data - 40;
+ oid_ptr++;
+ } else {
+ *oid_ptr = 2;
+ oid_ptr++;
+ *oid_ptr = data - 80;
+ oid_ptr++;
+ }
+ *oid_len = 2;
+ } else {
+ /* accepting zero length identifiers e.g. for getnext operation. uncommon but valid */
+ return ERR_OK;
+ }
+
+ while ((len > 0) && (*oid_len < oid_max_len)) {
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+ len--;
+
+ if ((data & 0x80) == 0x00) {
+ /* sub-identifier uses single octet */
+ *oid_ptr = data;
+ } else {
+ /* sub-identifier uses multiple octets */
+ u32_t sub_id = (data & ~0x80);
+ while ((len > 0) && ((data & 0x80) != 0)) {
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+ len--;
+
+ sub_id = (sub_id << 7) + (data & ~0x80);
+ }
+
+ if ((data & 0x80) != 0) {
+ /* "more bytes following" bit still set at end of len */
+ return ERR_VAL;
+ }
+ *oid_ptr = sub_id;
+ }
+ oid_ptr++;
+ (*oid_len)++;
+ }
+
+ if (len > 0) {
+ /* OID to long to fit in our buffer */
+ return ERR_MEM;
+ }
+
+ return ERR_OK;
+}
+
+/**
+ * Decodes (copies) raw data (ip-addresses, octet strings, opaque encoding)
+ * from incoming message into array.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param len length of the coded raw data (zero is valid, e.g. empty string!)
+ * @param buf return raw bytes
+ * @param buf_len returns length of the raw return value
+ * @param buf_max_len buffer size
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
+ */
+err_t
+snmp_asn1_dec_raw(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u8_t *buf, u16_t *buf_len, u16_t buf_max_len)
+{
+ if (len > buf_max_len) {
+ /* not enough dst space */
+ return ERR_MEM;
+ }
+ *buf_len = len;
+
+ while (len > 0) {
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, buf));
+ buf++;
+ len--;
+ }
+
+ return ERR_OK;
+}
+
+#if LWIP_HAVE_INT64
+/**
+ * Returns octet count for an u64_t.
+ *
+ * @param value value to be encoded
+ * @param octets_needed points to the return value
+ *
+ * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
+ * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
+ * of 0xFFFFFFFFFFFFFFFF is preceded with 0x00 and the length is 9 octets!!
+ */
+void
+snmp_asn1_enc_u64t_cnt(u64_t value, u16_t *octets_needed)
+{
+ /* check if high u32 is 0 */
+ if ((value >> 32) == 0) {
+ /* only low u32 is important */
+ snmp_asn1_enc_u32t_cnt((u32_t)value, octets_needed);
+ } else {
+ /* low u32 does not matter for length determination */
+ snmp_asn1_enc_u32t_cnt((u32_t)(value >> 32), octets_needed);
+ *octets_needed = *octets_needed + 4; /* add the 4 bytes of low u32 */
+ }
+}
+
+/**
+ * Decodes large positive integer (counter64) into 2x u32_t.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param len length of the coded integer field
+ * @param value return 64 bit integer
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
+ *
+ * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
+ * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
+ * of 0xFFFFFFFFFFFFFFFF is preceded with 0x00 and the length is 9 octets!!
+ */
+err_t
+snmp_asn1_dec_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u64_t *value)
+{
+ u8_t data;
+
+ if ((len > 0) && (len <= 9)) {
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+
+ /* expecting sign bit to be zero, only unsigned please! */
+ if (((len == 9) && (data == 0x00)) || ((len < 9) && ((data & 0x80) == 0))) {
+ *value = data;
+ len--;
+
+ while (len > 0) {
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+ *value <<= 8;
+ *value |= data;
+ len--;
+ }
+
+ return ERR_OK;
+ }
+ }
+
+ return ERR_VAL;
+}
+
+/**
+ * Encodes u64_t (counter64) into a pbuf chained ASN1 msg.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt())
+ * @param value is the value to be encoded
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
+ *
+ * @see snmp_asn1_enc_u64t_cnt()
+ */
+err_t
+snmp_asn1_enc_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t octets_needed, u64_t value)
+{
+ if (octets_needed > 9) {
+ return ERR_ARG;
+ }
+ if (octets_needed == 9) {
+ /* not enough bits in 'value' add leading 0x00 */
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, 0x00));
+ octets_needed--;
+ }
+
+ while (octets_needed > 1) {
+ octets_needed--;
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3))));
+ }
+
+ /* always write at least one octet (also in case of value == 0) */
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value)));
+
+ return ERR_OK;
+}
+#endif
+
+#endif /* LWIP_SNMP */
diff --git a/resources/ethernet/LwIP/apps/snmp/snmp_asn1.h b/resources/ethernet/LwIP/apps/snmp/snmp_asn1.h
new file mode 100644
index 00000000..c9daf066
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/snmp/snmp_asn1.h
@@ -0,0 +1,113 @@
+/**
+ * @file
+ * Abstract Syntax Notation One (ISO 8824, 8825) codec.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * Copyright (c) 2016 Elias Oenal.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons
+ * Martin Hentschel
+ * Elias Oenal
+ */
+
+#ifndef LWIP_HDR_APPS_SNMP_ASN1_H
+#define LWIP_HDR_APPS_SNMP_ASN1_H
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP
+
+#include "lwip/err.h"
+#include "lwip/apps/snmp_core.h"
+#include "snmp_pbuf_stream.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SNMP_ASN1_TLV_INDEFINITE_LENGTH 0x80
+
+#define SNMP_ASN1_CLASS_MASK 0xC0
+#define SNMP_ASN1_CONTENTTYPE_MASK 0x20
+#define SNMP_ASN1_DATATYPE_MASK 0x1F
+#define SNMP_ASN1_DATATYPE_EXTENDED 0x1F /* DataType indicating that datatype is encoded in following bytes */
+
+/* context specific (SNMP) tags (from SNMP spec. RFC1157 and RFC1905) */
+#define SNMP_ASN1_CONTEXT_PDU_GET_REQ 0
+#define SNMP_ASN1_CONTEXT_PDU_GET_NEXT_REQ 1
+#define SNMP_ASN1_CONTEXT_PDU_GET_RESP 2
+#define SNMP_ASN1_CONTEXT_PDU_SET_REQ 3
+#define SNMP_ASN1_CONTEXT_PDU_TRAP 4
+#define SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ 5
+#define SNMP_ASN1_CONTEXT_PDU_INFORM_REQ 6
+#define SNMP_ASN1_CONTEXT_PDU_V2_TRAP 7
+#define SNMP_ASN1_CONTEXT_PDU_REPORT 8
+
+#define SNMP_ASN1_CONTEXT_VARBIND_NO_SUCH_OBJECT 0
+#define SNMP_ASN1_CONTEXT_VARBIND_END_OF_MIB_VIEW 2
+
+struct snmp_asn1_tlv {
+ u8_t type; /* only U8 because extended types are not specified by SNMP */
+ u8_t type_len; /* encoded length of 'type' field (normally 1) */
+ u8_t length_len; /* indicates how many bytes are required to encode the 'value_len' field */
+ u16_t value_len; /* encoded length of the value */
+};
+#define SNMP_ASN1_TLV_HDR_LENGTH(tlv) ((tlv).type_len + (tlv).length_len)
+#define SNMP_ASN1_TLV_LENGTH(tlv) ((tlv).type_len + (tlv).length_len + (tlv).value_len)
+#define SNMP_ASN1_SET_TLV_PARAMS(tlv, type_, length_len_, value_len_) do { (tlv).type = (type_); (tlv).type_len = 0; (tlv).length_len = (length_len_); (tlv).value_len = (value_len_); } while (0);
+
+err_t snmp_asn1_dec_tlv(struct snmp_pbuf_stream *pbuf_stream, struct snmp_asn1_tlv *tlv);
+err_t snmp_asn1_dec_u32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value);
+err_t snmp_asn1_dec_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, s32_t *value);
+err_t snmp_asn1_dec_oid(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *oid, u8_t *oid_len, u8_t oid_max_len);
+err_t snmp_asn1_dec_raw(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u8_t *buf, u16_t *buf_len, u16_t buf_max_len);
+
+err_t snmp_ans1_enc_tlv(struct snmp_pbuf_stream *pbuf_stream, struct snmp_asn1_tlv *tlv);
+
+void snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed);
+void snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed);
+void snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed);
+void snmp_asn1_enc_oid_cnt(const u32_t *oid, u16_t oid_len, u16_t *octets_needed);
+err_t snmp_asn1_enc_oid(struct snmp_pbuf_stream *pbuf_stream, const u32_t *oid, u16_t oid_len);
+err_t snmp_asn1_enc_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t octets_needed, s32_t value);
+err_t snmp_asn1_enc_u32t(struct snmp_pbuf_stream *pbuf_stream, u16_t octets_needed, u32_t value);
+err_t snmp_asn1_enc_raw(struct snmp_pbuf_stream *pbuf_stream, const u8_t *raw, u16_t raw_len);
+
+#if LWIP_HAVE_INT64
+err_t snmp_asn1_dec_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u64_t *value);
+void snmp_asn1_enc_u64t_cnt(u64_t value, u16_t *octets_needed);
+err_t snmp_asn1_enc_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t octets_needed, u64_t value);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_SNMP */
+
+#endif /* LWIP_HDR_APPS_SNMP_ASN1_H */
diff --git a/resources/ethernet/LwIP/apps/snmp/snmp_core.c b/resources/ethernet/LwIP/apps/snmp/snmp_core.c
new file mode 100644
index 00000000..2b507168
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/snmp/snmp_core.c
@@ -0,0 +1,1353 @@
+/**
+ * @file
+ * MIB tree access/construction functions.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons
+ * Martin Hentschel
+*/
+
+/**
+ * @defgroup snmp SNMPv2c/v3 agent
+ * @ingroup apps
+ * SNMPv2c and SNMPv3 compatible agent\n
+ * There is also a MIB compiler and a MIB viewer in lwIP contrib repository
+ * (lwip-contrib/apps/LwipMibCompiler).\n
+ * The agent implements the most important MIB2 MIBs including IPv6 support
+ * (interfaces, UDP, TCP, SNMP, ICMP, SYSTEM). IP MIB is an older version
+ * without IPv6 statistics (TODO).\n
+ * Rewritten by Martin Hentschel and
+ * Dirk Ziegelmeier \n
+ *
+ * 0 Agent Capabilities
+ * ====================
+ *
+ * Features:
+ * ---------
+ * - SNMPv2c support.
+ * - SNMPv3 support (a port to ARM mbedtls is provided, LWIP_SNMP_V3_MBEDTLS option).
+ * - Low RAM usage - no memory pools, stack only.
+ * - MIB2 implementation is separated from SNMP stack.
+ * - Support for multiple MIBs (snmp_set_mibs() call) - e.g. for private MIB.
+ * - Simple and generic API for MIB implementation.
+ * - Comfortable node types and helper functions for scalar arrays and tables.
+ * - Counter64, bit and truthvalue datatype support.
+ * - Callbacks for SNMP writes e.g. to implement persistency.
+ * - Runs on two APIs: RAW and netconn.
+ * - Async API is gone - the stack now supports netconn API instead,
+ * so blocking operations can be done in MIB calls.
+ * SNMP runs in a worker thread when netconn API is used.
+ * - Simplified thread sync support for MIBs - useful when MIBs
+ * need to access variables shared with other threads where no locking is
+ * possible. Used in MIB2 to access lwIP stats from lwIP thread.
+ *
+ * MIB compiler (code generator):
+ * ------------------------------
+ * - Provided in lwIP contrib repository.
+ * - Written in C#. MIB viewer used Windows Forms.
+ * - Developed on Windows with Visual Studio 2010.
+ * - Can be compiled and used on all platforms with http://www.monodevelop.com/.
+ * - Based on a heavily modified version of of SharpSnmpLib (a4bd05c6afb4)
+ * (https://sharpsnmplib.codeplex.com/SourceControl/network/forks/Nemo157/MIBParserUpdate).
+ * - MIB parser, C file generation framework and LWIP code generation are cleanly
+ * separated, which means the code may be useful as a base for code generation
+ * of other SNMP agents.
+ *
+ * Notes:
+ * ------
+ * - Stack and MIB compiler were used to implement a Profinet device.
+ * Compiled/implemented MIBs: LLDP-MIB, LLDP-EXT-DOT3-MIB, LLDP-EXT-PNO-MIB.
+ *
+ * SNMPv1 per RFC1157 and SNMPv2c per RFC 3416
+ * -------------------------------------------
+ * Note the S in SNMP stands for "Simple". Note that "Simple" is
+ * relative. SNMP is simple compared to the complex ISO network
+ * management protocols CMIP (Common Management Information Protocol)
+ * and CMOT (CMip Over Tcp).
+ *
+ * SNMPv3
+ * ------
+ * When SNMPv3 is used, several functions from snmpv3.h must be implemented
+ * by the user. This is mainly user management and persistence handling.
+ * The sample provided in lwip-contrib is insecure, don't use it in production
+ * systems, especially the missing persistence for engine boots variable
+ * simplifies replay attacks.
+ *
+ * MIB II
+ * ------
+ * The standard lwIP stack management information base.
+ * This is a required MIB, so this is always enabled.
+ * The groups EGP, CMOT and transmission are disabled by default.
+ *
+ * Most mib-2 objects are not writable except:
+ * sysName, sysLocation, sysContact, snmpEnableAuthenTraps.
+ * Writing to or changing the ARP and IP address and route
+ * tables is not possible.
+ *
+ * Note lwIP has a very limited notion of IP routing. It currently
+ * doen't have a route table and doesn't have a notion of the U,G,H flags.
+ * Instead lwIP uses the interface list with only one default interface
+ * acting as a single gateway interface (G) for the default route.
+ *
+ * The agent returns a "virtual table" with the default route 0.0.0.0
+ * for the default interface and network routes (no H) for each
+ * network interface in the netif_list.
+ * All routes are considered to be up (U).
+ *
+ * Loading additional MIBs
+ * -----------------------
+ * MIBs can only be added in compile-time, not in run-time.
+ *
+ *
+ * 1 Building the Agent
+ * ====================
+ * First of all you'll need to add the following define
+ * to your local lwipopts.h:
+ * \#define LWIP_SNMP 1
+ *
+ * and add the source files your makefile.
+ *
+ * Note you'll might need to adapt you network driver to update
+ * the mib2 variables for your interface.
+ *
+ * 2 Running the Agent
+ * ===================
+ * The following function calls must be made in your program to
+ * actually get the SNMP agent running.
+ *
+ * Before starting the agent you should supply pointers
+ * for sysContact, sysLocation, and snmpEnableAuthenTraps.
+ * You can do this by calling
+ *
+ * - snmp_mib2_set_syscontact()
+ * - snmp_mib2_set_syslocation()
+ * - snmp_set_auth_traps_enabled()
+ *
+ * You can register a callback which is called on successful write access:
+ * snmp_set_write_callback().
+ *
+ * Additionally you may want to set
+ *
+ * - snmp_mib2_set_sysdescr()
+ * - snmp_set_device_enterprise_oid()
+ * - snmp_mib2_set_sysname()
+ *
+ * Also before starting the agent you need to setup
+ * one or more trap destinations using these calls:
+ *
+ * - snmp_trap_dst_enable()
+ * - snmp_trap_dst_ip_set()
+ *
+ * If you need more than MIB2, set the MIBs you want to use
+ * by snmp_set_mibs().
+ *
+ * Finally, enable the agent by calling snmp_init()
+ *
+ * @defgroup snmp_core Core
+ * @ingroup snmp
+ *
+ * @defgroup snmp_traps Traps
+ * @ingroup snmp
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "snmp_core_priv.h"
+#include "lwip/netif.h"
+#include
+
+
+#if (LWIP_SNMP && (SNMP_TRAP_DESTINATIONS<=0))
+#error "If you want to use SNMP, you have to define SNMP_TRAP_DESTINATIONS>=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_SNMP)
+#error "If you want to use SNMP, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if SNMP_MAX_OBJ_ID_LEN > 255
+#error "SNMP_MAX_OBJ_ID_LEN must fit into an u8_t"
+#endif
+
+struct snmp_statistics snmp_stats;
+static const struct snmp_obj_id snmp_device_enterprise_oid_default = {SNMP_DEVICE_ENTERPRISE_OID_LEN, SNMP_DEVICE_ENTERPRISE_OID};
+static const struct snmp_obj_id *snmp_device_enterprise_oid = &snmp_device_enterprise_oid_default;
+
+const u32_t snmp_zero_dot_zero_values[] = { 0, 0 };
+const struct snmp_obj_id_const_ref snmp_zero_dot_zero = { LWIP_ARRAYSIZE(snmp_zero_dot_zero_values), snmp_zero_dot_zero_values };
+
+#if SNMP_LWIP_MIB2 && LWIP_SNMP_V3
+#include "lwip/apps/snmp_mib2.h"
+#include "lwip/apps/snmp_snmpv2_framework.h"
+#include "lwip/apps/snmp_snmpv2_usm.h"
+static const struct snmp_mib *const default_mibs[] = { &mib2, &snmpframeworkmib, &snmpusmmib };
+static u8_t snmp_num_mibs = LWIP_ARRAYSIZE(default_mibs);
+#elif SNMP_LWIP_MIB2
+#include "lwip/apps/snmp_mib2.h"
+static const struct snmp_mib *const default_mibs[] = { &mib2 };
+static u8_t snmp_num_mibs = LWIP_ARRAYSIZE(default_mibs);
+#else
+static const struct snmp_mib *const default_mibs[] = { NULL };
+static u8_t snmp_num_mibs = 0;
+#endif
+
+/* List of known mibs */
+static struct snmp_mib const *const *snmp_mibs = default_mibs;
+
+/**
+ * @ingroup snmp_core
+ * Sets the MIBs to use.
+ * Example: call snmp_set_mibs() as follows:
+ * static const struct snmp_mib *my_snmp_mibs[] = {
+ * &mib2,
+ * &private_mib
+ * };
+ * snmp_set_mibs(my_snmp_mibs, LWIP_ARRAYSIZE(my_snmp_mibs));
+ */
+void
+snmp_set_mibs(const struct snmp_mib **mibs, u8_t num_mibs)
+{
+ LWIP_ASSERT_CORE_LOCKED();
+ LWIP_ASSERT("mibs pointer must be != NULL", (mibs != NULL));
+ LWIP_ASSERT("num_mibs pointer must be != 0", (num_mibs != 0));
+ snmp_mibs = mibs;
+ snmp_num_mibs = num_mibs;
+}
+
+/**
+ * @ingroup snmp_core
+ * 'device enterprise oid' is used for 'device OID' field in trap PDU's (for identification of generating device)
+ * as well as for value returned by MIB-2 'sysObjectID' field (if internal MIB2 implementation is used).
+ * The 'device enterprise oid' shall point to an OID located under 'private-enterprises' branch (1.3.6.1.4.1.XXX). If a vendor
+ * wants to provide a custom object there, he has to get its own enterprise oid from IANA (http://www.iana.org). It
+ * is not allowed to use LWIP enterprise ID!
+ * In order to identify a specific device it is recommended to create a dedicated OID for each device type under its own
+ * enterprise oid.
+ * e.g.
+ * device a > 1.3.6.1.4.1.XXX(ent-oid).1(devices).1(device a)
+ * device b > 1.3.6.1.4.1.XXX(ent-oid).1(devices).2(device b)
+ * for more details see description of 'sysObjectID' field in RFC1213-MIB
+ */
+void snmp_set_device_enterprise_oid(const struct snmp_obj_id *device_enterprise_oid)
+{
+ LWIP_ASSERT_CORE_LOCKED();
+ if (device_enterprise_oid == NULL) {
+ snmp_device_enterprise_oid = &snmp_device_enterprise_oid_default;
+ } else {
+ snmp_device_enterprise_oid = device_enterprise_oid;
+ }
+}
+
+/**
+ * @ingroup snmp_core
+ * Get 'device enterprise oid'
+ */
+const struct snmp_obj_id *snmp_get_device_enterprise_oid(void)
+{
+ LWIP_ASSERT_CORE_LOCKED();
+ return snmp_device_enterprise_oid;
+}
+
+#if LWIP_IPV4
+/**
+ * Conversion from InetAddressIPv4 oid to lwIP ip4_addr
+ * @param oid points to u32_t ident[4] input
+ * @param ip points to output struct
+ */
+u8_t
+snmp_oid_to_ip4(const u32_t *oid, ip4_addr_t *ip)
+{
+ if ((oid[0] > 0xFF) ||
+ (oid[1] > 0xFF) ||
+ (oid[2] > 0xFF) ||
+ (oid[3] > 0xFF)) {
+ ip4_addr_copy(*ip, *IP4_ADDR_ANY4);
+ return 0;
+ }
+
+ IP4_ADDR(ip, oid[0], oid[1], oid[2], oid[3]);
+ return 1;
+}
+
+/**
+ * Convert ip4_addr to InetAddressIPv4 (no InetAddressType)
+ * @param ip points to input struct
+ * @param oid points to u32_t ident[4] output
+ */
+void
+snmp_ip4_to_oid(const ip4_addr_t *ip, u32_t *oid)
+{
+ oid[0] = ip4_addr1(ip);
+ oid[1] = ip4_addr2(ip);
+ oid[2] = ip4_addr3(ip);
+ oid[3] = ip4_addr4(ip);
+}
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV6
+/**
+ * Conversion from InetAddressIPv6 oid to lwIP ip6_addr
+ * @param oid points to u32_t oid[16] input
+ * @param ip points to output struct
+ */
+u8_t
+snmp_oid_to_ip6(const u32_t *oid, ip6_addr_t *ip)
+{
+ if ((oid[0] > 0xFF) ||
+ (oid[1] > 0xFF) ||
+ (oid[2] > 0xFF) ||
+ (oid[3] > 0xFF) ||
+ (oid[4] > 0xFF) ||
+ (oid[5] > 0xFF) ||
+ (oid[6] > 0xFF) ||
+ (oid[7] > 0xFF) ||
+ (oid[8] > 0xFF) ||
+ (oid[9] > 0xFF) ||
+ (oid[10] > 0xFF) ||
+ (oid[11] > 0xFF) ||
+ (oid[12] > 0xFF) ||
+ (oid[13] > 0xFF) ||
+ (oid[14] > 0xFF) ||
+ (oid[15] > 0xFF)) {
+ ip6_addr_set_any(ip);
+ return 0;
+ }
+
+ ip->addr[0] = (oid[0] << 24) | (oid[1] << 16) | (oid[2] << 8) | (oid[3] << 0);
+ ip->addr[1] = (oid[4] << 24) | (oid[5] << 16) | (oid[6] << 8) | (oid[7] << 0);
+ ip->addr[2] = (oid[8] << 24) | (oid[9] << 16) | (oid[10] << 8) | (oid[11] << 0);
+ ip->addr[3] = (oid[12] << 24) | (oid[13] << 16) | (oid[14] << 8) | (oid[15] << 0);
+ return 1;
+}
+
+/**
+ * Convert ip6_addr to InetAddressIPv6 (no InetAddressType)
+ * @param ip points to input struct
+ * @param oid points to u32_t ident[16] output
+ */
+void
+snmp_ip6_to_oid(const ip6_addr_t *ip, u32_t *oid)
+{
+ oid[0] = (ip->addr[0] & 0xFF000000) >> 24;
+ oid[1] = (ip->addr[0] & 0x00FF0000) >> 16;
+ oid[2] = (ip->addr[0] & 0x0000FF00) >> 8;
+ oid[3] = (ip->addr[0] & 0x000000FF) >> 0;
+ oid[4] = (ip->addr[1] & 0xFF000000) >> 24;
+ oid[5] = (ip->addr[1] & 0x00FF0000) >> 16;
+ oid[6] = (ip->addr[1] & 0x0000FF00) >> 8;
+ oid[7] = (ip->addr[1] & 0x000000FF) >> 0;
+ oid[8] = (ip->addr[2] & 0xFF000000) >> 24;
+ oid[9] = (ip->addr[2] & 0x00FF0000) >> 16;
+ oid[10] = (ip->addr[2] & 0x0000FF00) >> 8;
+ oid[11] = (ip->addr[2] & 0x000000FF) >> 0;
+ oid[12] = (ip->addr[3] & 0xFF000000) >> 24;
+ oid[13] = (ip->addr[3] & 0x00FF0000) >> 16;
+ oid[14] = (ip->addr[3] & 0x0000FF00) >> 8;
+ oid[15] = (ip->addr[3] & 0x000000FF) >> 0;
+}
+#endif /* LWIP_IPV6 */
+
+#if LWIP_IPV4 || LWIP_IPV6
+/**
+ * Convert to InetAddressType+InetAddress+InetPortNumber
+ * @param ip IP address
+ * @param port Port
+ * @param oid OID
+ * @return OID length
+ */
+u8_t
+snmp_ip_port_to_oid(const ip_addr_t *ip, u16_t port, u32_t *oid)
+{
+ u8_t idx;
+
+ idx = snmp_ip_to_oid(ip, oid);
+ oid[idx] = port;
+ idx++;
+
+ return idx;
+}
+
+/**
+ * Convert to InetAddressType+InetAddress
+ * @param ip IP address
+ * @param oid OID
+ * @return OID length
+ */
+u8_t
+snmp_ip_to_oid(const ip_addr_t *ip, u32_t *oid)
+{
+ if (IP_IS_ANY_TYPE_VAL(*ip)) {
+ oid[0] = 0; /* any */
+ oid[1] = 0; /* no IP OIDs follow */
+ return 2;
+ } else if (IP_IS_V6(ip)) {
+#if LWIP_IPV6
+ oid[0] = 2; /* ipv6 */
+ oid[1] = 16; /* 16 InetAddressIPv6 OIDs follow */
+ snmp_ip6_to_oid(ip_2_ip6(ip), &oid[2]);
+ return 18;
+#else /* LWIP_IPV6 */
+ return 0;
+#endif /* LWIP_IPV6 */
+ } else {
+#if LWIP_IPV4
+ oid[0] = 1; /* ipv4 */
+ oid[1] = 4; /* 4 InetAddressIPv4 OIDs follow */
+ snmp_ip4_to_oid(ip_2_ip4(ip), &oid[2]);
+ return 6;
+#else /* LWIP_IPV4 */
+ return 0;
+#endif /* LWIP_IPV4 */
+ }
+}
+
+/**
+ * Convert from InetAddressType+InetAddress to ip_addr_t
+ * @param oid OID
+ * @param oid_len OID length
+ * @param ip IP address
+ * @return Parsed OID length
+ */
+u8_t
+snmp_oid_to_ip(const u32_t *oid, u8_t oid_len, ip_addr_t *ip)
+{
+ /* InetAddressType */
+ if (oid_len < 1) {
+ return 0;
+ }
+
+ if (oid[0] == 0) { /* any */
+ /* 1x InetAddressType, 1x OID len */
+ if (oid_len < 2) {
+ return 0;
+ }
+ if (oid[1] != 0) {
+ return 0;
+ }
+
+ memset(ip, 0, sizeof(*ip));
+ IP_SET_TYPE(ip, IPADDR_TYPE_ANY);
+
+ return 2;
+ } else if (oid[0] == 1) { /* ipv4 */
+#if LWIP_IPV4
+ /* 1x InetAddressType, 1x OID len, 4x InetAddressIPv4 */
+ if (oid_len < 6) {
+ return 0;
+ }
+
+ /* 4x ipv4 OID */
+ if (oid[1] != 4) {
+ return 0;
+ }
+
+ IP_SET_TYPE(ip, IPADDR_TYPE_V4);
+ if (!snmp_oid_to_ip4(&oid[2], ip_2_ip4(ip))) {
+ return 0;
+ }
+
+ return 6;
+#else /* LWIP_IPV4 */
+ return 0;
+#endif /* LWIP_IPV4 */
+ } else if (oid[0] == 2) { /* ipv6 */
+#if LWIP_IPV6
+ /* 1x InetAddressType, 1x OID len, 16x InetAddressIPv6 */
+ if (oid_len < 18) {
+ return 0;
+ }
+
+ /* 16x ipv6 OID */
+ if (oid[1] != 16) {
+ return 0;
+ }
+
+ IP_SET_TYPE(ip, IPADDR_TYPE_V6);
+ if (!snmp_oid_to_ip6(&oid[2], ip_2_ip6(ip))) {
+ return 0;
+ }
+
+ return 18;
+#else /* LWIP_IPV6 */
+ return 0;
+#endif /* LWIP_IPV6 */
+ } else { /* unsupported InetAddressType */
+ return 0;
+ }
+}
+
+/**
+ * Convert from InetAddressType+InetAddress+InetPortNumber to ip_addr_t and u16_t
+ * @param oid OID
+ * @param oid_len OID length
+ * @param ip IP address
+ * @param port Port
+ * @return Parsed OID length
+ */
+u8_t
+snmp_oid_to_ip_port(const u32_t *oid, u8_t oid_len, ip_addr_t *ip, u16_t *port)
+{
+ u8_t idx;
+
+ /* InetAddressType + InetAddress */
+ idx = snmp_oid_to_ip(&oid[0], oid_len, ip);
+ if (idx == 0) {
+ return 0;
+ }
+
+ /* InetPortNumber */
+ if (oid_len < (idx + 1)) {
+ return 0;
+ }
+ if (oid[idx] > 0xffff) {
+ return 0;
+ }
+ *port = (u16_t)oid[idx];
+ idx++;
+
+ return idx;
+}
+
+#endif /* LWIP_IPV4 || LWIP_IPV6 */
+
+/**
+ * Assign an OID to struct snmp_obj_id
+ * @param target Assignment target
+ * @param oid OID
+ * @param oid_len OID length
+ */
+void
+snmp_oid_assign(struct snmp_obj_id *target, const u32_t *oid, u8_t oid_len)
+{
+ LWIP_ASSERT("oid_len <= SNMP_MAX_OBJ_ID_LEN", oid_len <= SNMP_MAX_OBJ_ID_LEN);
+
+ target->len = oid_len;
+
+ if (oid_len > 0) {
+ MEMCPY(target->id, oid, oid_len * sizeof(u32_t));
+ }
+}
+
+/**
+ * Prefix an OID to OID in struct snmp_obj_id
+ * @param target Assignment target to prefix
+ * @param oid OID
+ * @param oid_len OID length
+ */
+void
+snmp_oid_prefix(struct snmp_obj_id *target, const u32_t *oid, u8_t oid_len)
+{
+ LWIP_ASSERT("target->len + oid_len <= SNMP_MAX_OBJ_ID_LEN", (target->len + oid_len) <= SNMP_MAX_OBJ_ID_LEN);
+
+ if (oid_len > 0) {
+ /* move existing OID to make room at the beginning for OID to insert */
+ int i;
+ for (i = target->len - 1; i >= 0; i--) {
+ target->id[i + oid_len] = target->id[i];
+ }
+
+ /* paste oid at the beginning */
+ MEMCPY(target->id, oid, oid_len * sizeof(u32_t));
+ }
+}
+
+/**
+ * Combine two OIDs into struct snmp_obj_id
+ * @param target Assignmet target
+ * @param oid1 OID 1
+ * @param oid1_len OID 1 length
+ * @param oid2 OID 2
+ * @param oid2_len OID 2 length
+ */
+void
+snmp_oid_combine(struct snmp_obj_id *target, const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len)
+{
+ snmp_oid_assign(target, oid1, oid1_len);
+ snmp_oid_append(target, oid2, oid2_len);
+}
+
+/**
+ * Append OIDs to struct snmp_obj_id
+ * @param target Assignment target to append to
+ * @param oid OID
+ * @param oid_len OID length
+ */
+void
+snmp_oid_append(struct snmp_obj_id *target, const u32_t *oid, u8_t oid_len)
+{
+ LWIP_ASSERT("offset + oid_len <= SNMP_MAX_OBJ_ID_LEN", (target->len + oid_len) <= SNMP_MAX_OBJ_ID_LEN);
+
+ if (oid_len > 0) {
+ MEMCPY(&target->id[target->len], oid, oid_len * sizeof(u32_t));
+ target->len = (u8_t)(target->len + oid_len);
+ }
+}
+
+/**
+ * Compare two OIDs
+ * @param oid1 OID 1
+ * @param oid1_len OID 1 length
+ * @param oid2 OID 2
+ * @param oid2_len OID 2 length
+ * @return -1: OID1<OID2 1: OID1 >OID2 0: equal
+ */
+s8_t
+snmp_oid_compare(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len)
+{
+ u8_t level = 0;
+ LWIP_ASSERT("'oid1' param must not be NULL or 'oid1_len' param be 0!", (oid1 != NULL) || (oid1_len == 0));
+ LWIP_ASSERT("'oid2' param must not be NULL or 'oid2_len' param be 0!", (oid2 != NULL) || (oid2_len == 0));
+
+ while ((level < oid1_len) && (level < oid2_len)) {
+ if (*oid1 < *oid2) {
+ return -1;
+ }
+ if (*oid1 > *oid2) {
+ return 1;
+ }
+
+ level++;
+ oid1++;
+ oid2++;
+ }
+
+ /* common part of both OID's is equal, compare length */
+ if (oid1_len < oid2_len) {
+ return -1;
+ }
+ if (oid1_len > oid2_len) {
+ return 1;
+ }
+
+ /* they are equal */
+ return 0;
+}
+
+
+/**
+ * Check of two OIDs are equal
+ * @param oid1 OID 1
+ * @param oid1_len OID 1 length
+ * @param oid2 OID 2
+ * @param oid2_len OID 2 length
+ * @return 1: equal 0: non-equal
+ */
+u8_t
+snmp_oid_equal(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len)
+{
+ return (snmp_oid_compare(oid1, oid1_len, oid2, oid2_len) == 0) ? 1 : 0;
+}
+
+/**
+ * Convert netif to interface index
+ * @param netif netif
+ * @return index
+ */
+u8_t
+netif_to_num(const struct netif *netif)
+{
+ return netif_get_index(netif);
+}
+
+static const struct snmp_mib *
+snmp_get_mib_from_oid(const u32_t *oid, u8_t oid_len)
+{
+ const u32_t *list_oid;
+ const u32_t *searched_oid;
+ u8_t i, l;
+
+ u8_t max_match_len = 0;
+ const struct snmp_mib *matched_mib = NULL;
+
+ LWIP_ASSERT("'oid' param must not be NULL!", (oid != NULL));
+
+ if (oid_len == 0) {
+ return NULL;
+ }
+
+ for (i = 0; i < snmp_num_mibs; i++) {
+ LWIP_ASSERT("MIB array not initialized correctly", (snmp_mibs[i] != NULL));
+ LWIP_ASSERT("MIB array not initialized correctly - base OID is NULL", (snmp_mibs[i]->base_oid != NULL));
+
+ if (oid_len >= snmp_mibs[i]->base_oid_len) {
+ l = snmp_mibs[i]->base_oid_len;
+ list_oid = snmp_mibs[i]->base_oid;
+ searched_oid = oid;
+
+ while (l > 0) {
+ if (*list_oid != *searched_oid) {
+ break;
+ }
+
+ l--;
+ list_oid++;
+ searched_oid++;
+ }
+
+ if ((l == 0) && (snmp_mibs[i]->base_oid_len > max_match_len)) {
+ max_match_len = snmp_mibs[i]->base_oid_len;
+ matched_mib = snmp_mibs[i];
+ }
+ }
+ }
+
+ return matched_mib;
+}
+
+static const struct snmp_mib *
+snmp_get_next_mib(const u32_t *oid, u8_t oid_len)
+{
+ u8_t i;
+ const struct snmp_mib *next_mib = NULL;
+
+ LWIP_ASSERT("'oid' param must not be NULL!", (oid != NULL));
+
+ if (oid_len == 0) {
+ return NULL;
+ }
+
+ for (i = 0; i < snmp_num_mibs; i++) {
+ if (snmp_mibs[i]->base_oid != NULL) {
+ /* check if mib is located behind starting point */
+ if (snmp_oid_compare(snmp_mibs[i]->base_oid, snmp_mibs[i]->base_oid_len, oid, oid_len) > 0) {
+ if ((next_mib == NULL) ||
+ (snmp_oid_compare(snmp_mibs[i]->base_oid, snmp_mibs[i]->base_oid_len,
+ next_mib->base_oid, next_mib->base_oid_len) < 0)) {
+ next_mib = snmp_mibs[i];
+ }
+ }
+ }
+ }
+
+ return next_mib;
+}
+
+static const struct snmp_mib *
+snmp_get_mib_between(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len)
+{
+ const struct snmp_mib *next_mib = snmp_get_next_mib(oid1, oid1_len);
+
+ LWIP_ASSERT("'oid2' param must not be NULL!", (oid2 != NULL));
+ LWIP_ASSERT("'oid2_len' param must be greater than 0!", (oid2_len > 0));
+
+ if (next_mib != NULL) {
+ if (snmp_oid_compare(next_mib->base_oid, next_mib->base_oid_len, oid2, oid2_len) < 0) {
+ return next_mib;
+ }
+ }
+
+ return NULL;
+}
+
+u8_t
+snmp_get_node_instance_from_oid(const u32_t *oid, u8_t oid_len, struct snmp_node_instance *node_instance)
+{
+ u8_t result = SNMP_ERR_NOSUCHOBJECT;
+ const struct snmp_mib *mib;
+ const struct snmp_node *mn = NULL;
+
+ mib = snmp_get_mib_from_oid(oid, oid_len);
+ if (mib != NULL) {
+ u8_t oid_instance_len;
+
+ mn = snmp_mib_tree_resolve_exact(mib, oid, oid_len, &oid_instance_len);
+ if ((mn != NULL) && (mn->node_type != SNMP_NODE_TREE)) {
+ /* get instance */
+ const struct snmp_leaf_node *leaf_node = (const struct snmp_leaf_node *)(const void *)mn;
+
+ node_instance->node = mn;
+ snmp_oid_assign(&node_instance->instance_oid, oid + (oid_len - oid_instance_len), oid_instance_len);
+
+ result = leaf_node->get_instance(
+ oid,
+ oid_len - oid_instance_len,
+ node_instance);
+
+#ifdef LWIP_DEBUG
+ if (result == SNMP_ERR_NOERROR) {
+ if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_READ) != 0) && (node_instance->get_value == NULL)) {
+ LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is readable but no get_value function is specified\n"));
+ }
+ if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_WRITE) != 0) && (node_instance->set_value == NULL)) {
+ LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is writable but no set_value and/or set_test function is specified\n"));
+ }
+ }
+#endif
+ }
+ }
+
+ return result;
+}
+
+u8_t
+snmp_get_next_node_instance_from_oid(const u32_t *oid, u8_t oid_len, snmp_validate_node_instance_method validate_node_instance_method, void *validate_node_instance_arg, struct snmp_obj_id *node_oid, struct snmp_node_instance *node_instance)
+{
+ const struct snmp_mib *mib;
+ const struct snmp_node *mn = NULL;
+ const u32_t *start_oid = NULL;
+ u8_t start_oid_len = 0;
+
+ /* resolve target MIB from passed OID */
+ mib = snmp_get_mib_from_oid(oid, oid_len);
+ if (mib == NULL) {
+ /* passed OID does not reference any known MIB, start at the next closest MIB */
+ mib = snmp_get_next_mib(oid, oid_len);
+
+ if (mib != NULL) {
+ start_oid = mib->base_oid;
+ start_oid_len = mib->base_oid_len;
+ }
+ } else {
+ start_oid = oid;
+ start_oid_len = oid_len;
+ }
+
+ /* resolve target node from MIB, skip to next MIB if no suitable node is found in current MIB */
+ while ((mib != NULL) && (mn == NULL)) {
+ u8_t oid_instance_len;
+
+ /* check if OID directly references a node inside current MIB, in this case we have to ask this node for the next instance */
+ mn = snmp_mib_tree_resolve_exact(mib, start_oid, start_oid_len, &oid_instance_len);
+ if (mn != NULL) {
+ snmp_oid_assign(node_oid, start_oid, start_oid_len - oid_instance_len); /* set oid to node */
+ snmp_oid_assign(&node_instance->instance_oid, start_oid + (start_oid_len - oid_instance_len), oid_instance_len); /* set (relative) instance oid */
+ } else {
+ /* OID does not reference a node, search for the next closest node inside MIB; set instance_oid.len to zero because we want the first instance of this node */
+ mn = snmp_mib_tree_resolve_next(mib, start_oid, start_oid_len, node_oid);
+ node_instance->instance_oid.len = 0;
+ }
+
+ /* validate the node; if the node has no further instance or the returned instance is invalid, search for the next in MIB and validate again */
+ node_instance->node = mn;
+ while (mn != NULL) {
+ u8_t result;
+
+ /* clear fields which may have values from previous loops */
+ node_instance->asn1_type = 0;
+ node_instance->access = SNMP_NODE_INSTANCE_NOT_ACCESSIBLE;
+ node_instance->get_value = NULL;
+ node_instance->set_test = NULL;
+ node_instance->set_value = NULL;
+ node_instance->release_instance = NULL;
+ node_instance->reference.ptr = NULL;
+ node_instance->reference_len = 0;
+
+ result = ((const struct snmp_leaf_node *)(const void *)mn)->get_next_instance(
+ node_oid->id,
+ node_oid->len,
+ node_instance);
+
+ if (result == SNMP_ERR_NOERROR) {
+#ifdef LWIP_DEBUG
+ if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_READ) != 0) && (node_instance->get_value == NULL)) {
+ LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is readable but no get_value function is specified\n"));
+ }
+ if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_WRITE) != 0) && (node_instance->set_value == NULL)) {
+ LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is writable but no set_value function is specified\n"));
+ }
+#endif
+
+ /* validate node because the node may be not accessible for example (but let the caller decide what is valid */
+ if ((validate_node_instance_method == NULL) ||
+ (validate_node_instance_method(node_instance, validate_node_instance_arg) == SNMP_ERR_NOERROR)) {
+ /* node_oid "returns" the full result OID (including the instance part) */
+ snmp_oid_append(node_oid, node_instance->instance_oid.id, node_instance->instance_oid.len);
+ break;
+ }
+
+ if (node_instance->release_instance != NULL) {
+ node_instance->release_instance(node_instance);
+ }
+ /*
+ the instance itself is not valid, ask for next instance from same node.
+ we don't have to change any variables because node_instance->instance_oid is used as input (starting point)
+ as well as output (resulting next OID), so we have to simply call get_next_instance method again
+ */
+ } else {
+ if (node_instance->release_instance != NULL) {
+ node_instance->release_instance(node_instance);
+ }
+
+ /* the node has no further instance, skip to next node */
+ mn = snmp_mib_tree_resolve_next(mib, node_oid->id, node_oid->len, &node_instance->instance_oid); /* misuse node_instance->instance_oid as tmp buffer */
+ if (mn != NULL) {
+ /* prepare for next loop */
+ snmp_oid_assign(node_oid, node_instance->instance_oid.id, node_instance->instance_oid.len);
+ node_instance->instance_oid.len = 0;
+ node_instance->node = mn;
+ }
+ }
+ }
+
+ if (mn != NULL) {
+ /*
+ we found a suitable next node,
+ now we have to check if a inner MIB is located between the searched OID and the resulting OID.
+ this is possible because MIB's may be located anywhere in the global tree, that means also in
+ the subtree of another MIB (e.g. if searched OID is .2 and resulting OID is .4, then another
+ MIB having .3 as root node may exist)
+ */
+ const struct snmp_mib *intermediate_mib;
+ intermediate_mib = snmp_get_mib_between(start_oid, start_oid_len, node_oid->id, node_oid->len);
+
+ if (intermediate_mib != NULL) {
+ /* search for first node inside intermediate mib in next loop */
+ if (node_instance->release_instance != NULL) {
+ node_instance->release_instance(node_instance);
+ }
+
+ mn = NULL;
+ mib = intermediate_mib;
+ start_oid = mib->base_oid;
+ start_oid_len = mib->base_oid_len;
+ }
+ /* else { we found out target node } */
+ } else {
+ /*
+ there is no further (suitable) node inside this MIB, search for the next MIB with following priority
+ 1. search for inner MIB's (whose root is located inside tree of current MIB)
+ 2. search for surrouding MIB's (where the current MIB is the inner MIB) and continue there if any
+ 3. take the next closest MIB (not being related to the current MIB)
+ */
+ const struct snmp_mib *next_mib;
+ next_mib = snmp_get_next_mib(start_oid, start_oid_len); /* returns MIB's related to point 1 and 3 */
+
+ /* is the found MIB an inner MIB? (point 1) */
+ if ((next_mib != NULL) && (next_mib->base_oid_len > mib->base_oid_len) &&
+ (snmp_oid_compare(next_mib->base_oid, mib->base_oid_len, mib->base_oid, mib->base_oid_len) == 0)) {
+ /* yes it is -> continue at inner MIB */
+ mib = next_mib;
+ start_oid = mib->base_oid;
+ start_oid_len = mib->base_oid_len;
+ } else {
+ /* check if there is a surrounding mib where to continue (point 2) (only possible if OID length > 1) */
+ if (mib->base_oid_len > 1) {
+ mib = snmp_get_mib_from_oid(mib->base_oid, mib->base_oid_len - 1);
+
+ if (mib == NULL) {
+ /* no surrounding mib, use next mib encountered above (point 3) */
+ mib = next_mib;
+
+ if (mib != NULL) {
+ start_oid = mib->base_oid;
+ start_oid_len = mib->base_oid_len;
+ }
+ }
+ /* else { start_oid stays the same because we want to continue from current offset in surrounding mib (point 2) } */
+ }
+ }
+ }
+ }
+
+ if (mib == NULL) {
+ /* loop is only left when mib == null (error) or mib_node != NULL (success) */
+ return SNMP_ERR_ENDOFMIBVIEW;
+ }
+
+ return SNMP_ERR_NOERROR;
+}
+
+/**
+ * Searches tree for the supplied object identifier.
+ *
+ */
+const struct snmp_node *
+snmp_mib_tree_resolve_exact(const struct snmp_mib *mib, const u32_t *oid, u8_t oid_len, u8_t *oid_instance_len)
+{
+ const struct snmp_node *const *node = &mib->root_node;
+ u8_t oid_offset = mib->base_oid_len;
+
+ while ((oid_offset < oid_len) && ((*node)->node_type == SNMP_NODE_TREE)) {
+ /* search for matching sub node */
+ u32_t subnode_oid = *(oid + oid_offset);
+
+ u32_t i = (*(const struct snmp_tree_node * const *)node)->subnode_count;
+ node = (*(const struct snmp_tree_node * const *)node)->subnodes;
+ while ((i > 0) && ((*node)->oid != subnode_oid)) {
+ node++;
+ i--;
+ }
+
+ if (i == 0) {
+ /* no matching subnode found */
+ return NULL;
+ }
+
+ oid_offset++;
+ }
+
+ if ((*node)->node_type != SNMP_NODE_TREE) {
+ /* we found a leaf node */
+ *oid_instance_len = oid_len - oid_offset;
+ return (*node);
+ }
+
+ return NULL;
+}
+
+const struct snmp_node *
+snmp_mib_tree_resolve_next(const struct snmp_mib *mib, const u32_t *oid, u8_t oid_len, struct snmp_obj_id *oidret)
+{
+ u8_t oid_offset = mib->base_oid_len;
+ const struct snmp_node *const *node;
+ const struct snmp_tree_node *node_stack[SNMP_MAX_OBJ_ID_LEN];
+ s32_t nsi = 0; /* NodeStackIndex */
+ u32_t subnode_oid;
+
+ if (mib->root_node->node_type != SNMP_NODE_TREE) {
+ /* a next operation on a mib with only a leaf node will always return NULL because there is no other node */
+ return NULL;
+ }
+
+ /* first build node stack related to passed oid (as far as possible), then go backwards to determine the next node */
+ node_stack[nsi] = (const struct snmp_tree_node *)(const void *)mib->root_node;
+ while (oid_offset < oid_len) {
+ /* search for matching sub node */
+ u32_t i = node_stack[nsi]->subnode_count;
+ node = node_stack[nsi]->subnodes;
+
+ subnode_oid = *(oid + oid_offset);
+
+ while ((i > 0) && ((*node)->oid != subnode_oid)) {
+ node++;
+ i--;
+ }
+
+ if ((i == 0) || ((*node)->node_type != SNMP_NODE_TREE)) {
+ /* no (matching) tree-subnode found */
+ break;
+ }
+ nsi++;
+ node_stack[nsi] = (const struct snmp_tree_node *)(const void *)(*node);
+
+ oid_offset++;
+ }
+
+
+ if (oid_offset >= oid_len) {
+ /* passed oid references a tree node -> return first useable sub node of it */
+ subnode_oid = 0;
+ } else {
+ subnode_oid = *(oid + oid_offset) + 1;
+ }
+
+ while (nsi >= 0) {
+ const struct snmp_node *subnode = NULL;
+
+ /* find next node on current level */
+ s32_t i = node_stack[nsi]->subnode_count;
+ node = node_stack[nsi]->subnodes;
+ while (i > 0) {
+ if ((*node)->oid == subnode_oid) {
+ subnode = *node;
+ break;
+ } else if (((*node)->oid > subnode_oid) && ((subnode == NULL) || ((*node)->oid < subnode->oid))) {
+ subnode = *node;
+ }
+
+ node++;
+ i--;
+ }
+
+ if (subnode == NULL) {
+ /* no further node found on this level, go one level up and start searching with index of current node*/
+ subnode_oid = node_stack[nsi]->node.oid + 1;
+ nsi--;
+ } else {
+ if (subnode->node_type == SNMP_NODE_TREE) {
+ /* next is a tree node, go into it and start searching */
+ nsi++;
+ node_stack[nsi] = (const struct snmp_tree_node *)(const void *)subnode;
+ subnode_oid = 0;
+ } else {
+ /* we found a leaf node -> fill oidret and return it */
+ snmp_oid_assign(oidret, mib->base_oid, mib->base_oid_len);
+ i = 1;
+ while (i <= nsi) {
+ oidret->id[oidret->len] = node_stack[i]->node.oid;
+ oidret->len++;
+ i++;
+ }
+
+ oidret->id[oidret->len] = subnode->oid;
+ oidret->len++;
+
+ return subnode;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/** initialize struct next_oid_state using this function before passing it to next_oid_check */
+void
+snmp_next_oid_init(struct snmp_next_oid_state *state,
+ const u32_t *start_oid, u8_t start_oid_len,
+ u32_t *next_oid_buf, u8_t next_oid_max_len)
+{
+ state->start_oid = start_oid;
+ state->start_oid_len = start_oid_len;
+ state->next_oid = next_oid_buf;
+ state->next_oid_len = 0;
+ state->next_oid_max_len = next_oid_max_len;
+ state->status = SNMP_NEXT_OID_STATUS_NO_MATCH;
+}
+
+/** checks if the passed incomplete OID may be a possible candidate for snmp_next_oid_check();
+this methid is intended if the complete OID is not yet known but it is very expensive to build it up,
+so it is possible to test the starting part before building up the complete oid and pass it to snmp_next_oid_check()*/
+u8_t
+snmp_next_oid_precheck(struct snmp_next_oid_state *state, const u32_t *oid, u8_t oid_len)
+{
+ if (state->status != SNMP_NEXT_OID_STATUS_BUF_TO_SMALL) {
+ u8_t start_oid_len = (oid_len < state->start_oid_len) ? oid_len : state->start_oid_len;
+
+ /* check passed OID is located behind start offset */
+ if (snmp_oid_compare(oid, oid_len, state->start_oid, start_oid_len) >= 0) {
+ /* check if new oid is located closer to start oid than current closest oid */
+ if ((state->status == SNMP_NEXT_OID_STATUS_NO_MATCH) ||
+ (snmp_oid_compare(oid, oid_len, state->next_oid, state->next_oid_len) < 0)) {
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/** checks the passed OID if it is a candidate to be the next one (get_next); returns !=0 if passed oid is currently closest, otherwise 0 */
+u8_t
+snmp_next_oid_check(struct snmp_next_oid_state *state, const u32_t *oid, u8_t oid_len, void *reference)
+{
+ /* do not overwrite a fail result */
+ if (state->status != SNMP_NEXT_OID_STATUS_BUF_TO_SMALL) {
+ /* check passed OID is located behind start offset */
+ if (snmp_oid_compare(oid, oid_len, state->start_oid, state->start_oid_len) > 0) {
+ /* check if new oid is located closer to start oid than current closest oid */
+ if ((state->status == SNMP_NEXT_OID_STATUS_NO_MATCH) ||
+ (snmp_oid_compare(oid, oid_len, state->next_oid, state->next_oid_len) < 0)) {
+ if (oid_len <= state->next_oid_max_len) {
+ MEMCPY(state->next_oid, oid, oid_len * sizeof(u32_t));
+ state->next_oid_len = oid_len;
+ state->status = SNMP_NEXT_OID_STATUS_SUCCESS;
+ state->reference = reference;
+ return 1;
+ } else {
+ state->status = SNMP_NEXT_OID_STATUS_BUF_TO_SMALL;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+u8_t
+snmp_oid_in_range(const u32_t *oid_in, u8_t oid_len, const struct snmp_oid_range *oid_ranges, u8_t oid_ranges_len)
+{
+ u8_t i;
+
+ if (oid_len != oid_ranges_len) {
+ return 0;
+ }
+
+ for (i = 0; i < oid_ranges_len; i++) {
+ if ((oid_in[i] < oid_ranges[i].min) || (oid_in[i] > oid_ranges[i].max)) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+snmp_err_t
+snmp_set_test_ok(struct snmp_node_instance *instance, u16_t value_len, void *value)
+{
+ LWIP_UNUSED_ARG(instance);
+ LWIP_UNUSED_ARG(value_len);
+ LWIP_UNUSED_ARG(value);
+
+ return SNMP_ERR_NOERROR;
+}
+
+/**
+ * Decodes BITS pseudotype value from ASN.1 OctetString.
+ *
+ * @note Because BITS pseudo type is encoded as OCTET STRING, it cannot directly
+ * be encoded/decoded by the agent. Instead call this function as required from
+ * get/test/set methods.
+ *
+ * @param buf points to a buffer holding the ASN1 octet string
+ * @param buf_len length of octet string
+ * @param bit_value decoded Bit value with Bit0 == LSB
+ * @return ERR_OK if successful, ERR_ARG if bit value contains more than 32 bit
+ */
+err_t
+snmp_decode_bits(const u8_t *buf, u32_t buf_len, u32_t *bit_value)
+{
+ u8_t b;
+ u8_t bits_processed = 0;
+ *bit_value = 0;
+
+ while (buf_len > 0) {
+ /* any bit set in this byte? */
+ if (*buf != 0x00) {
+ if (bits_processed >= 32) {
+ /* accept more than 4 bytes, but only when no bits are set */
+ return ERR_VAL;
+ }
+
+ b = *buf;
+ do {
+ if (b & 0x80) {
+ *bit_value |= (1 << bits_processed);
+ }
+ bits_processed++;
+ b <<= 1;
+ } while ((bits_processed & 0x07) != 0); /* &0x07 -> % 8 */
+ } else {
+ bits_processed += 8;
+ }
+
+ buf_len--;
+ buf++;
+ }
+
+ return ERR_OK;
+}
+
+err_t
+snmp_decode_truthvalue(const s32_t *asn1_value, u8_t *bool_value)
+{
+ /* defined by RFC1443:
+ TruthValue ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "Represents a boolean value."
+ SYNTAX INTEGER { true(1), false(2) }
+ */
+
+ if ((asn1_value == NULL) || (bool_value == NULL)) {
+ return ERR_ARG;
+ }
+
+ if (*asn1_value == 1) {
+ *bool_value = 1;
+ } else if (*asn1_value == 2) {
+ *bool_value = 0;
+ } else {
+ return ERR_VAL;
+ }
+
+ return ERR_OK;
+}
+
+/**
+ * Encodes BITS pseudotype value into ASN.1 OctetString.
+ *
+ * @note Because BITS pseudo type is encoded as OCTET STRING, it cannot directly
+ * be encoded/decoded by the agent. Instead call this function as required from
+ * get/test/set methods.
+ *
+ * @param buf points to a buffer where the resulting ASN1 octet string is stored to
+ * @param buf_len max length of the bufffer
+ * @param bit_value Bit value to encode with Bit0 == LSB
+ * @param bit_count Number of possible bits for the bit value (according to rfc we have to send all bits independant from their truth value)
+ * @return number of bytes used from buffer to store the resulting OctetString
+ */
+u8_t
+snmp_encode_bits(u8_t *buf, u32_t buf_len, u32_t bit_value, u8_t bit_count)
+{
+ u8_t len = 0;
+ u8_t min_bytes = (bit_count + 7) >> 3; /* >>3 -> / 8 */
+
+ while ((buf_len > 0) && (bit_value != 0x00)) {
+ s8_t i = 7;
+ *buf = 0x00;
+ while (i >= 0) {
+ if (bit_value & 0x01) {
+ *buf |= 0x01;
+ }
+
+ if (i > 0) {
+ *buf <<= 1;
+ }
+
+ bit_value >>= 1;
+ i--;
+ }
+
+ buf++;
+ buf_len--;
+ len++;
+ }
+
+ if (len < min_bytes) {
+ buf += len;
+ buf_len -= len;
+
+ while ((len < min_bytes) && (buf_len > 0)) {
+ *buf = 0x00;
+ buf++;
+ buf_len--;
+ len++;
+ }
+ }
+
+ return len;
+}
+
+u8_t
+snmp_encode_truthvalue(s32_t *asn1_value, u32_t bool_value)
+{
+ /* defined by RFC1443:
+ TruthValue ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "Represents a boolean value."
+ SYNTAX INTEGER { true(1), false(2) }
+ */
+
+ if (asn1_value == NULL) {
+ return 0;
+ }
+
+ if (bool_value) {
+ *asn1_value = 1; /* defined by RFC1443 */
+ } else {
+ *asn1_value = 2; /* defined by RFC1443 */
+ }
+
+ return sizeof(s32_t);
+}
+
+#endif /* LWIP_SNMP */
diff --git a/resources/ethernet/LwIP/apps/snmp/snmp_core_priv.h b/resources/ethernet/LwIP/apps/snmp/snmp_core_priv.h
new file mode 100644
index 00000000..5e548643
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/snmp/snmp_core_priv.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Martin Hentschel
+ *
+ */
+
+#ifndef LWIP_HDR_APPS_SNMP_CORE_PRIV_H
+#define LWIP_HDR_APPS_SNMP_CORE_PRIV_H
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/snmp_core.h"
+#include "snmp_asn1.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* (outdated) SNMPv1 error codes
+ * shall not be used by MIBS anymore, nevertheless required from core for properly answering a v1 request
+ */
+#define SNMP_ERR_NOSUCHNAME 2
+#define SNMP_ERR_BADVALUE 3
+#define SNMP_ERR_READONLY 4
+/* error codes which are internal and shall not be used by MIBS
+ * shall not be used by MIBS anymore, nevertheless required from core for properly answering a v1 request
+ */
+#define SNMP_ERR_TOOBIG 1
+#define SNMP_ERR_AUTHORIZATIONERROR 16
+
+#define SNMP_ERR_UNKNOWN_ENGINEID 30
+#define SNMP_ERR_UNKNOWN_SECURITYNAME 31
+#define SNMP_ERR_UNSUPPORTED_SECLEVEL 32
+#define SNMP_ERR_NOTINTIMEWINDOW 33
+#define SNMP_ERR_DECRYIPTION_ERROR 34
+
+#define SNMP_ERR_NOSUCHOBJECT SNMP_VARBIND_EXCEPTION_OFFSET + SNMP_ASN1_CONTEXT_VARBIND_NO_SUCH_OBJECT
+#define SNMP_ERR_ENDOFMIBVIEW SNMP_VARBIND_EXCEPTION_OFFSET + SNMP_ASN1_CONTEXT_VARBIND_END_OF_MIB_VIEW
+
+
+const struct snmp_node *snmp_mib_tree_resolve_exact(const struct snmp_mib *mib, const u32_t *oid, u8_t oid_len, u8_t *oid_instance_len);
+const struct snmp_node *snmp_mib_tree_resolve_next(const struct snmp_mib *mib, const u32_t *oid, u8_t oid_len, struct snmp_obj_id *oidret);
+
+typedef u8_t (*snmp_validate_node_instance_method)(struct snmp_node_instance *, void *);
+
+u8_t snmp_get_node_instance_from_oid(const u32_t *oid, u8_t oid_len, struct snmp_node_instance *node_instance);
+u8_t snmp_get_next_node_instance_from_oid(const u32_t *oid, u8_t oid_len, snmp_validate_node_instance_method validate_node_instance_method, void *validate_node_instance_arg, struct snmp_obj_id *node_oid, struct snmp_node_instance *node_instance);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_SNMP */
+
+#endif /* LWIP_HDR_APPS_SNMP_CORE_PRIV_H */
diff --git a/resources/ethernet/LwIP/apps/snmp/snmp_mib2.c b/resources/ethernet/LwIP/apps/snmp/snmp_mib2.c
new file mode 100644
index 00000000..dc49ae6d
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/snmp/snmp_mib2.c
@@ -0,0 +1,116 @@
+/**
+ * @file
+ * Management Information Base II (RFC1213) objects and functions.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Dirk Ziegelmeier
+ * Christiaan Simons
+ */
+
+/**
+ * @defgroup snmp_mib2 MIB2
+ * @ingroup snmp
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP && SNMP_LWIP_MIB2 /* don't build if not configured for use in lwipopts.h */
+
+#if !LWIP_STATS
+#error LWIP_SNMP MIB2 needs LWIP_STATS (for MIB2)
+#endif
+#if !MIB2_STATS
+#error LWIP_SNMP MIB2 needs MIB2_STATS (for MIB2)
+#endif
+
+#include "lwip/snmp.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_mib2.h"
+#include "lwip/apps/snmp_scalar.h"
+
+#if SNMP_USE_NETCONN
+#include "lwip/tcpip.h"
+#include "lwip/priv/tcpip_priv.h"
+void
+snmp_mib2_lwip_synchronizer(snmp_threadsync_called_fn fn, void *arg)
+{
+#if LWIP_TCPIP_CORE_LOCKING
+ LOCK_TCPIP_CORE();
+ fn(arg);
+ UNLOCK_TCPIP_CORE();
+#else
+ tcpip_callback(fn, arg);
+#endif
+}
+
+struct snmp_threadsync_instance snmp_mib2_lwip_locks;
+#endif
+
+/* dot3 and EtherLike MIB not planned. (transmission .1.3.6.1.2.1.10) */
+/* historical (some say hysterical). (cmot .1.3.6.1.2.1.9) */
+/* lwIP has no EGP, thus may not implement it. (egp .1.3.6.1.2.1.8) */
+
+/* --- mib-2 .1.3.6.1.2.1 ----------------------------------------------------- */
+extern const struct snmp_scalar_array_node snmp_mib2_snmp_root;
+extern const struct snmp_tree_node snmp_mib2_udp_root;
+extern const struct snmp_tree_node snmp_mib2_tcp_root;
+extern const struct snmp_scalar_array_node snmp_mib2_icmp_root;
+extern const struct snmp_tree_node snmp_mib2_interface_root;
+extern const struct snmp_scalar_array_node snmp_mib2_system_node;
+extern const struct snmp_tree_node snmp_mib2_at_root;
+extern const struct snmp_tree_node snmp_mib2_ip_root;
+
+static const struct snmp_node *const mib2_nodes[] = {
+ &snmp_mib2_system_node.node.node,
+ &snmp_mib2_interface_root.node,
+#if LWIP_ARP && LWIP_IPV4
+ &snmp_mib2_at_root.node,
+#endif /* LWIP_ARP && LWIP_IPV4 */
+#if LWIP_IPV4
+ &snmp_mib2_ip_root.node,
+#endif /* LWIP_IPV4 */
+#if LWIP_ICMP
+ &snmp_mib2_icmp_root.node.node,
+#endif /* LWIP_ICMP */
+#if LWIP_TCP
+ &snmp_mib2_tcp_root.node,
+#endif /* LWIP_TCP */
+#if LWIP_UDP
+ &snmp_mib2_udp_root.node,
+#endif /* LWIP_UDP */
+ &snmp_mib2_snmp_root.node.node
+};
+
+static const struct snmp_tree_node mib2_root = SNMP_CREATE_TREE_NODE(1, mib2_nodes);
+
+static const u32_t mib2_base_oid_arr[] = { 1, 3, 6, 1, 2, 1 };
+const struct snmp_mib mib2 = SNMP_MIB_CREATE(mib2_base_oid_arr, &mib2_root.node);
+
+#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */
diff --git a/resources/ethernet/LwIP/apps/snmp/snmp_mib2_icmp.c b/resources/ethernet/LwIP/apps/snmp/snmp_mib2_icmp.c
new file mode 100644
index 00000000..0a82375e
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/snmp/snmp_mib2_icmp.c
@@ -0,0 +1,182 @@
+/**
+ * @file
+ * Management Information Base II (RFC1213) ICMP objects and functions.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Dirk Ziegelmeier
+ * Christiaan Simons
+ */
+
+#include "lwip/snmp.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_mib2.h"
+#include "lwip/apps/snmp_table.h"
+#include "lwip/apps/snmp_scalar.h"
+#include "lwip/icmp.h"
+#include "lwip/stats.h"
+
+#if LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_ICMP
+
+#if SNMP_USE_NETCONN
+#define SYNC_NODE_NAME(node_name) node_name ## _synced
+#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
+ static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
+#else
+#define SYNC_NODE_NAME(node_name) node_name
+#define CREATE_LWIP_SYNC_NODE(oid, node_name)
+#endif
+
+/* --- icmp .1.3.6.1.2.1.5 ----------------------------------------------------- */
+
+static s16_t
+icmp_get_value(const struct snmp_scalar_array_node_def *node, void *value)
+{
+ u32_t *uint_ptr = (u32_t *)value;
+
+ switch (node->oid) {
+ case 1: /* icmpInMsgs */
+ *uint_ptr = STATS_GET(mib2.icmpinmsgs);
+ return sizeof(*uint_ptr);
+ case 2: /* icmpInErrors */
+ *uint_ptr = STATS_GET(mib2.icmpinerrors);
+ return sizeof(*uint_ptr);
+ case 3: /* icmpInDestUnreachs */
+ *uint_ptr = STATS_GET(mib2.icmpindestunreachs);
+ return sizeof(*uint_ptr);
+ case 4: /* icmpInTimeExcds */
+ *uint_ptr = STATS_GET(mib2.icmpintimeexcds);
+ return sizeof(*uint_ptr);
+ case 5: /* icmpInParmProbs */
+ *uint_ptr = STATS_GET(mib2.icmpinparmprobs);
+ return sizeof(*uint_ptr);
+ case 6: /* icmpInSrcQuenchs */
+ *uint_ptr = STATS_GET(mib2.icmpinsrcquenchs);
+ return sizeof(*uint_ptr);
+ case 7: /* icmpInRedirects */
+ *uint_ptr = STATS_GET(mib2.icmpinredirects);
+ return sizeof(*uint_ptr);
+ case 8: /* icmpInEchos */
+ *uint_ptr = STATS_GET(mib2.icmpinechos);
+ return sizeof(*uint_ptr);
+ case 9: /* icmpInEchoReps */
+ *uint_ptr = STATS_GET(mib2.icmpinechoreps);
+ return sizeof(*uint_ptr);
+ case 10: /* icmpInTimestamps */
+ *uint_ptr = STATS_GET(mib2.icmpintimestamps);
+ return sizeof(*uint_ptr);
+ case 11: /* icmpInTimestampReps */
+ *uint_ptr = STATS_GET(mib2.icmpintimestampreps);
+ return sizeof(*uint_ptr);
+ case 12: /* icmpInAddrMasks */
+ *uint_ptr = STATS_GET(mib2.icmpinaddrmasks);
+ return sizeof(*uint_ptr);
+ case 13: /* icmpInAddrMaskReps */
+ *uint_ptr = STATS_GET(mib2.icmpinaddrmaskreps);
+ return sizeof(*uint_ptr);
+ case 14: /* icmpOutMsgs */
+ *uint_ptr = STATS_GET(mib2.icmpoutmsgs);
+ return sizeof(*uint_ptr);
+ case 15: /* icmpOutErrors */
+ *uint_ptr = STATS_GET(mib2.icmpouterrors);
+ return sizeof(*uint_ptr);
+ case 16: /* icmpOutDestUnreachs */
+ *uint_ptr = STATS_GET(mib2.icmpoutdestunreachs);
+ return sizeof(*uint_ptr);
+ case 17: /* icmpOutTimeExcds */
+ *uint_ptr = STATS_GET(mib2.icmpouttimeexcds);
+ return sizeof(*uint_ptr);
+ case 18: /* icmpOutParmProbs: not supported -> always 0 */
+ *uint_ptr = 0;
+ return sizeof(*uint_ptr);
+ case 19: /* icmpOutSrcQuenchs: not supported -> always 0 */
+ *uint_ptr = 0;
+ return sizeof(*uint_ptr);
+ case 20: /* icmpOutRedirects: not supported -> always 0 */
+ *uint_ptr = 0;
+ return sizeof(*uint_ptr);
+ case 21: /* icmpOutEchos */
+ *uint_ptr = STATS_GET(mib2.icmpoutechos);
+ return sizeof(*uint_ptr);
+ case 22: /* icmpOutEchoReps */
+ *uint_ptr = STATS_GET(mib2.icmpoutechoreps);
+ return sizeof(*uint_ptr);
+ case 23: /* icmpOutTimestamps: not supported -> always 0 */
+ *uint_ptr = 0;
+ return sizeof(*uint_ptr);
+ case 24: /* icmpOutTimestampReps: not supported -> always 0 */
+ *uint_ptr = 0;
+ return sizeof(*uint_ptr);
+ case 25: /* icmpOutAddrMasks: not supported -> always 0 */
+ *uint_ptr = 0;
+ return sizeof(*uint_ptr);
+ case 26: /* icmpOutAddrMaskReps: not supported -> always 0 */
+ *uint_ptr = 0;
+ return sizeof(*uint_ptr);
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG, ("icmp_get_value(): unknown id: %"S32_F"\n", node->oid));
+ break;
+ }
+
+ return 0;
+}
+
+
+static const struct snmp_scalar_array_node_def icmp_nodes[] = {
+ { 1, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ { 2, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ { 3, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ { 4, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ { 5, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ { 6, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ { 7, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ { 8, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ { 9, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ {10, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ {11, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ {12, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ {13, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ {14, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ {15, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ {16, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ {17, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ {18, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ {19, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ {20, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ {21, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ {22, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ {23, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ {24, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ {25, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ {26, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}
+};
+
+const struct snmp_scalar_array_node snmp_mib2_icmp_root = SNMP_SCALAR_CREATE_ARRAY_NODE(5, icmp_nodes, icmp_get_value, NULL, NULL);
+
+#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_ICMP */
diff --git a/resources/ethernet/LwIP/apps/snmp/snmp_mib2_interfaces.c b/resources/ethernet/LwIP/apps/snmp/snmp_mib2_interfaces.c
new file mode 100644
index 00000000..c53ce9e8
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/snmp/snmp_mib2_interfaces.c
@@ -0,0 +1,368 @@
+/**
+ * @file
+ * Management Information Base II (RFC1213) INTERFACES objects and functions.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Dirk Ziegelmeier
+ * Christiaan Simons
+ */
+
+#include "lwip/snmp.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_mib2.h"
+#include "lwip/apps/snmp_table.h"
+#include "lwip/apps/snmp_scalar.h"
+#include "lwip/netif.h"
+#include "lwip/stats.h"
+
+#include
+
+#if LWIP_SNMP && SNMP_LWIP_MIB2
+
+#if SNMP_USE_NETCONN
+#define SYNC_NODE_NAME(node_name) node_name ## _synced
+#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
+ static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
+#else
+#define SYNC_NODE_NAME(node_name) node_name
+#define CREATE_LWIP_SYNC_NODE(oid, node_name)
+#endif
+
+
+/* --- interfaces .1.3.6.1.2.1.2 ----------------------------------------------------- */
+
+static s16_t
+interfaces_get_value(struct snmp_node_instance *instance, void *value)
+{
+ if (instance->node->oid == 1) {
+ s32_t *sint_ptr = (s32_t *)value;
+ s32_t num_netifs = 0;
+
+ struct netif *netif;
+ NETIF_FOREACH(netif) {
+ num_netifs++;
+ }
+
+ *sint_ptr = num_netifs;
+ return sizeof(*sint_ptr);
+ }
+
+ return 0;
+}
+
+/* list of allowed value ranges for incoming OID */
+static const struct snmp_oid_range interfaces_Table_oid_ranges[] = {
+ { 1, 0xff } /* netif->num is u8_t */
+};
+
+static const u8_t iftable_ifOutQLen = 0;
+
+static const u8_t iftable_ifOperStatus_up = 1;
+static const u8_t iftable_ifOperStatus_down = 2;
+
+static const u8_t iftable_ifAdminStatus_up = 1;
+static const u8_t iftable_ifAdminStatus_lowerLayerDown = 7;
+static const u8_t iftable_ifAdminStatus_down = 2;
+
+static snmp_err_t
+interfaces_Table_get_cell_instance(const u32_t *column, const u32_t *row_oid, u8_t row_oid_len, struct snmp_node_instance *cell_instance)
+{
+ u32_t ifIndex;
+ struct netif *netif;
+
+ LWIP_UNUSED_ARG(column);
+
+ /* check if incoming OID length and if values are in plausible range */
+ if (!snmp_oid_in_range(row_oid, row_oid_len, interfaces_Table_oid_ranges, LWIP_ARRAYSIZE(interfaces_Table_oid_ranges))) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ /* get netif index from incoming OID */
+ ifIndex = row_oid[0];
+
+ /* find netif with index */
+ NETIF_FOREACH(netif) {
+ if (netif_to_num(netif) == ifIndex) {
+ /* store netif pointer for subsequent operations (get/test/set) */
+ cell_instance->reference.ptr = netif;
+ return SNMP_ERR_NOERROR;
+ }
+ }
+
+ /* not found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static snmp_err_t
+interfaces_Table_get_next_cell_instance(const u32_t *column, struct snmp_obj_id *row_oid, struct snmp_node_instance *cell_instance)
+{
+ struct netif *netif;
+ struct snmp_next_oid_state state;
+ u32_t result_temp[LWIP_ARRAYSIZE(interfaces_Table_oid_ranges)];
+
+ LWIP_UNUSED_ARG(column);
+
+ /* init struct to search next oid */
+ snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(interfaces_Table_oid_ranges));
+
+ /* iterate over all possible OIDs to find the next one */
+ NETIF_FOREACH(netif) {
+ u32_t test_oid[LWIP_ARRAYSIZE(interfaces_Table_oid_ranges)];
+ test_oid[0] = netif_to_num(netif);
+
+ /* check generated OID: is it a candidate for the next one? */
+ snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(interfaces_Table_oid_ranges), netif);
+ }
+
+ /* did we find a next one? */
+ if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+ snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+ /* store netif pointer for subsequent operations (get/test/set) */
+ cell_instance->reference.ptr = /* (struct netif*) */state.reference;
+ return SNMP_ERR_NOERROR;
+ }
+
+ /* not found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static s16_t
+interfaces_Table_get_value(struct snmp_node_instance *instance, void *value)
+{
+ struct netif *netif = (struct netif *)instance->reference.ptr;
+ u32_t *value_u32 = (u32_t *)value;
+ s32_t *value_s32 = (s32_t *)value;
+ u16_t value_len;
+
+ switch (SNMP_TABLE_GET_COLUMN_FROM_OID(instance->instance_oid.id)) {
+ case 1: /* ifIndex */
+ *value_s32 = netif_to_num(netif);
+ value_len = sizeof(*value_s32);
+ break;
+ case 2: /* ifDescr */
+ value_len = sizeof(netif->name);
+ MEMCPY(value, netif->name, value_len);
+ break;
+ case 3: /* ifType */
+ *value_s32 = netif->link_type;
+ value_len = sizeof(*value_s32);
+ break;
+ case 4: /* ifMtu */
+ *value_s32 = netif->mtu;
+ value_len = sizeof(*value_s32);
+ break;
+ case 5: /* ifSpeed */
+ *value_u32 = netif->link_speed;
+ value_len = sizeof(*value_u32);
+ break;
+ case 6: /* ifPhysAddress */
+ value_len = sizeof(netif->hwaddr);
+ MEMCPY(value, &netif->hwaddr, value_len);
+ break;
+ case 7: /* ifAdminStatus */
+ if (netif_is_up(netif)) {
+ *value_s32 = iftable_ifOperStatus_up;
+ } else {
+ *value_s32 = iftable_ifOperStatus_down;
+ }
+ value_len = sizeof(*value_s32);
+ break;
+ case 8: /* ifOperStatus */
+ if (netif_is_up(netif)) {
+ if (netif_is_link_up(netif)) {
+ *value_s32 = iftable_ifAdminStatus_up;
+ } else {
+ *value_s32 = iftable_ifAdminStatus_lowerLayerDown;
+ }
+ } else {
+ *value_s32 = iftable_ifAdminStatus_down;
+ }
+ value_len = sizeof(*value_s32);
+ break;
+ case 9: /* ifLastChange */
+ *value_u32 = netif->ts;
+ value_len = sizeof(*value_u32);
+ break;
+ case 10: /* ifInOctets */
+ *value_u32 = netif->mib2_counters.ifinoctets;
+ value_len = sizeof(*value_u32);
+ break;
+ case 11: /* ifInUcastPkts */
+ *value_u32 = netif->mib2_counters.ifinucastpkts;
+ value_len = sizeof(*value_u32);
+ break;
+ case 12: /* ifInNUcastPkts */
+ *value_u32 = netif->mib2_counters.ifinnucastpkts;
+ value_len = sizeof(*value_u32);
+ break;
+ case 13: /* ifInDiscards */
+ *value_u32 = netif->mib2_counters.ifindiscards;
+ value_len = sizeof(*value_u32);
+ break;
+ case 14: /* ifInErrors */
+ *value_u32 = netif->mib2_counters.ifinerrors;
+ value_len = sizeof(*value_u32);
+ break;
+ case 15: /* ifInUnkownProtos */
+ *value_u32 = netif->mib2_counters.ifinunknownprotos;
+ value_len = sizeof(*value_u32);
+ break;
+ case 16: /* ifOutOctets */
+ *value_u32 = netif->mib2_counters.ifoutoctets;
+ value_len = sizeof(*value_u32);
+ break;
+ case 17: /* ifOutUcastPkts */
+ *value_u32 = netif->mib2_counters.ifoutucastpkts;
+ value_len = sizeof(*value_u32);
+ break;
+ case 18: /* ifOutNUcastPkts */
+ *value_u32 = netif->mib2_counters.ifoutnucastpkts;
+ value_len = sizeof(*value_u32);
+ break;
+ case 19: /* ifOutDiscarts */
+ *value_u32 = netif->mib2_counters.ifoutdiscards;
+ value_len = sizeof(*value_u32);
+ break;
+ case 20: /* ifOutErrors */
+ *value_u32 = netif->mib2_counters.ifouterrors;
+ value_len = sizeof(*value_u32);
+ break;
+ case 21: /* ifOutQLen */
+ *value_u32 = iftable_ifOutQLen;
+ value_len = sizeof(*value_u32);
+ break;
+ /** @note returning zeroDotZero (0.0) no media specific MIB support */
+ case 22: /* ifSpecific */
+ value_len = snmp_zero_dot_zero.len * sizeof(u32_t);
+ MEMCPY(value, snmp_zero_dot_zero.id, value_len);
+ break;
+ default:
+ return 0;
+ }
+
+ return value_len;
+}
+
+#if !SNMP_SAFE_REQUESTS
+
+static snmp_err_t
+interfaces_Table_set_test(struct snmp_node_instance *instance, u16_t len, void *value)
+{
+ s32_t *sint_ptr = (s32_t *)value;
+
+ /* stack should never call this method for another column,
+ because all other columns are set to readonly */
+ LWIP_ASSERT("Invalid column", (SNMP_TABLE_GET_COLUMN_FROM_OID(instance->instance_oid.id) == 7));
+ LWIP_UNUSED_ARG(len);
+
+ if (*sint_ptr == 1 || *sint_ptr == 2) {
+ return SNMP_ERR_NOERROR;
+ }
+
+ return SNMP_ERR_WRONGVALUE;
+}
+
+static snmp_err_t
+interfaces_Table_set_value(struct snmp_node_instance *instance, u16_t len, void *value)
+{
+ struct netif *netif = (struct netif *)instance->reference.ptr;
+ s32_t *sint_ptr = (s32_t *)value;
+
+ /* stack should never call this method for another column,
+ because all other columns are set to readonly */
+ LWIP_ASSERT("Invalid column", (SNMP_TABLE_GET_COLUMN_FROM_OID(instance->instance_oid.id) == 7));
+ LWIP_UNUSED_ARG(len);
+
+ if (*sint_ptr == 1) {
+ netif_set_up(netif);
+ } else if (*sint_ptr == 2) {
+ netif_set_down(netif);
+ }
+
+ return SNMP_ERR_NOERROR;
+}
+
+#endif /* SNMP_SAFE_REQUESTS */
+
+static const struct snmp_scalar_node interfaces_Number = SNMP_SCALAR_CREATE_NODE_READONLY(1, SNMP_ASN1_TYPE_INTEGER, interfaces_get_value);
+
+static const struct snmp_table_col_def interfaces_Table_columns[] = {
+ { 1, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifIndex */
+ { 2, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifDescr */
+ { 3, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifType */
+ { 4, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifMtu */
+ { 5, SNMP_ASN1_TYPE_GAUGE, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifSpeed */
+ { 6, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifPhysAddress */
+#if !SNMP_SAFE_REQUESTS
+ { 7, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_WRITE }, /* ifAdminStatus */
+#else
+ { 7, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifAdminStatus */
+#endif
+ { 8, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOperStatus */
+ { 9, SNMP_ASN1_TYPE_TIMETICKS, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifLastChange */
+ { 10, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInOctets */
+ { 11, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInUcastPkts */
+ { 12, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInNUcastPkts */
+ { 13, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInDiscarts */
+ { 14, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInErrors */
+ { 15, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInUnkownProtos */
+ { 16, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutOctets */
+ { 17, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutUcastPkts */
+ { 18, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutNUcastPkts */
+ { 19, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutDiscarts */
+ { 20, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutErrors */
+ { 21, SNMP_ASN1_TYPE_GAUGE, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutQLen */
+ { 22, SNMP_ASN1_TYPE_OBJECT_ID, SNMP_NODE_INSTANCE_READ_ONLY } /* ifSpecific */
+};
+
+#if !SNMP_SAFE_REQUESTS
+static const struct snmp_table_node interfaces_Table = SNMP_TABLE_CREATE(
+ 2, interfaces_Table_columns,
+ interfaces_Table_get_cell_instance, interfaces_Table_get_next_cell_instance,
+ interfaces_Table_get_value, interfaces_Table_set_test, interfaces_Table_set_value);
+#else
+static const struct snmp_table_node interfaces_Table = SNMP_TABLE_CREATE(
+ 2, interfaces_Table_columns,
+ interfaces_Table_get_cell_instance, interfaces_Table_get_next_cell_instance,
+ interfaces_Table_get_value, NULL, NULL);
+#endif
+
+/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */
+CREATE_LWIP_SYNC_NODE(1, interfaces_Number)
+CREATE_LWIP_SYNC_NODE(2, interfaces_Table)
+
+static const struct snmp_node *const interface_nodes[] = {
+ &SYNC_NODE_NAME(interfaces_Number).node.node,
+ &SYNC_NODE_NAME(interfaces_Table).node.node
+};
+
+const struct snmp_tree_node snmp_mib2_interface_root = SNMP_CREATE_TREE_NODE(2, interface_nodes);
+
+#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */
diff --git a/resources/ethernet/LwIP/apps/snmp/snmp_mib2_ip.c b/resources/ethernet/LwIP/apps/snmp/snmp_mib2_ip.c
new file mode 100644
index 00000000..de109907
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/snmp/snmp_mib2_ip.c
@@ -0,0 +1,731 @@
+/**
+ * @file
+ * Management Information Base II (RFC1213) IP objects and functions.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Dirk Ziegelmeier
+ * Christiaan Simons
+ */
+
+#include "lwip/snmp.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_mib2.h"
+#include "lwip/apps/snmp_table.h"
+#include "lwip/apps/snmp_scalar.h"
+#include "lwip/stats.h"
+#include "lwip/netif.h"
+#include "lwip/ip.h"
+#include "lwip/etharp.h"
+
+#if LWIP_SNMP && SNMP_LWIP_MIB2
+
+#if SNMP_USE_NETCONN
+#define SYNC_NODE_NAME(node_name) node_name ## _synced
+#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
+ static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
+#else
+#define SYNC_NODE_NAME(node_name) node_name
+#define CREATE_LWIP_SYNC_NODE(oid, node_name)
+#endif
+
+#if LWIP_IPV4
+/* --- ip .1.3.6.1.2.1.4 ----------------------------------------------------- */
+
+static s16_t
+ip_get_value(struct snmp_node_instance *instance, void *value)
+{
+ s32_t *sint_ptr = (s32_t *)value;
+ u32_t *uint_ptr = (u32_t *)value;
+
+ switch (instance->node->oid) {
+ case 1: /* ipForwarding */
+#if IP_FORWARD
+ /* forwarding */
+ *sint_ptr = 1;
+#else
+ /* not-forwarding */
+ *sint_ptr = 2;
+#endif
+ return sizeof(*sint_ptr);
+ case 2: /* ipDefaultTTL */
+ *sint_ptr = IP_DEFAULT_TTL;
+ return sizeof(*sint_ptr);
+ case 3: /* ipInReceives */
+ *uint_ptr = STATS_GET(mib2.ipinreceives);
+ return sizeof(*uint_ptr);
+ case 4: /* ipInHdrErrors */
+ *uint_ptr = STATS_GET(mib2.ipinhdrerrors);
+ return sizeof(*uint_ptr);
+ case 5: /* ipInAddrErrors */
+ *uint_ptr = STATS_GET(mib2.ipinaddrerrors);
+ return sizeof(*uint_ptr);
+ case 6: /* ipForwDatagrams */
+ *uint_ptr = STATS_GET(mib2.ipforwdatagrams);
+ return sizeof(*uint_ptr);
+ case 7: /* ipInUnknownProtos */
+ *uint_ptr = STATS_GET(mib2.ipinunknownprotos);
+ return sizeof(*uint_ptr);
+ case 8: /* ipInDiscards */
+ *uint_ptr = STATS_GET(mib2.ipindiscards);
+ return sizeof(*uint_ptr);
+ case 9: /* ipInDelivers */
+ *uint_ptr = STATS_GET(mib2.ipindelivers);
+ return sizeof(*uint_ptr);
+ case 10: /* ipOutRequests */
+ *uint_ptr = STATS_GET(mib2.ipoutrequests);
+ return sizeof(*uint_ptr);
+ case 11: /* ipOutDiscards */
+ *uint_ptr = STATS_GET(mib2.ipoutdiscards);
+ return sizeof(*uint_ptr);
+ case 12: /* ipOutNoRoutes */
+ *uint_ptr = STATS_GET(mib2.ipoutnoroutes);
+ return sizeof(*uint_ptr);
+ case 13: /* ipReasmTimeout */
+#if IP_REASSEMBLY
+ *sint_ptr = IP_REASS_MAXAGE;
+#else
+ *sint_ptr = 0;
+#endif
+ return sizeof(*sint_ptr);
+ case 14: /* ipReasmReqds */
+ *uint_ptr = STATS_GET(mib2.ipreasmreqds);
+ return sizeof(*uint_ptr);
+ case 15: /* ipReasmOKs */
+ *uint_ptr = STATS_GET(mib2.ipreasmoks);
+ return sizeof(*uint_ptr);
+ case 16: /* ipReasmFails */
+ *uint_ptr = STATS_GET(mib2.ipreasmfails);
+ return sizeof(*uint_ptr);
+ case 17: /* ipFragOKs */
+ *uint_ptr = STATS_GET(mib2.ipfragoks);
+ return sizeof(*uint_ptr);
+ case 18: /* ipFragFails */
+ *uint_ptr = STATS_GET(mib2.ipfragfails);
+ return sizeof(*uint_ptr);
+ case 19: /* ipFragCreates */
+ *uint_ptr = STATS_GET(mib2.ipfragcreates);
+ return sizeof(*uint_ptr);
+ case 23: /* ipRoutingDiscards: not supported -> always 0 */
+ *uint_ptr = 0;
+ return sizeof(*uint_ptr);
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG, ("ip_get_value(): unknown id: %"S32_F"\n", instance->node->oid));
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * Test ip object value before setting.
+ *
+ * @param instance node instance
+ * @param len return value space (in bytes)
+ * @param value points to (varbind) space to copy value from.
+ *
+ * @note we allow set if the value matches the hardwired value,
+ * otherwise return badvalue.
+ */
+static snmp_err_t
+ip_set_test(struct snmp_node_instance *instance, u16_t len, void *value)
+{
+ snmp_err_t ret = SNMP_ERR_WRONGVALUE;
+ s32_t *sint_ptr = (s32_t *)value;
+
+ LWIP_UNUSED_ARG(len);
+ switch (instance->node->oid) {
+ case 1: /* ipForwarding */
+#if IP_FORWARD
+ /* forwarding */
+ if (*sint_ptr == 1)
+#else
+ /* not-forwarding */
+ if (*sint_ptr == 2)
+#endif
+ {
+ ret = SNMP_ERR_NOERROR;
+ }
+ break;
+ case 2: /* ipDefaultTTL */
+ if (*sint_ptr == IP_DEFAULT_TTL) {
+ ret = SNMP_ERR_NOERROR;
+ }
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG, ("ip_set_test(): unknown id: %"S32_F"\n", instance->node->oid));
+ break;
+ }
+
+ return ret;
+}
+
+static snmp_err_t
+ip_set_value(struct snmp_node_instance *instance, u16_t len, void *value)
+{
+ LWIP_UNUSED_ARG(instance);
+ LWIP_UNUSED_ARG(len);
+ LWIP_UNUSED_ARG(value);
+ /* nothing to do here because in set_test we only accept values being the same as our own stored value -> no need to store anything */
+ return SNMP_ERR_NOERROR;
+}
+
+/* --- ipAddrTable --- */
+
+/* list of allowed value ranges for incoming OID */
+static const struct snmp_oid_range ip_AddrTable_oid_ranges[] = {
+ { 0, 0xff }, /* IP A */
+ { 0, 0xff }, /* IP B */
+ { 0, 0xff }, /* IP C */
+ { 0, 0xff } /* IP D */
+};
+
+static snmp_err_t
+ip_AddrTable_get_cell_value_core(struct netif *netif, const u32_t *column, union snmp_variant_value *value, u32_t *value_len)
+{
+ LWIP_UNUSED_ARG(value_len);
+
+ switch (*column) {
+ case 1: /* ipAdEntAddr */
+ value->u32 = netif_ip4_addr(netif)->addr;
+ break;
+ case 2: /* ipAdEntIfIndex */
+ value->u32 = netif_to_num(netif);
+ break;
+ case 3: /* ipAdEntNetMask */
+ value->u32 = netif_ip4_netmask(netif)->addr;
+ break;
+ case 4: /* ipAdEntBcastAddr */
+ /* lwIP oddity, there's no broadcast
+ address in the netif we can rely on */
+ value->u32 = IPADDR_BROADCAST & 1;
+ break;
+ case 5: /* ipAdEntReasmMaxSize */
+#if IP_REASSEMBLY
+ /* @todo The theoretical maximum is IP_REASS_MAX_PBUFS * size of the pbufs,
+ * but only if receiving one fragmented packet at a time.
+ * The current solution is to calculate for 2 simultaneous packets...
+ */
+ value->u32 = (IP_HLEN + ((IP_REASS_MAX_PBUFS / 2) *
+ (PBUF_POOL_BUFSIZE - PBUF_LINK_ENCAPSULATION_HLEN - PBUF_LINK_HLEN - IP_HLEN)));
+#else
+ /** @todo returning MTU would be a bad thing and
+ returning a wild guess like '576' isn't good either */
+ value->u32 = 0;
+#endif
+ break;
+ default:
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ return SNMP_ERR_NOERROR;
+}
+
+static snmp_err_t
+ip_AddrTable_get_cell_value(const u32_t *column, const u32_t *row_oid, u8_t row_oid_len, union snmp_variant_value *value, u32_t *value_len)
+{
+ ip4_addr_t ip;
+ struct netif *netif;
+
+ /* check if incoming OID length and if values are in plausible range */
+ if (!snmp_oid_in_range(row_oid, row_oid_len, ip_AddrTable_oid_ranges, LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges))) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ /* get IP from incoming OID */
+ snmp_oid_to_ip4(&row_oid[0], &ip); /* we know it succeeds because of oid_in_range check above */
+
+ /* find netif with requested ip */
+ NETIF_FOREACH(netif) {
+ if (ip4_addr_cmp(&ip, netif_ip4_addr(netif))) {
+ /* fill in object properties */
+ return ip_AddrTable_get_cell_value_core(netif, column, value, value_len);
+ }
+ }
+
+ /* not found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static snmp_err_t
+ip_AddrTable_get_next_cell_instance_and_value(const u32_t *column, struct snmp_obj_id *row_oid, union snmp_variant_value *value, u32_t *value_len)
+{
+ struct netif *netif;
+ struct snmp_next_oid_state state;
+ u32_t result_temp[LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges)];
+
+ /* init struct to search next oid */
+ snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges));
+
+ /* iterate over all possible OIDs to find the next one */
+ NETIF_FOREACH(netif) {
+ u32_t test_oid[LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges)];
+ snmp_ip4_to_oid(netif_ip4_addr(netif), &test_oid[0]);
+
+ /* check generated OID: is it a candidate for the next one? */
+ snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges), netif);
+ }
+
+ /* did we find a next one? */
+ if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+ snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+ /* fill in object properties */
+ return ip_AddrTable_get_cell_value_core((struct netif *)state.reference, column, value, value_len);
+ }
+
+ /* not found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+/* --- ipRouteTable --- */
+
+/* list of allowed value ranges for incoming OID */
+static const struct snmp_oid_range ip_RouteTable_oid_ranges[] = {
+ { 0, 0xff }, /* IP A */
+ { 0, 0xff }, /* IP B */
+ { 0, 0xff }, /* IP C */
+ { 0, 0xff }, /* IP D */
+};
+
+static snmp_err_t
+ip_RouteTable_get_cell_value_core(struct netif *netif, u8_t default_route, const u32_t *column, union snmp_variant_value *value, u32_t *value_len)
+{
+ switch (*column) {
+ case 1: /* ipRouteDest */
+ if (default_route) {
+ /* default rte has 0.0.0.0 dest */
+ value->u32 = IP4_ADDR_ANY4->addr;
+ } else {
+ /* netifs have netaddress dest */
+ ip4_addr_t tmp;
+ ip4_addr_get_network(&tmp, netif_ip4_addr(netif), netif_ip4_netmask(netif));
+ value->u32 = tmp.addr;
+ }
+ break;
+ case 2: /* ipRouteIfIndex */
+ value->u32 = netif_to_num(netif);
+ break;
+ case 3: /* ipRouteMetric1 */
+ if (default_route) {
+ value->s32 = 1; /* default */
+ } else {
+ value->s32 = 0; /* normal */
+ }
+ break;
+ case 4: /* ipRouteMetric2 */
+ case 5: /* ipRouteMetric3 */
+ case 6: /* ipRouteMetric4 */
+ value->s32 = -1; /* none */
+ break;
+ case 7: /* ipRouteNextHop */
+ if (default_route) {
+ /* default rte: gateway */
+ value->u32 = netif_ip4_gw(netif)->addr;
+ } else {
+ /* other rtes: netif ip_addr */
+ value->u32 = netif_ip4_addr(netif)->addr;
+ }
+ break;
+ case 8: /* ipRouteType */
+ if (default_route) {
+ /* default rte is indirect */
+ value->u32 = 4; /* indirect */
+ } else {
+ /* other rtes are direct */
+ value->u32 = 3; /* direct */
+ }
+ break;
+ case 9: /* ipRouteProto */
+ /* locally defined routes */
+ value->u32 = 2; /* local */
+ break;
+ case 10: /* ipRouteAge */
+ /* @todo (sysuptime - timestamp last change) / 100 */
+ value->u32 = 0;
+ break;
+ case 11: /* ipRouteMask */
+ if (default_route) {
+ /* default rte use 0.0.0.0 mask */
+ value->u32 = IP4_ADDR_ANY4->addr;
+ } else {
+ /* other rtes use netmask */
+ value->u32 = netif_ip4_netmask(netif)->addr;
+ }
+ break;
+ case 12: /* ipRouteMetric5 */
+ value->s32 = -1; /* none */
+ break;
+ case 13: /* ipRouteInfo */
+ value->const_ptr = snmp_zero_dot_zero.id;
+ *value_len = snmp_zero_dot_zero.len * sizeof(u32_t);
+ break;
+ default:
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ return SNMP_ERR_NOERROR;
+}
+
+static snmp_err_t
+ip_RouteTable_get_cell_value(const u32_t *column, const u32_t *row_oid, u8_t row_oid_len, union snmp_variant_value *value, u32_t *value_len)
+{
+ ip4_addr_t test_ip;
+ struct netif *netif;
+
+ /* check if incoming OID length and if values are in plausible range */
+ if (!snmp_oid_in_range(row_oid, row_oid_len, ip_RouteTable_oid_ranges, LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges))) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ /* get IP and port from incoming OID */
+ snmp_oid_to_ip4(&row_oid[0], &test_ip); /* we know it succeeds because of oid_in_range check above */
+
+ /* default route is on default netif */
+ if (ip4_addr_isany_val(test_ip) && (netif_default != NULL)) {
+ /* fill in object properties */
+ return ip_RouteTable_get_cell_value_core(netif_default, 1, column, value, value_len);
+ }
+
+ /* find netif with requested route */
+ NETIF_FOREACH(netif) {
+ ip4_addr_t dst;
+ ip4_addr_get_network(&dst, netif_ip4_addr(netif), netif_ip4_netmask(netif));
+
+ if (ip4_addr_cmp(&dst, &test_ip)) {
+ /* fill in object properties */
+ return ip_RouteTable_get_cell_value_core(netif, 0, column, value, value_len);
+ }
+ }
+
+ /* not found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static snmp_err_t
+ip_RouteTable_get_next_cell_instance_and_value(const u32_t *column, struct snmp_obj_id *row_oid, union snmp_variant_value *value, u32_t *value_len)
+{
+ struct netif *netif;
+ struct snmp_next_oid_state state;
+ u32_t result_temp[LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges)];
+ u32_t test_oid[LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges)];
+
+ /* init struct to search next oid */
+ snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges));
+
+ /* check default route */
+ if (netif_default != NULL) {
+ snmp_ip4_to_oid(IP4_ADDR_ANY4, &test_oid[0]);
+ snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges), netif_default);
+ }
+
+ /* iterate over all possible OIDs to find the next one */
+ NETIF_FOREACH(netif) {
+ ip4_addr_t dst;
+ ip4_addr_get_network(&dst, netif_ip4_addr(netif), netif_ip4_netmask(netif));
+
+ /* check generated OID: is it a candidate for the next one? */
+ if (!ip4_addr_isany_val(dst)) {
+ snmp_ip4_to_oid(&dst, &test_oid[0]);
+ snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges), netif);
+ }
+ }
+
+ /* did we find a next one? */
+ if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+ ip4_addr_t dst;
+ snmp_oid_to_ip4(&result_temp[0], &dst);
+ snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+ /* fill in object properties */
+ return ip_RouteTable_get_cell_value_core((struct netif *)state.reference, ip4_addr_isany_val(dst), column, value, value_len);
+ } else {
+ /* not found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+}
+
+#if LWIP_ARP && LWIP_IPV4
+/* --- ipNetToMediaTable --- */
+
+/* list of allowed value ranges for incoming OID */
+static const struct snmp_oid_range ip_NetToMediaTable_oid_ranges[] = {
+ { 1, 0xff }, /* IfIndex */
+ { 0, 0xff }, /* IP A */
+ { 0, 0xff }, /* IP B */
+ { 0, 0xff }, /* IP C */
+ { 0, 0xff } /* IP D */
+};
+
+static snmp_err_t
+ip_NetToMediaTable_get_cell_value_core(size_t arp_table_index, const u32_t *column, union snmp_variant_value *value, u32_t *value_len)
+{
+ ip4_addr_t *ip;
+ struct netif *netif;
+ struct eth_addr *ethaddr;
+
+ etharp_get_entry(arp_table_index, &ip, &netif, ðaddr);
+
+ /* value */
+ switch (*column) {
+ case 1: /* atIfIndex / ipNetToMediaIfIndex */
+ value->u32 = netif_to_num(netif);
+ break;
+ case 2: /* atPhysAddress / ipNetToMediaPhysAddress */
+ value->ptr = ethaddr;
+ *value_len = sizeof(*ethaddr);
+ break;
+ case 3: /* atNetAddress / ipNetToMediaNetAddress */
+ value->u32 = ip->addr;
+ break;
+ case 4: /* ipNetToMediaType */
+ value->u32 = 3; /* dynamic*/
+ break;
+ default:
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ return SNMP_ERR_NOERROR;
+}
+
+static snmp_err_t
+ip_NetToMediaTable_get_cell_value(const u32_t *column, const u32_t *row_oid, u8_t row_oid_len, union snmp_variant_value *value, u32_t *value_len)
+{
+ ip4_addr_t ip_in;
+ u8_t netif_index;
+ size_t i;
+
+ /* check if incoming OID length and if values are in plausible range */
+ if (!snmp_oid_in_range(row_oid, row_oid_len, ip_NetToMediaTable_oid_ranges, LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges))) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ /* get IP from incoming OID */
+ netif_index = (u8_t)row_oid[0];
+ snmp_oid_to_ip4(&row_oid[1], &ip_in); /* we know it succeeds because of oid_in_range check above */
+
+ /* find requested entry */
+ for (i = 0; i < ARP_TABLE_SIZE; i++) {
+ ip4_addr_t *ip;
+ struct netif *netif;
+ struct eth_addr *ethaddr;
+
+ if (etharp_get_entry(i, &ip, &netif, ðaddr)) {
+ if ((netif_index == netif_to_num(netif)) && ip4_addr_cmp(&ip_in, ip)) {
+ /* fill in object properties */
+ return ip_NetToMediaTable_get_cell_value_core(i, column, value, value_len);
+ }
+ }
+ }
+
+ /* not found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static snmp_err_t
+ip_NetToMediaTable_get_next_cell_instance_and_value(const u32_t *column, struct snmp_obj_id *row_oid, union snmp_variant_value *value, u32_t *value_len)
+{
+ size_t i;
+ struct snmp_next_oid_state state;
+ u32_t result_temp[LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges)];
+
+ /* init struct to search next oid */
+ snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges));
+
+ /* iterate over all possible OIDs to find the next one */
+ for (i = 0; i < ARP_TABLE_SIZE; i++) {
+ ip4_addr_t *ip;
+ struct netif *netif;
+ struct eth_addr *ethaddr;
+
+ if (etharp_get_entry(i, &ip, &netif, ðaddr)) {
+ u32_t test_oid[LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges)];
+
+ test_oid[0] = netif_to_num(netif);
+ snmp_ip4_to_oid(ip, &test_oid[1]);
+
+ /* check generated OID: is it a candidate for the next one? */
+ snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges), LWIP_PTR_NUMERIC_CAST(void *, i));
+ }
+ }
+
+ /* did we find a next one? */
+ if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+ snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+ /* fill in object properties */
+ return ip_NetToMediaTable_get_cell_value_core(LWIP_PTR_NUMERIC_CAST(size_t, state.reference), column, value, value_len);
+ }
+
+ /* not found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+#endif /* LWIP_ARP && LWIP_IPV4 */
+
+static const struct snmp_scalar_node ip_Forwarding = SNMP_SCALAR_CREATE_NODE(1, SNMP_NODE_INSTANCE_READ_WRITE, SNMP_ASN1_TYPE_INTEGER, ip_get_value, ip_set_test, ip_set_value);
+static const struct snmp_scalar_node ip_DefaultTTL = SNMP_SCALAR_CREATE_NODE(2, SNMP_NODE_INSTANCE_READ_WRITE, SNMP_ASN1_TYPE_INTEGER, ip_get_value, ip_set_test, ip_set_value);
+static const struct snmp_scalar_node ip_InReceives = SNMP_SCALAR_CREATE_NODE_READONLY(3, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_InHdrErrors = SNMP_SCALAR_CREATE_NODE_READONLY(4, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_InAddrErrors = SNMP_SCALAR_CREATE_NODE_READONLY(5, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_ForwDatagrams = SNMP_SCALAR_CREATE_NODE_READONLY(6, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_InUnknownProtos = SNMP_SCALAR_CREATE_NODE_READONLY(7, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_InDiscards = SNMP_SCALAR_CREATE_NODE_READONLY(8, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_InDelivers = SNMP_SCALAR_CREATE_NODE_READONLY(9, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_OutRequests = SNMP_SCALAR_CREATE_NODE_READONLY(10, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_OutDiscards = SNMP_SCALAR_CREATE_NODE_READONLY(11, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_OutNoRoutes = SNMP_SCALAR_CREATE_NODE_READONLY(12, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_ReasmTimeout = SNMP_SCALAR_CREATE_NODE_READONLY(13, SNMP_ASN1_TYPE_INTEGER, ip_get_value);
+static const struct snmp_scalar_node ip_ReasmReqds = SNMP_SCALAR_CREATE_NODE_READONLY(14, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_ReasmOKs = SNMP_SCALAR_CREATE_NODE_READONLY(15, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_ReasmFails = SNMP_SCALAR_CREATE_NODE_READONLY(16, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_FragOKs = SNMP_SCALAR_CREATE_NODE_READONLY(17, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_FragFails = SNMP_SCALAR_CREATE_NODE_READONLY(18, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_FragCreates = SNMP_SCALAR_CREATE_NODE_READONLY(19, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_RoutingDiscards = SNMP_SCALAR_CREATE_NODE_READONLY(23, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+
+static const struct snmp_table_simple_col_def ip_AddrTable_columns[] = {
+ { 1, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipAdEntAddr */
+ { 2, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipAdEntIfIndex */
+ { 3, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipAdEntNetMask */
+ { 4, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipAdEntBcastAddr */
+ { 5, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 } /* ipAdEntReasmMaxSize */
+};
+
+static const struct snmp_table_simple_node ip_AddrTable = SNMP_TABLE_CREATE_SIMPLE(20, ip_AddrTable_columns, ip_AddrTable_get_cell_value, ip_AddrTable_get_next_cell_instance_and_value);
+
+static const struct snmp_table_simple_col_def ip_RouteTable_columns[] = {
+ { 1, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteDest */
+ { 2, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteIfIndex */
+ { 3, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric1 */
+ { 4, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric2 */
+ { 5, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric3 */
+ { 6, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric4 */
+ { 7, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteNextHop */
+ { 8, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteType */
+ { 9, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteProto */
+ { 10, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteAge */
+ { 11, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteMask */
+ { 12, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric5 */
+ { 13, SNMP_ASN1_TYPE_OBJECT_ID, SNMP_VARIANT_VALUE_TYPE_PTR } /* ipRouteInfo */
+};
+
+static const struct snmp_table_simple_node ip_RouteTable = SNMP_TABLE_CREATE_SIMPLE(21, ip_RouteTable_columns, ip_RouteTable_get_cell_value, ip_RouteTable_get_next_cell_instance_and_value);
+#endif /* LWIP_IPV4 */
+
+#if LWIP_ARP && LWIP_IPV4
+static const struct snmp_table_simple_col_def ip_NetToMediaTable_columns[] = {
+ { 1, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipNetToMediaIfIndex */
+ { 2, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_VARIANT_VALUE_TYPE_PTR }, /* ipNetToMediaPhysAddress */
+ { 3, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipNetToMediaNetAddress */
+ { 4, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 } /* ipNetToMediaType */
+};
+
+static const struct snmp_table_simple_node ip_NetToMediaTable = SNMP_TABLE_CREATE_SIMPLE(22, ip_NetToMediaTable_columns, ip_NetToMediaTable_get_cell_value, ip_NetToMediaTable_get_next_cell_instance_and_value);
+#endif /* LWIP_ARP && LWIP_IPV4 */
+
+#if LWIP_IPV4
+/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */
+CREATE_LWIP_SYNC_NODE( 1, ip_Forwarding)
+CREATE_LWIP_SYNC_NODE( 2, ip_DefaultTTL)
+CREATE_LWIP_SYNC_NODE( 3, ip_InReceives)
+CREATE_LWIP_SYNC_NODE( 4, ip_InHdrErrors)
+CREATE_LWIP_SYNC_NODE( 5, ip_InAddrErrors)
+CREATE_LWIP_SYNC_NODE( 6, ip_ForwDatagrams)
+CREATE_LWIP_SYNC_NODE( 7, ip_InUnknownProtos)
+CREATE_LWIP_SYNC_NODE( 8, ip_InDiscards)
+CREATE_LWIP_SYNC_NODE( 9, ip_InDelivers)
+CREATE_LWIP_SYNC_NODE(10, ip_OutRequests)
+CREATE_LWIP_SYNC_NODE(11, ip_OutDiscards)
+CREATE_LWIP_SYNC_NODE(12, ip_OutNoRoutes)
+CREATE_LWIP_SYNC_NODE(13, ip_ReasmTimeout)
+CREATE_LWIP_SYNC_NODE(14, ip_ReasmReqds)
+CREATE_LWIP_SYNC_NODE(15, ip_ReasmOKs)
+CREATE_LWIP_SYNC_NODE(15, ip_ReasmFails)
+CREATE_LWIP_SYNC_NODE(17, ip_FragOKs)
+CREATE_LWIP_SYNC_NODE(18, ip_FragFails)
+CREATE_LWIP_SYNC_NODE(19, ip_FragCreates)
+CREATE_LWIP_SYNC_NODE(20, ip_AddrTable)
+CREATE_LWIP_SYNC_NODE(21, ip_RouteTable)
+#if LWIP_ARP
+CREATE_LWIP_SYNC_NODE(22, ip_NetToMediaTable)
+#endif /* LWIP_ARP */
+CREATE_LWIP_SYNC_NODE(23, ip_RoutingDiscards)
+
+static const struct snmp_node *const ip_nodes[] = {
+ &SYNC_NODE_NAME(ip_Forwarding).node.node,
+ &SYNC_NODE_NAME(ip_DefaultTTL).node.node,
+ &SYNC_NODE_NAME(ip_InReceives).node.node,
+ &SYNC_NODE_NAME(ip_InHdrErrors).node.node,
+ &SYNC_NODE_NAME(ip_InAddrErrors).node.node,
+ &SYNC_NODE_NAME(ip_ForwDatagrams).node.node,
+ &SYNC_NODE_NAME(ip_InUnknownProtos).node.node,
+ &SYNC_NODE_NAME(ip_InDiscards).node.node,
+ &SYNC_NODE_NAME(ip_InDelivers).node.node,
+ &SYNC_NODE_NAME(ip_OutRequests).node.node,
+ &SYNC_NODE_NAME(ip_OutDiscards).node.node,
+ &SYNC_NODE_NAME(ip_OutNoRoutes).node.node,
+ &SYNC_NODE_NAME(ip_ReasmTimeout).node.node,
+ &SYNC_NODE_NAME(ip_ReasmReqds).node.node,
+ &SYNC_NODE_NAME(ip_ReasmOKs).node.node,
+ &SYNC_NODE_NAME(ip_ReasmFails).node.node,
+ &SYNC_NODE_NAME(ip_FragOKs).node.node,
+ &SYNC_NODE_NAME(ip_FragFails).node.node,
+ &SYNC_NODE_NAME(ip_FragCreates).node.node,
+ &SYNC_NODE_NAME(ip_AddrTable).node.node,
+ &SYNC_NODE_NAME(ip_RouteTable).node.node,
+#if LWIP_ARP
+ &SYNC_NODE_NAME(ip_NetToMediaTable).node.node,
+#endif /* LWIP_ARP */
+ &SYNC_NODE_NAME(ip_RoutingDiscards).node.node
+};
+
+const struct snmp_tree_node snmp_mib2_ip_root = SNMP_CREATE_TREE_NODE(4, ip_nodes);
+#endif /* LWIP_IPV4 */
+
+/* --- at .1.3.6.1.2.1.3 ----------------------------------------------------- */
+
+#if LWIP_ARP && LWIP_IPV4
+/* at node table is a subset of ip_nettomedia table (same rows but less columns) */
+static const struct snmp_table_simple_col_def at_Table_columns[] = {
+ { 1, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* atIfIndex */
+ { 2, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_VARIANT_VALUE_TYPE_PTR }, /* atPhysAddress */
+ { 3, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 } /* atNetAddress */
+};
+
+static const struct snmp_table_simple_node at_Table = SNMP_TABLE_CREATE_SIMPLE(1, at_Table_columns, ip_NetToMediaTable_get_cell_value, ip_NetToMediaTable_get_next_cell_instance_and_value);
+
+/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */
+CREATE_LWIP_SYNC_NODE(1, at_Table)
+
+static const struct snmp_node *const at_nodes[] = {
+ &SYNC_NODE_NAME(at_Table).node.node
+};
+
+const struct snmp_tree_node snmp_mib2_at_root = SNMP_CREATE_TREE_NODE(3, at_nodes);
+#endif /* LWIP_ARP && LWIP_IPV4 */
+
+#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */
diff --git a/resources/ethernet/LwIP/apps/snmp/snmp_mib2_snmp.c b/resources/ethernet/LwIP/apps/snmp/snmp_mib2_snmp.c
new file mode 100644
index 00000000..6661c83d
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/snmp/snmp_mib2_snmp.c
@@ -0,0 +1,227 @@
+/**
+ * @file
+ * Management Information Base II (RFC1213) SNMP objects and functions.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Dirk Ziegelmeier
+ * Christiaan Simons
+ */
+
+#include "lwip/snmp.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_mib2.h"
+#include "lwip/apps/snmp_scalar.h"
+
+#if LWIP_SNMP && SNMP_LWIP_MIB2
+
+#define MIB2_AUTH_TRAPS_ENABLED 1
+#define MIB2_AUTH_TRAPS_DISABLED 2
+
+/* --- snmp .1.3.6.1.2.1.11 ----------------------------------------------------- */
+static s16_t
+snmp_get_value(const struct snmp_scalar_array_node_def *node, void *value)
+{
+ u32_t *uint_ptr = (u32_t *)value;
+ switch (node->oid) {
+ case 1: /* snmpInPkts */
+ *uint_ptr = snmp_stats.inpkts;
+ break;
+ case 2: /* snmpOutPkts */
+ *uint_ptr = snmp_stats.outpkts;
+ break;
+ case 3: /* snmpInBadVersions */
+ *uint_ptr = snmp_stats.inbadversions;
+ break;
+ case 4: /* snmpInBadCommunityNames */
+ *uint_ptr = snmp_stats.inbadcommunitynames;
+ break;
+ case 5: /* snmpInBadCommunityUses */
+ *uint_ptr = snmp_stats.inbadcommunityuses;
+ break;
+ case 6: /* snmpInASNParseErrs */
+ *uint_ptr = snmp_stats.inasnparseerrs;
+ break;
+ case 8: /* snmpInTooBigs */
+ *uint_ptr = snmp_stats.intoobigs;
+ break;
+ case 9: /* snmpInNoSuchNames */
+ *uint_ptr = snmp_stats.innosuchnames;
+ break;
+ case 10: /* snmpInBadValues */
+ *uint_ptr = snmp_stats.inbadvalues;
+ break;
+ case 11: /* snmpInReadOnlys */
+ *uint_ptr = snmp_stats.inreadonlys;
+ break;
+ case 12: /* snmpInGenErrs */
+ *uint_ptr = snmp_stats.ingenerrs;
+ break;
+ case 13: /* snmpInTotalReqVars */
+ *uint_ptr = snmp_stats.intotalreqvars;
+ break;
+ case 14: /* snmpInTotalSetVars */
+ *uint_ptr = snmp_stats.intotalsetvars;
+ break;
+ case 15: /* snmpInGetRequests */
+ *uint_ptr = snmp_stats.ingetrequests;
+ break;
+ case 16: /* snmpInGetNexts */
+ *uint_ptr = snmp_stats.ingetnexts;
+ break;
+ case 17: /* snmpInSetRequests */
+ *uint_ptr = snmp_stats.insetrequests;
+ break;
+ case 18: /* snmpInGetResponses */
+ *uint_ptr = snmp_stats.ingetresponses;
+ break;
+ case 19: /* snmpInTraps */
+ *uint_ptr = snmp_stats.intraps;
+ break;
+ case 20: /* snmpOutTooBigs */
+ *uint_ptr = snmp_stats.outtoobigs;
+ break;
+ case 21: /* snmpOutNoSuchNames */
+ *uint_ptr = snmp_stats.outnosuchnames;
+ break;
+ case 22: /* snmpOutBadValues */
+ *uint_ptr = snmp_stats.outbadvalues;
+ break;
+ case 24: /* snmpOutGenErrs */
+ *uint_ptr = snmp_stats.outgenerrs;
+ break;
+ case 25: /* snmpOutGetRequests */
+ *uint_ptr = snmp_stats.outgetrequests;
+ break;
+ case 26: /* snmpOutGetNexts */
+ *uint_ptr = snmp_stats.outgetnexts;
+ break;
+ case 27: /* snmpOutSetRequests */
+ *uint_ptr = snmp_stats.outsetrequests;
+ break;
+ case 28: /* snmpOutGetResponses */
+ *uint_ptr = snmp_stats.outgetresponses;
+ break;
+ case 29: /* snmpOutTraps */
+ *uint_ptr = snmp_stats.outtraps;
+ break;
+ case 30: /* snmpEnableAuthenTraps */
+ if (snmp_get_auth_traps_enabled() == SNMP_AUTH_TRAPS_DISABLED) {
+ *uint_ptr = MIB2_AUTH_TRAPS_DISABLED;
+ } else {
+ *uint_ptr = MIB2_AUTH_TRAPS_ENABLED;
+ }
+ break;
+ case 31: /* snmpSilentDrops */
+ *uint_ptr = 0; /* not supported */
+ break;
+ case 32: /* snmpProxyDrops */
+ *uint_ptr = 0; /* not supported */
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG, ("snmp_get_value(): unknown id: %"S32_F"\n", node->oid));
+ return 0;
+ }
+
+ return sizeof(*uint_ptr);
+}
+
+static snmp_err_t
+snmp_set_test(const struct snmp_scalar_array_node_def *node, u16_t len, void *value)
+{
+ snmp_err_t ret = SNMP_ERR_WRONGVALUE;
+ LWIP_UNUSED_ARG(len);
+
+ if (node->oid == 30) {
+ /* snmpEnableAuthenTraps */
+ s32_t *sint_ptr = (s32_t *)value;
+
+ /* we should have writable non-volatile mem here */
+ if ((*sint_ptr == MIB2_AUTH_TRAPS_DISABLED) || (*sint_ptr == MIB2_AUTH_TRAPS_ENABLED)) {
+ ret = SNMP_ERR_NOERROR;
+ }
+ }
+ return ret;
+}
+
+static snmp_err_t
+snmp_set_value(const struct snmp_scalar_array_node_def *node, u16_t len, void *value)
+{
+ LWIP_UNUSED_ARG(len);
+
+ if (node->oid == 30) {
+ /* snmpEnableAuthenTraps */
+ s32_t *sint_ptr = (s32_t *)value;
+ if (*sint_ptr == MIB2_AUTH_TRAPS_DISABLED) {
+ snmp_set_auth_traps_enabled(SNMP_AUTH_TRAPS_DISABLED);
+ } else {
+ snmp_set_auth_traps_enabled(SNMP_AUTH_TRAPS_ENABLED);
+ }
+ }
+
+ return SNMP_ERR_NOERROR;
+}
+
+/* the following nodes access variables in SNMP stack (snmp_stats) from SNMP worker thread -> OK, no sync needed */
+static const struct snmp_scalar_array_node_def snmp_nodes[] = {
+ { 1, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInPkts */
+ { 2, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutPkts */
+ { 3, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInBadVersions */
+ { 4, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInBadCommunityNames */
+ { 5, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInBadCommunityUses */
+ { 6, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInASNParseErrs */
+ { 8, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInTooBigs */
+ { 9, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInNoSuchNames */
+ {10, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInBadValues */
+ {11, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInReadOnlys */
+ {12, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInGenErrs */
+ {13, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInTotalReqVars */
+ {14, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInTotalSetVars */
+ {15, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInGetRequests */
+ {16, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInGetNexts */
+ {17, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInSetRequests */
+ {18, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInGetResponses */
+ {19, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInTraps */
+ {20, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutTooBigs */
+ {21, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutNoSuchNames */
+ {22, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutBadValues */
+ {24, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutGenErrs */
+ {25, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutGetRequests */
+ {26, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutGetNexts */
+ {27, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutSetRequests */
+ {28, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutGetResponses */
+ {29, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutTraps */
+ {30, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_WRITE}, /* snmpEnableAuthenTraps */
+ {31, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpSilentDrops */
+ {32, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY} /* snmpProxyDrops */
+};
+
+const struct snmp_scalar_array_node snmp_mib2_snmp_root = SNMP_SCALAR_CREATE_ARRAY_NODE(11, snmp_nodes, snmp_get_value, snmp_set_test, snmp_set_value);
+
+#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */
diff --git a/resources/ethernet/LwIP/apps/snmp/snmp_mib2_system.c b/resources/ethernet/LwIP/apps/snmp/snmp_mib2_system.c
new file mode 100644
index 00000000..ce28892c
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/snmp/snmp_mib2_system.c
@@ -0,0 +1,376 @@
+/**
+ * @file
+ * Management Information Base II (RFC1213) SYSTEM objects and functions.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Dirk Ziegelmeier
+ * Christiaan Simons
+ */
+
+#include "lwip/snmp.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_mib2.h"
+#include "lwip/apps/snmp_table.h"
+#include "lwip/apps/snmp_scalar.h"
+#include "lwip/sys.h"
+
+#include
+
+#if LWIP_SNMP && SNMP_LWIP_MIB2
+
+#if SNMP_USE_NETCONN
+#define SYNC_NODE_NAME(node_name) node_name ## _synced
+#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
+ static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
+#else
+#define SYNC_NODE_NAME(node_name) node_name
+#define CREATE_LWIP_SYNC_NODE(oid, node_name)
+#endif
+
+/* --- system .1.3.6.1.2.1.1 ----------------------------------------------------- */
+
+/** mib-2.system.sysDescr */
+static const u8_t sysdescr_default[] = SNMP_LWIP_MIB2_SYSDESC;
+static const u8_t *sysdescr = sysdescr_default;
+static const u16_t *sysdescr_len = NULL; /* use strlen for determining len */
+
+/** mib-2.system.sysContact */
+static const u8_t syscontact_default[] = SNMP_LWIP_MIB2_SYSCONTACT;
+static const u8_t *syscontact = syscontact_default;
+static const u16_t *syscontact_len = NULL; /* use strlen for determining len */
+static u8_t *syscontact_wr = NULL; /* if writable, points to the same buffer as syscontact (required for correct constness) */
+static u16_t *syscontact_wr_len = NULL; /* if writable, points to the same buffer as syscontact_len (required for correct constness) */
+static u16_t syscontact_bufsize = 0; /* 0=not writable */
+
+/** mib-2.system.sysName */
+static const u8_t sysname_default[] = SNMP_LWIP_MIB2_SYSNAME;
+static const u8_t *sysname = sysname_default;
+static const u16_t *sysname_len = NULL; /* use strlen for determining len */
+static u8_t *sysname_wr = NULL; /* if writable, points to the same buffer as sysname (required for correct constness) */
+static u16_t *sysname_wr_len = NULL; /* if writable, points to the same buffer as sysname_len (required for correct constness) */
+static u16_t sysname_bufsize = 0; /* 0=not writable */
+
+/** mib-2.system.sysLocation */
+static const u8_t syslocation_default[] = SNMP_LWIP_MIB2_SYSLOCATION;
+static const u8_t *syslocation = syslocation_default;
+static const u16_t *syslocation_len = NULL; /* use strlen for determining len */
+static u8_t *syslocation_wr = NULL; /* if writable, points to the same buffer as syslocation (required for correct constness) */
+static u16_t *syslocation_wr_len = NULL; /* if writable, points to the same buffer as syslocation_len (required for correct constness) */
+static u16_t syslocation_bufsize = 0; /* 0=not writable */
+
+/**
+ * @ingroup snmp_mib2
+ * Initializes sysDescr pointers.
+ *
+ * @param str if non-NULL then copy str pointer
+ * @param len points to string length, excluding zero terminator
+ */
+void
+snmp_mib2_set_sysdescr(const u8_t *str, const u16_t *len)
+{
+ if (str != NULL) {
+ sysdescr = str;
+ sysdescr_len = len;
+ }
+}
+
+/**
+ * @ingroup snmp_mib2
+ * Initializes sysContact pointers
+ *
+ * @param ocstr if non-NULL then copy str pointer
+ * @param ocstrlen points to string length, excluding zero terminator.
+ * if set to NULL it is assumed that ocstr is NULL-terminated.
+ * @param bufsize size of the buffer in bytes.
+ * (this is required because the buffer can be overwritten by snmp-set)
+ * if ocstrlen is NULL buffer needs space for terminating 0 byte.
+ * otherwise complete buffer is used for string.
+ * if bufsize is set to 0, the value is regarded as read-only.
+ */
+void
+snmp_mib2_set_syscontact(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize)
+{
+ if (ocstr != NULL) {
+ syscontact = ocstr;
+ syscontact_wr = ocstr;
+ syscontact_len = ocstrlen;
+ syscontact_wr_len = ocstrlen;
+ syscontact_bufsize = bufsize;
+ }
+}
+
+/**
+ * @ingroup snmp_mib2
+ * see \ref snmp_mib2_set_syscontact but set pointer to readonly memory
+ */
+void
+snmp_mib2_set_syscontact_readonly(const u8_t *ocstr, const u16_t *ocstrlen)
+{
+ if (ocstr != NULL) {
+ syscontact = ocstr;
+ syscontact_len = ocstrlen;
+ syscontact_wr = NULL;
+ syscontact_wr_len = NULL;
+ syscontact_bufsize = 0;
+ }
+}
+
+
+/**
+ * @ingroup snmp_mib2
+ * Initializes sysName pointers
+ *
+ * @param ocstr if non-NULL then copy str pointer
+ * @param ocstrlen points to string length, excluding zero terminator.
+ * if set to NULL it is assumed that ocstr is NULL-terminated.
+ * @param bufsize size of the buffer in bytes.
+ * (this is required because the buffer can be overwritten by snmp-set)
+ * if ocstrlen is NULL buffer needs space for terminating 0 byte.
+ * otherwise complete buffer is used for string.
+ * if bufsize is set to 0, the value is regarded as read-only.
+ */
+void
+snmp_mib2_set_sysname(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize)
+{
+ if (ocstr != NULL) {
+ sysname = ocstr;
+ sysname_wr = ocstr;
+ sysname_len = ocstrlen;
+ sysname_wr_len = ocstrlen;
+ sysname_bufsize = bufsize;
+ }
+}
+
+/**
+ * @ingroup snmp_mib2
+ * see \ref snmp_mib2_set_sysname but set pointer to readonly memory
+ */
+void
+snmp_mib2_set_sysname_readonly(const u8_t *ocstr, const u16_t *ocstrlen)
+{
+ if (ocstr != NULL) {
+ sysname = ocstr;
+ sysname_len = ocstrlen;
+ sysname_wr = NULL;
+ sysname_wr_len = NULL;
+ sysname_bufsize = 0;
+ }
+}
+
+/**
+ * @ingroup snmp_mib2
+ * Initializes sysLocation pointers
+ *
+ * @param ocstr if non-NULL then copy str pointer
+ * @param ocstrlen points to string length, excluding zero terminator.
+ * if set to NULL it is assumed that ocstr is NULL-terminated.
+ * @param bufsize size of the buffer in bytes.
+ * (this is required because the buffer can be overwritten by snmp-set)
+ * if ocstrlen is NULL buffer needs space for terminating 0 byte.
+ * otherwise complete buffer is used for string.
+ * if bufsize is set to 0, the value is regarded as read-only.
+ */
+void
+snmp_mib2_set_syslocation(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize)
+{
+ if (ocstr != NULL) {
+ syslocation = ocstr;
+ syslocation_wr = ocstr;
+ syslocation_len = ocstrlen;
+ syslocation_wr_len = ocstrlen;
+ syslocation_bufsize = bufsize;
+ }
+}
+
+/**
+ * @ingroup snmp_mib2
+ * see \ref snmp_mib2_set_syslocation but set pointer to readonly memory
+ */
+void
+snmp_mib2_set_syslocation_readonly(const u8_t *ocstr, const u16_t *ocstrlen)
+{
+ if (ocstr != NULL) {
+ syslocation = ocstr;
+ syslocation_len = ocstrlen;
+ syslocation_wr = NULL;
+ syslocation_wr_len = NULL;
+ syslocation_bufsize = 0;
+ }
+}
+
+
+static s16_t
+system_get_value(const struct snmp_scalar_array_node_def *node, void *value)
+{
+ const u8_t *var = NULL;
+ const s16_t *var_len;
+ u16_t result;
+
+ switch (node->oid) {
+ case 1: /* sysDescr */
+ var = sysdescr;
+ var_len = (const s16_t *)sysdescr_len;
+ break;
+ case 2: { /* sysObjectID */
+ const struct snmp_obj_id *dev_enterprise_oid = snmp_get_device_enterprise_oid();
+ MEMCPY(value, dev_enterprise_oid->id, dev_enterprise_oid->len * sizeof(u32_t));
+ return dev_enterprise_oid->len * sizeof(u32_t);
+ }
+ case 3: /* sysUpTime */
+ MIB2_COPY_SYSUPTIME_TO((u32_t *)value);
+ return sizeof(u32_t);
+ case 4: /* sysContact */
+ var = syscontact;
+ var_len = (const s16_t *)syscontact_len;
+ break;
+ case 5: /* sysName */
+ var = sysname;
+ var_len = (const s16_t *)sysname_len;
+ break;
+ case 6: /* sysLocation */
+ var = syslocation;
+ var_len = (const s16_t *)syslocation_len;
+ break;
+ case 7: /* sysServices */
+ *(s32_t *)value = SNMP_SYSSERVICES;
+ return sizeof(s32_t);
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG, ("system_get_value(): unknown id: %"S32_F"\n", node->oid));
+ return 0;
+ }
+
+ /* handle string values (OID 1,4,5 and 6) */
+ LWIP_ASSERT("", (value != NULL));
+ if (var_len == NULL) {
+ result = (s16_t)strlen((const char *)var);
+ } else {
+ result = *var_len;
+ }
+ MEMCPY(value, var, result);
+ return result;
+}
+
+static snmp_err_t
+system_set_test(const struct snmp_scalar_array_node_def *node, u16_t len, void *value)
+{
+ snmp_err_t ret = SNMP_ERR_WRONGVALUE;
+ const u16_t *var_bufsize = NULL;
+ const u16_t *var_wr_len;
+
+ LWIP_UNUSED_ARG(value);
+
+ switch (node->oid) {
+ case 4: /* sysContact */
+ var_bufsize = &syscontact_bufsize;
+ var_wr_len = syscontact_wr_len;
+ break;
+ case 5: /* sysName */
+ var_bufsize = &sysname_bufsize;
+ var_wr_len = sysname_wr_len;
+ break;
+ case 6: /* sysLocation */
+ var_bufsize = &syslocation_bufsize;
+ var_wr_len = syslocation_wr_len;
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG, ("system_set_test(): unknown id: %"S32_F"\n", node->oid));
+ return ret;
+ }
+
+ /* check if value is writable at all */
+ if (*var_bufsize > 0) {
+ if (var_wr_len == NULL) {
+ /* we have to take the terminating 0 into account */
+ if (len < *var_bufsize) {
+ ret = SNMP_ERR_NOERROR;
+ }
+ } else {
+ if (len <= *var_bufsize) {
+ ret = SNMP_ERR_NOERROR;
+ }
+ }
+ } else {
+ ret = SNMP_ERR_NOTWRITABLE;
+ }
+
+ return ret;
+}
+
+static snmp_err_t
+system_set_value(const struct snmp_scalar_array_node_def *node, u16_t len, void *value)
+{
+ u8_t *var_wr = NULL;
+ u16_t *var_wr_len;
+
+ switch (node->oid) {
+ case 4: /* sysContact */
+ var_wr = syscontact_wr;
+ var_wr_len = syscontact_wr_len;
+ break;
+ case 5: /* sysName */
+ var_wr = sysname_wr;
+ var_wr_len = sysname_wr_len;
+ break;
+ case 6: /* sysLocation */
+ var_wr = syslocation_wr;
+ var_wr_len = syslocation_wr_len;
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG, ("system_set_value(): unknown id: %"S32_F"\n", node->oid));
+ return SNMP_ERR_GENERROR;
+ }
+
+ /* no need to check size of target buffer, this was already done in set_test method */
+ LWIP_ASSERT("", var_wr != NULL);
+ MEMCPY(var_wr, value, len);
+
+ if (var_wr_len == NULL) {
+ /* add terminating 0 */
+ var_wr[len] = 0;
+ } else {
+ *var_wr_len = len;
+ }
+
+ return SNMP_ERR_NOERROR;
+}
+
+static const struct snmp_scalar_array_node_def system_nodes[] = {
+ {1, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* sysDescr */
+ {2, SNMP_ASN1_TYPE_OBJECT_ID, SNMP_NODE_INSTANCE_READ_ONLY}, /* sysObjectID */
+ {3, SNMP_ASN1_TYPE_TIMETICKS, SNMP_NODE_INSTANCE_READ_ONLY}, /* sysUpTime */
+ {4, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_WRITE}, /* sysContact */
+ {5, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_WRITE}, /* sysName */
+ {6, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_WRITE}, /* sysLocation */
+ {7, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY} /* sysServices */
+};
+
+const struct snmp_scalar_array_node snmp_mib2_system_node = SNMP_SCALAR_CREATE_ARRAY_NODE(1, system_nodes, system_get_value, system_set_test, system_set_value);
+
+#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */
diff --git a/resources/ethernet/LwIP/apps/snmp/snmp_mib2_tcp.c b/resources/ethernet/LwIP/apps/snmp/snmp_mib2_tcp.c
new file mode 100644
index 00000000..61cf2db0
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/snmp/snmp_mib2_tcp.c
@@ -0,0 +1,607 @@
+/**
+ * @file
+ * Management Information Base II (RFC1213) TCP objects and functions.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Dirk Ziegelmeier
+ * Christiaan Simons
+ */
+
+#include "lwip/snmp.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_mib2.h"
+#include "lwip/apps/snmp_table.h"
+#include "lwip/apps/snmp_scalar.h"
+#include "lwip/tcp.h"
+#include "lwip/priv/tcp_priv.h"
+#include "lwip/stats.h"
+
+#include
+
+#if LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_TCP
+
+#if SNMP_USE_NETCONN
+#define SYNC_NODE_NAME(node_name) node_name ## _synced
+#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
+ static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
+#else
+#define SYNC_NODE_NAME(node_name) node_name
+#define CREATE_LWIP_SYNC_NODE(oid, node_name)
+#endif
+
+/* --- tcp .1.3.6.1.2.1.6 ----------------------------------------------------- */
+
+static s16_t
+tcp_get_value(struct snmp_node_instance *instance, void *value)
+{
+ u32_t *uint_ptr = (u32_t *)value;
+ s32_t *sint_ptr = (s32_t *)value;
+
+ switch (instance->node->oid) {
+ case 1: /* tcpRtoAlgorithm, vanj(4) */
+ *sint_ptr = 4;
+ return sizeof(*sint_ptr);
+ case 2: /* tcpRtoMin */
+ /* @todo not the actual value, a guess,
+ needs to be calculated */
+ *sint_ptr = 1000;
+ return sizeof(*sint_ptr);
+ case 3: /* tcpRtoMax */
+ /* @todo not the actual value, a guess,
+ needs to be calculated */
+ *sint_ptr = 60000;
+ return sizeof(*sint_ptr);
+ case 4: /* tcpMaxConn */
+ *sint_ptr = MEMP_NUM_TCP_PCB;
+ return sizeof(*sint_ptr);
+ case 5: /* tcpActiveOpens */
+ *uint_ptr = STATS_GET(mib2.tcpactiveopens);
+ return sizeof(*uint_ptr);
+ case 6: /* tcpPassiveOpens */
+ *uint_ptr = STATS_GET(mib2.tcppassiveopens);
+ return sizeof(*uint_ptr);
+ case 7: /* tcpAttemptFails */
+ *uint_ptr = STATS_GET(mib2.tcpattemptfails);
+ return sizeof(*uint_ptr);
+ case 8: /* tcpEstabResets */
+ *uint_ptr = STATS_GET(mib2.tcpestabresets);
+ return sizeof(*uint_ptr);
+ case 9: { /* tcpCurrEstab */
+ u16_t tcpcurrestab = 0;
+ struct tcp_pcb *pcb = tcp_active_pcbs;
+ while (pcb != NULL) {
+ if ((pcb->state == ESTABLISHED) ||
+ (pcb->state == CLOSE_WAIT)) {
+ tcpcurrestab++;
+ }
+ pcb = pcb->next;
+ }
+ *uint_ptr = tcpcurrestab;
+ }
+ return sizeof(*uint_ptr);
+ case 10: /* tcpInSegs */
+ *uint_ptr = STATS_GET(mib2.tcpinsegs);
+ return sizeof(*uint_ptr);
+ case 11: /* tcpOutSegs */
+ *uint_ptr = STATS_GET(mib2.tcpoutsegs);
+ return sizeof(*uint_ptr);
+ case 12: /* tcpRetransSegs */
+ *uint_ptr = STATS_GET(mib2.tcpretranssegs);
+ return sizeof(*uint_ptr);
+ case 14: /* tcpInErrs */
+ *uint_ptr = STATS_GET(mib2.tcpinerrs);
+ return sizeof(*uint_ptr);
+ case 15: /* tcpOutRsts */
+ *uint_ptr = STATS_GET(mib2.tcpoutrsts);
+ return sizeof(*uint_ptr);
+#if LWIP_HAVE_INT64
+ case 17: { /* tcpHCInSegs */
+ /* use the 32 bit counter for now... */
+ u64_t val64 = STATS_GET(mib2.tcpinsegs);
+ *((u64_t *)value) = val64;
+ }
+ return sizeof(u64_t);
+ case 18: { /* tcpHCOutSegs */
+ /* use the 32 bit counter for now... */
+ u64_t val64 = STATS_GET(mib2.tcpoutsegs);
+ *((u64_t *)value) = val64;
+ }
+ return sizeof(u64_t);
+#endif
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG, ("tcp_get_value(): unknown id: %"S32_F"\n", instance->node->oid));
+ break;
+ }
+
+ return 0;
+}
+
+/* --- tcpConnTable --- */
+
+#if LWIP_IPV4
+
+/* list of allowed value ranges for incoming OID */
+static const struct snmp_oid_range tcp_ConnTable_oid_ranges[] = {
+ { 0, 0xff }, /* IP A */
+ { 0, 0xff }, /* IP B */
+ { 0, 0xff }, /* IP C */
+ { 0, 0xff }, /* IP D */
+ { 0, 0xffff }, /* Port */
+ { 0, 0xff }, /* IP A */
+ { 0, 0xff }, /* IP B */
+ { 0, 0xff }, /* IP C */
+ { 0, 0xff }, /* IP D */
+ { 0, 0xffff } /* Port */
+};
+
+static snmp_err_t
+tcp_ConnTable_get_cell_value_core(struct tcp_pcb *pcb, const u32_t *column, union snmp_variant_value *value, u32_t *value_len)
+{
+ LWIP_UNUSED_ARG(value_len);
+
+ /* value */
+ switch (*column) {
+ case 1: /* tcpConnState */
+ value->u32 = pcb->state + 1;
+ break;
+ case 2: /* tcpConnLocalAddress */
+ value->u32 = ip_2_ip4(&pcb->local_ip)->addr;
+ break;
+ case 3: /* tcpConnLocalPort */
+ value->u32 = pcb->local_port;
+ break;
+ case 4: /* tcpConnRemAddress */
+ if (pcb->state == LISTEN) {
+ value->u32 = IP4_ADDR_ANY4->addr;
+ } else {
+ value->u32 = ip_2_ip4(&pcb->remote_ip)->addr;
+ }
+ break;
+ case 5: /* tcpConnRemPort */
+ if (pcb->state == LISTEN) {
+ value->u32 = 0;
+ } else {
+ value->u32 = pcb->remote_port;
+ }
+ break;
+ default:
+ LWIP_ASSERT("invalid id", 0);
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ return SNMP_ERR_NOERROR;
+}
+
+static snmp_err_t
+tcp_ConnTable_get_cell_value(const u32_t *column, const u32_t *row_oid, u8_t row_oid_len, union snmp_variant_value *value, u32_t *value_len)
+{
+ u8_t i;
+ ip4_addr_t local_ip;
+ ip4_addr_t remote_ip;
+ u16_t local_port;
+ u16_t remote_port;
+ struct tcp_pcb *pcb;
+
+ /* check if incoming OID length and if values are in plausible range */
+ if (!snmp_oid_in_range(row_oid, row_oid_len, tcp_ConnTable_oid_ranges, LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges))) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ /* get IPs and ports from incoming OID */
+ snmp_oid_to_ip4(&row_oid[0], &local_ip); /* we know it succeeds because of oid_in_range check above */
+ local_port = (u16_t)row_oid[4];
+ snmp_oid_to_ip4(&row_oid[5], &remote_ip); /* we know it succeeds because of oid_in_range check above */
+ remote_port = (u16_t)row_oid[9];
+
+ /* find tcp_pcb with requested ips and ports */
+ for (i = 0; i < LWIP_ARRAYSIZE(tcp_pcb_lists); i++) {
+ pcb = *tcp_pcb_lists[i];
+
+ while (pcb != NULL) {
+ /* do local IP and local port match? */
+ if (IP_IS_V4_VAL(pcb->local_ip) &&
+ ip4_addr_cmp(&local_ip, ip_2_ip4(&pcb->local_ip)) && (local_port == pcb->local_port)) {
+
+ /* PCBs in state LISTEN are not connected and have no remote_ip or remote_port */
+ if (pcb->state == LISTEN) {
+ if (ip4_addr_cmp(&remote_ip, IP4_ADDR_ANY4) && (remote_port == 0)) {
+ /* fill in object properties */
+ return tcp_ConnTable_get_cell_value_core(pcb, column, value, value_len);
+ }
+ } else {
+ if (IP_IS_V4_VAL(pcb->remote_ip) &&
+ ip4_addr_cmp(&remote_ip, ip_2_ip4(&pcb->remote_ip)) && (remote_port == pcb->remote_port)) {
+ /* fill in object properties */
+ return tcp_ConnTable_get_cell_value_core(pcb, column, value, value_len);
+ }
+ }
+ }
+
+ pcb = pcb->next;
+ }
+ }
+
+ /* not found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static snmp_err_t
+tcp_ConnTable_get_next_cell_instance_and_value(const u32_t *column, struct snmp_obj_id *row_oid, union snmp_variant_value *value, u32_t *value_len)
+{
+ u8_t i;
+ struct tcp_pcb *pcb;
+ struct snmp_next_oid_state state;
+ u32_t result_temp[LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges)];
+
+ /* init struct to search next oid */
+ snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges));
+
+ /* iterate over all possible OIDs to find the next one */
+ for (i = 0; i < LWIP_ARRAYSIZE(tcp_pcb_lists); i++) {
+ pcb = *tcp_pcb_lists[i];
+ while (pcb != NULL) {
+ u32_t test_oid[LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges)];
+
+ if (IP_IS_V4_VAL(pcb->local_ip)) {
+ snmp_ip4_to_oid(ip_2_ip4(&pcb->local_ip), &test_oid[0]);
+ test_oid[4] = pcb->local_port;
+
+ /* PCBs in state LISTEN are not connected and have no remote_ip or remote_port */
+ if (pcb->state == LISTEN) {
+ snmp_ip4_to_oid(IP4_ADDR_ANY4, &test_oid[5]);
+ test_oid[9] = 0;
+ } else {
+ if (IP_IS_V6_VAL(pcb->remote_ip)) { /* should never happen */
+ continue;
+ }
+ snmp_ip4_to_oid(ip_2_ip4(&pcb->remote_ip), &test_oid[5]);
+ test_oid[9] = pcb->remote_port;
+ }
+
+ /* check generated OID: is it a candidate for the next one? */
+ snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges), pcb);
+ }
+
+ pcb = pcb->next;
+ }
+ }
+
+ /* did we find a next one? */
+ if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+ snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+ /* fill in object properties */
+ return tcp_ConnTable_get_cell_value_core((struct tcp_pcb *)state.reference, column, value, value_len);
+ }
+
+ /* not found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+#endif /* LWIP_IPV4 */
+
+/* --- tcpConnectionTable --- */
+
+static snmp_err_t
+tcp_ConnectionTable_get_cell_value_core(const u32_t *column, struct tcp_pcb *pcb, union snmp_variant_value *value)
+{
+ /* all items except tcpConnectionState and tcpConnectionProcess are declared as not-accessible */
+ switch (*column) {
+ case 7: /* tcpConnectionState */
+ value->u32 = pcb->state + 1;
+ break;
+ case 8: /* tcpConnectionProcess */
+ value->u32 = 0; /* not supported */
+ break;
+ default:
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ return SNMP_ERR_NOERROR;
+}
+
+static snmp_err_t
+tcp_ConnectionTable_get_cell_value(const u32_t *column, const u32_t *row_oid, u8_t row_oid_len, union snmp_variant_value *value, u32_t *value_len)
+{
+ ip_addr_t local_ip, remote_ip;
+ u16_t local_port, remote_port;
+ struct tcp_pcb *pcb;
+ u8_t idx = 0;
+ u8_t i;
+ struct tcp_pcb **const tcp_pcb_nonlisten_lists[] = {&tcp_bound_pcbs, &tcp_active_pcbs, &tcp_tw_pcbs};
+
+ LWIP_UNUSED_ARG(value_len);
+
+ /* tcpConnectionLocalAddressType + tcpConnectionLocalAddress + tcpConnectionLocalPort */
+ idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len - idx, &local_ip, &local_port);
+ if (idx == 0) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ /* tcpConnectionRemAddressType + tcpConnectionRemAddress + tcpConnectionRemPort */
+ idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len - idx, &remote_ip, &remote_port);
+ if (idx == 0) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ /* find tcp_pcb with requested ip and port*/
+ for (i = 0; i < LWIP_ARRAYSIZE(tcp_pcb_nonlisten_lists); i++) {
+ pcb = *tcp_pcb_nonlisten_lists[i];
+
+ while (pcb != NULL) {
+ if (ip_addr_cmp(&local_ip, &pcb->local_ip) &&
+ (local_port == pcb->local_port) &&
+ ip_addr_cmp(&remote_ip, &pcb->remote_ip) &&
+ (remote_port == pcb->remote_port)) {
+ /* fill in object properties */
+ return tcp_ConnectionTable_get_cell_value_core(column, pcb, value);
+ }
+ pcb = pcb->next;
+ }
+ }
+
+ /* not found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static snmp_err_t
+tcp_ConnectionTable_get_next_cell_instance_and_value(const u32_t *column, struct snmp_obj_id *row_oid, union snmp_variant_value *value, u32_t *value_len)
+{
+ struct tcp_pcb *pcb;
+ struct snmp_next_oid_state state;
+ /* 1x tcpConnectionLocalAddressType + 1x OID len + 16x tcpConnectionLocalAddress + 1x tcpConnectionLocalPort
+ * 1x tcpConnectionRemAddressType + 1x OID len + 16x tcpConnectionRemAddress + 1x tcpConnectionRemPort */
+ u32_t result_temp[38];
+ u8_t i;
+ struct tcp_pcb **const tcp_pcb_nonlisten_lists[] = {&tcp_bound_pcbs, &tcp_active_pcbs, &tcp_tw_pcbs};
+
+ LWIP_UNUSED_ARG(value_len);
+
+ /* init struct to search next oid */
+ snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(result_temp));
+
+ /* iterate over all possible OIDs to find the next one */
+ for (i = 0; i < LWIP_ARRAYSIZE(tcp_pcb_nonlisten_lists); i++) {
+ pcb = *tcp_pcb_nonlisten_lists[i];
+
+ while (pcb != NULL) {
+ u8_t idx = 0;
+ u32_t test_oid[LWIP_ARRAYSIZE(result_temp)];
+
+ /* tcpConnectionLocalAddressType + tcpConnectionLocalAddress + tcpConnectionLocalPort */
+ idx += snmp_ip_port_to_oid(&pcb->local_ip, pcb->local_port, &test_oid[idx]);
+
+ /* tcpConnectionRemAddressType + tcpConnectionRemAddress + tcpConnectionRemPort */
+ idx += snmp_ip_port_to_oid(&pcb->remote_ip, pcb->remote_port, &test_oid[idx]);
+
+ /* check generated OID: is it a candidate for the next one? */
+ snmp_next_oid_check(&state, test_oid, idx, pcb);
+
+ pcb = pcb->next;
+ }
+ }
+
+ /* did we find a next one? */
+ if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+ snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+ /* fill in object properties */
+ return tcp_ConnectionTable_get_cell_value_core(column, (struct tcp_pcb *)state.reference, value);
+ } else {
+ /* not found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+}
+
+/* --- tcpListenerTable --- */
+
+static snmp_err_t
+tcp_ListenerTable_get_cell_value_core(const u32_t *column, union snmp_variant_value *value)
+{
+ /* all items except tcpListenerProcess are declared as not-accessible */
+ switch (*column) {
+ case 4: /* tcpListenerProcess */
+ value->u32 = 0; /* not supported */
+ break;
+ default:
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ return SNMP_ERR_NOERROR;
+}
+
+static snmp_err_t
+tcp_ListenerTable_get_cell_value(const u32_t *column, const u32_t *row_oid, u8_t row_oid_len, union snmp_variant_value *value, u32_t *value_len)
+{
+ ip_addr_t local_ip;
+ u16_t local_port;
+ struct tcp_pcb_listen *pcb;
+ u8_t idx = 0;
+
+ LWIP_UNUSED_ARG(value_len);
+
+ /* tcpListenerLocalAddressType + tcpListenerLocalAddress + tcpListenerLocalPort */
+ idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len - idx, &local_ip, &local_port);
+ if (idx == 0) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ /* find tcp_pcb with requested ip and port*/
+ pcb = tcp_listen_pcbs.listen_pcbs;
+ while (pcb != NULL) {
+ if (ip_addr_cmp(&local_ip, &pcb->local_ip) &&
+ (local_port == pcb->local_port)) {
+ /* fill in object properties */
+ return tcp_ListenerTable_get_cell_value_core(column, value);
+ }
+ pcb = pcb->next;
+ }
+
+ /* not found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static snmp_err_t
+tcp_ListenerTable_get_next_cell_instance_and_value(const u32_t *column, struct snmp_obj_id *row_oid, union snmp_variant_value *value, u32_t *value_len)
+{
+ struct tcp_pcb_listen *pcb;
+ struct snmp_next_oid_state state;
+ /* 1x tcpListenerLocalAddressType + 1x OID len + 16x tcpListenerLocalAddress + 1x tcpListenerLocalPort */
+ u32_t result_temp[19];
+
+ LWIP_UNUSED_ARG(value_len);
+
+ /* init struct to search next oid */
+ snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(result_temp));
+
+ /* iterate over all possible OIDs to find the next one */
+ pcb = tcp_listen_pcbs.listen_pcbs;
+ while (pcb != NULL) {
+ u8_t idx = 0;
+ u32_t test_oid[LWIP_ARRAYSIZE(result_temp)];
+
+ /* tcpListenerLocalAddressType + tcpListenerLocalAddress + tcpListenerLocalPort */
+ idx += snmp_ip_port_to_oid(&pcb->local_ip, pcb->local_port, &test_oid[idx]);
+
+ /* check generated OID: is it a candidate for the next one? */
+ snmp_next_oid_check(&state, test_oid, idx, NULL);
+
+ pcb = pcb->next;
+ }
+
+ /* did we find a next one? */
+ if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+ snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+ /* fill in object properties */
+ return tcp_ListenerTable_get_cell_value_core(column, value);
+ } else {
+ /* not found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+}
+
+static const struct snmp_scalar_node tcp_RtoAlgorithm = SNMP_SCALAR_CREATE_NODE_READONLY(1, SNMP_ASN1_TYPE_INTEGER, tcp_get_value);
+static const struct snmp_scalar_node tcp_RtoMin = SNMP_SCALAR_CREATE_NODE_READONLY(2, SNMP_ASN1_TYPE_INTEGER, tcp_get_value);
+static const struct snmp_scalar_node tcp_RtoMax = SNMP_SCALAR_CREATE_NODE_READONLY(3, SNMP_ASN1_TYPE_INTEGER, tcp_get_value);
+static const struct snmp_scalar_node tcp_MaxConn = SNMP_SCALAR_CREATE_NODE_READONLY(4, SNMP_ASN1_TYPE_INTEGER, tcp_get_value);
+static const struct snmp_scalar_node tcp_ActiveOpens = SNMP_SCALAR_CREATE_NODE_READONLY(5, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
+static const struct snmp_scalar_node tcp_PassiveOpens = SNMP_SCALAR_CREATE_NODE_READONLY(6, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
+static const struct snmp_scalar_node tcp_AttemptFails = SNMP_SCALAR_CREATE_NODE_READONLY(7, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
+static const struct snmp_scalar_node tcp_EstabResets = SNMP_SCALAR_CREATE_NODE_READONLY(8, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
+static const struct snmp_scalar_node tcp_CurrEstab = SNMP_SCALAR_CREATE_NODE_READONLY(9, SNMP_ASN1_TYPE_GAUGE, tcp_get_value);
+static const struct snmp_scalar_node tcp_InSegs = SNMP_SCALAR_CREATE_NODE_READONLY(10, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
+static const struct snmp_scalar_node tcp_OutSegs = SNMP_SCALAR_CREATE_NODE_READONLY(11, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
+static const struct snmp_scalar_node tcp_RetransSegs = SNMP_SCALAR_CREATE_NODE_READONLY(12, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
+static const struct snmp_scalar_node tcp_InErrs = SNMP_SCALAR_CREATE_NODE_READONLY(14, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
+static const struct snmp_scalar_node tcp_OutRsts = SNMP_SCALAR_CREATE_NODE_READONLY(15, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
+#if LWIP_HAVE_INT64
+static const struct snmp_scalar_node tcp_HCInSegs = SNMP_SCALAR_CREATE_NODE_READONLY(17, SNMP_ASN1_TYPE_COUNTER64, tcp_get_value);
+static const struct snmp_scalar_node tcp_HCOutSegs = SNMP_SCALAR_CREATE_NODE_READONLY(18, SNMP_ASN1_TYPE_COUNTER64, tcp_get_value);
+#endif
+
+#if LWIP_IPV4
+static const struct snmp_table_simple_col_def tcp_ConnTable_columns[] = {
+ { 1, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnState */
+ { 2, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnLocalAddress */
+ { 3, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnLocalPort */
+ { 4, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnRemAddress */
+ { 5, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 } /* tcpConnRemPort */
+};
+
+static const struct snmp_table_simple_node tcp_ConnTable = SNMP_TABLE_CREATE_SIMPLE(13, tcp_ConnTable_columns, tcp_ConnTable_get_cell_value, tcp_ConnTable_get_next_cell_instance_and_value);
+#endif /* LWIP_IPV4 */
+
+static const struct snmp_table_simple_col_def tcp_ConnectionTable_columns[] = {
+ /* all items except tcpConnectionState and tcpConnectionProcess are declared as not-accessible */
+ { 7, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnectionState */
+ { 8, SNMP_ASN1_TYPE_UNSIGNED32, SNMP_VARIANT_VALUE_TYPE_U32 } /* tcpConnectionProcess */
+};
+
+static const struct snmp_table_simple_node tcp_ConnectionTable = SNMP_TABLE_CREATE_SIMPLE(19, tcp_ConnectionTable_columns, tcp_ConnectionTable_get_cell_value, tcp_ConnectionTable_get_next_cell_instance_and_value);
+
+
+static const struct snmp_table_simple_col_def tcp_ListenerTable_columns[] = {
+ /* all items except tcpListenerProcess are declared as not-accessible */
+ { 4, SNMP_ASN1_TYPE_UNSIGNED32, SNMP_VARIANT_VALUE_TYPE_U32 } /* tcpListenerProcess */
+};
+
+static const struct snmp_table_simple_node tcp_ListenerTable = SNMP_TABLE_CREATE_SIMPLE(20, tcp_ListenerTable_columns, tcp_ListenerTable_get_cell_value, tcp_ListenerTable_get_next_cell_instance_and_value);
+
+/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */
+CREATE_LWIP_SYNC_NODE( 1, tcp_RtoAlgorithm)
+CREATE_LWIP_SYNC_NODE( 2, tcp_RtoMin)
+CREATE_LWIP_SYNC_NODE( 3, tcp_RtoMax)
+CREATE_LWIP_SYNC_NODE( 4, tcp_MaxConn)
+CREATE_LWIP_SYNC_NODE( 5, tcp_ActiveOpens)
+CREATE_LWIP_SYNC_NODE( 6, tcp_PassiveOpens)
+CREATE_LWIP_SYNC_NODE( 7, tcp_AttemptFails)
+CREATE_LWIP_SYNC_NODE( 8, tcp_EstabResets)
+CREATE_LWIP_SYNC_NODE( 9, tcp_CurrEstab)
+CREATE_LWIP_SYNC_NODE(10, tcp_InSegs)
+CREATE_LWIP_SYNC_NODE(11, tcp_OutSegs)
+CREATE_LWIP_SYNC_NODE(12, tcp_RetransSegs)
+#if LWIP_IPV4
+CREATE_LWIP_SYNC_NODE(13, tcp_ConnTable)
+#endif /* LWIP_IPV4 */
+CREATE_LWIP_SYNC_NODE(14, tcp_InErrs)
+CREATE_LWIP_SYNC_NODE(15, tcp_OutRsts)
+#if LWIP_HAVE_INT64
+CREATE_LWIP_SYNC_NODE(17, tcp_HCInSegs)
+CREATE_LWIP_SYNC_NODE(18, tcp_HCOutSegs)
+#endif
+CREATE_LWIP_SYNC_NODE(19, tcp_ConnectionTable)
+CREATE_LWIP_SYNC_NODE(20, tcp_ListenerTable)
+
+static const struct snmp_node *const tcp_nodes[] = {
+ &SYNC_NODE_NAME(tcp_RtoAlgorithm).node.node,
+ &SYNC_NODE_NAME(tcp_RtoMin).node.node,
+ &SYNC_NODE_NAME(tcp_RtoMax).node.node,
+ &SYNC_NODE_NAME(tcp_MaxConn).node.node,
+ &SYNC_NODE_NAME(tcp_ActiveOpens).node.node,
+ &SYNC_NODE_NAME(tcp_PassiveOpens).node.node,
+ &SYNC_NODE_NAME(tcp_AttemptFails).node.node,
+ &SYNC_NODE_NAME(tcp_EstabResets).node.node,
+ &SYNC_NODE_NAME(tcp_CurrEstab).node.node,
+ &SYNC_NODE_NAME(tcp_InSegs).node.node,
+ &SYNC_NODE_NAME(tcp_OutSegs).node.node,
+ &SYNC_NODE_NAME(tcp_RetransSegs).node.node,
+#if LWIP_IPV4
+ &SYNC_NODE_NAME(tcp_ConnTable).node.node,
+#endif /* LWIP_IPV4 */
+ &SYNC_NODE_NAME(tcp_InErrs).node.node,
+ &SYNC_NODE_NAME(tcp_OutRsts).node.node,
+ &SYNC_NODE_NAME(tcp_HCInSegs).node.node,
+#if LWIP_HAVE_INT64
+ &SYNC_NODE_NAME(tcp_HCOutSegs).node.node,
+ &SYNC_NODE_NAME(tcp_ConnectionTable).node.node,
+#endif
+ &SYNC_NODE_NAME(tcp_ListenerTable).node.node
+};
+
+const struct snmp_tree_node snmp_mib2_tcp_root = SNMP_CREATE_TREE_NODE(6, tcp_nodes);
+#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_TCP */
diff --git a/resources/ethernet/LwIP/apps/snmp/snmp_mib2_udp.c b/resources/ethernet/LwIP/apps/snmp/snmp_mib2_udp.c
new file mode 100644
index 00000000..3c89029e
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/snmp/snmp_mib2_udp.c
@@ -0,0 +1,372 @@
+/**
+ * @file
+ * Management Information Base II (RFC1213) UDP objects and functions.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Dirk Ziegelmeier
+ * Christiaan Simons
+ */
+
+#include "lwip/snmp.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_mib2.h"
+#include "lwip/apps/snmp_table.h"
+#include "lwip/apps/snmp_scalar.h"
+#include "lwip/udp.h"
+#include "lwip/stats.h"
+
+#include
+
+#if LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_UDP
+
+#if SNMP_USE_NETCONN
+#define SYNC_NODE_NAME(node_name) node_name ## _synced
+#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
+ static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
+#else
+#define SYNC_NODE_NAME(node_name) node_name
+#define CREATE_LWIP_SYNC_NODE(oid, node_name)
+#endif
+
+/* --- udp .1.3.6.1.2.1.7 ----------------------------------------------------- */
+
+static s16_t
+udp_get_value(struct snmp_node_instance *instance, void *value)
+{
+ u32_t *uint_ptr = (u32_t *)value;
+
+ switch (instance->node->oid) {
+ case 1: /* udpInDatagrams */
+ *uint_ptr = STATS_GET(mib2.udpindatagrams);
+ return sizeof(*uint_ptr);
+ case 2: /* udpNoPorts */
+ *uint_ptr = STATS_GET(mib2.udpnoports);
+ return sizeof(*uint_ptr);
+ case 3: /* udpInErrors */
+ *uint_ptr = STATS_GET(mib2.udpinerrors);
+ return sizeof(*uint_ptr);
+ case 4: /* udpOutDatagrams */
+ *uint_ptr = STATS_GET(mib2.udpoutdatagrams);
+ return sizeof(*uint_ptr);
+#if LWIP_HAVE_INT64
+ case 8: { /* udpHCInDatagrams */
+ /* use the 32 bit counter for now... */
+ u64_t val64 = STATS_GET(mib2.udpindatagrams);
+ *((u64_t *)value) = val64;
+ }
+ return sizeof(u64_t);
+ case 9: { /* udpHCOutDatagrams */
+ /* use the 32 bit counter for now... */
+ u64_t val64 = STATS_GET(mib2.udpoutdatagrams);
+ *((u64_t *)value) = val64;
+ }
+ return sizeof(u64_t);
+#endif
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG, ("udp_get_value(): unknown id: %"S32_F"\n", instance->node->oid));
+ break;
+ }
+
+ return 0;
+}
+
+/* --- udpEndpointTable --- */
+
+static snmp_err_t
+udp_endpointTable_get_cell_value_core(const u32_t *column, union snmp_variant_value *value)
+{
+ /* all items except udpEndpointProcess are declared as not-accessible */
+ switch (*column) {
+ case 8: /* udpEndpointProcess */
+ value->u32 = 0; /* not supported */
+ break;
+ default:
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ return SNMP_ERR_NOERROR;
+}
+
+static snmp_err_t
+udp_endpointTable_get_cell_value(const u32_t *column, const u32_t *row_oid, u8_t row_oid_len, union snmp_variant_value *value, u32_t *value_len)
+{
+ ip_addr_t local_ip, remote_ip;
+ u16_t local_port, remote_port;
+ struct udp_pcb *pcb;
+ u8_t idx = 0;
+
+ LWIP_UNUSED_ARG(value_len);
+
+ /* udpEndpointLocalAddressType + udpEndpointLocalAddress + udpEndpointLocalPort */
+ idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len - idx, &local_ip, &local_port);
+ if (idx == 0) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ /* udpEndpointRemoteAddressType + udpEndpointRemoteAddress + udpEndpointRemotePort */
+ idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len - idx, &remote_ip, &remote_port);
+ if (idx == 0) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ /* udpEndpointInstance */
+ if (row_oid_len < (idx + 1)) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+ if (row_oid[idx] != 0) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ /* find udp_pcb with requested ip and port*/
+ pcb = udp_pcbs;
+ while (pcb != NULL) {
+ if (ip_addr_cmp(&local_ip, &pcb->local_ip) &&
+ (local_port == pcb->local_port) &&
+ ip_addr_cmp(&remote_ip, &pcb->remote_ip) &&
+ (remote_port == pcb->remote_port)) {
+ /* fill in object properties */
+ return udp_endpointTable_get_cell_value_core(column, value);
+ }
+ pcb = pcb->next;
+ }
+
+ /* not found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static snmp_err_t
+udp_endpointTable_get_next_cell_instance_and_value(const u32_t *column, struct snmp_obj_id *row_oid, union snmp_variant_value *value, u32_t *value_len)
+{
+ struct udp_pcb *pcb;
+ struct snmp_next_oid_state state;
+ /* 1x udpEndpointLocalAddressType + 1x OID len + 16x udpEndpointLocalAddress + 1x udpEndpointLocalPort +
+ * 1x udpEndpointRemoteAddressType + 1x OID len + 16x udpEndpointRemoteAddress + 1x udpEndpointRemotePort +
+ * 1x udpEndpointInstance = 39
+ */
+ u32_t result_temp[39];
+
+ LWIP_UNUSED_ARG(value_len);
+
+ /* init struct to search next oid */
+ snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(result_temp));
+
+ /* iterate over all possible OIDs to find the next one */
+ pcb = udp_pcbs;
+ while (pcb != NULL) {
+ u32_t test_oid[LWIP_ARRAYSIZE(result_temp)];
+ u8_t idx = 0;
+
+ /* udpEndpointLocalAddressType + udpEndpointLocalAddress + udpEndpointLocalPort */
+ idx += snmp_ip_port_to_oid(&pcb->local_ip, pcb->local_port, &test_oid[idx]);
+
+ /* udpEndpointRemoteAddressType + udpEndpointRemoteAddress + udpEndpointRemotePort */
+ idx += snmp_ip_port_to_oid(&pcb->remote_ip, pcb->remote_port, &test_oid[idx]);
+
+ test_oid[idx] = 0; /* udpEndpointInstance */
+ idx++;
+
+ /* check generated OID: is it a candidate for the next one? */
+ snmp_next_oid_check(&state, test_oid, idx, NULL);
+
+ pcb = pcb->next;
+ }
+
+ /* did we find a next one? */
+ if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+ snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+ /* fill in object properties */
+ return udp_endpointTable_get_cell_value_core(column, value);
+ } else {
+ /* not found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+}
+
+/* --- udpTable --- */
+
+#if LWIP_IPV4
+
+/* list of allowed value ranges for incoming OID */
+static const struct snmp_oid_range udp_Table_oid_ranges[] = {
+ { 0, 0xff }, /* IP A */
+ { 0, 0xff }, /* IP B */
+ { 0, 0xff }, /* IP C */
+ { 0, 0xff }, /* IP D */
+ { 1, 0xffff } /* Port */
+};
+
+static snmp_err_t
+udp_Table_get_cell_value_core(struct udp_pcb *pcb, const u32_t *column, union snmp_variant_value *value, u32_t *value_len)
+{
+ LWIP_UNUSED_ARG(value_len);
+
+ switch (*column) {
+ case 1: /* udpLocalAddress */
+ /* set reference to PCB local IP and return a generic node that copies IP4 addresses */
+ value->u32 = ip_2_ip4(&pcb->local_ip)->addr;
+ break;
+ case 2: /* udpLocalPort */
+ /* set reference to PCB local port and return a generic node that copies u16_t values */
+ value->u32 = pcb->local_port;
+ break;
+ default:
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ return SNMP_ERR_NOERROR;
+}
+
+static snmp_err_t
+udp_Table_get_cell_value(const u32_t *column, const u32_t *row_oid, u8_t row_oid_len, union snmp_variant_value *value, u32_t *value_len)
+{
+ ip4_addr_t ip;
+ u16_t port;
+ struct udp_pcb *pcb;
+
+ /* check if incoming OID length and if values are in plausible range */
+ if (!snmp_oid_in_range(row_oid, row_oid_len, udp_Table_oid_ranges, LWIP_ARRAYSIZE(udp_Table_oid_ranges))) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ /* get IP and port from incoming OID */
+ snmp_oid_to_ip4(&row_oid[0], &ip); /* we know it succeeds because of oid_in_range check above */
+ port = (u16_t)row_oid[4];
+
+ /* find udp_pcb with requested ip and port*/
+ pcb = udp_pcbs;
+ while (pcb != NULL) {
+ if (IP_IS_V4_VAL(pcb->local_ip)) {
+ if (ip4_addr_cmp(&ip, ip_2_ip4(&pcb->local_ip)) && (port == pcb->local_port)) {
+ /* fill in object properties */
+ return udp_Table_get_cell_value_core(pcb, column, value, value_len);
+ }
+ }
+ pcb = pcb->next;
+ }
+
+ /* not found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static snmp_err_t
+udp_Table_get_next_cell_instance_and_value(const u32_t *column, struct snmp_obj_id *row_oid, union snmp_variant_value *value, u32_t *value_len)
+{
+ struct udp_pcb *pcb;
+ struct snmp_next_oid_state state;
+ u32_t result_temp[LWIP_ARRAYSIZE(udp_Table_oid_ranges)];
+
+ /* init struct to search next oid */
+ snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(udp_Table_oid_ranges));
+
+ /* iterate over all possible OIDs to find the next one */
+ pcb = udp_pcbs;
+ while (pcb != NULL) {
+ u32_t test_oid[LWIP_ARRAYSIZE(udp_Table_oid_ranges)];
+
+ if (IP_IS_V4_VAL(pcb->local_ip)) {
+ snmp_ip4_to_oid(ip_2_ip4(&pcb->local_ip), &test_oid[0]);
+ test_oid[4] = pcb->local_port;
+
+ /* check generated OID: is it a candidate for the next one? */
+ snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(udp_Table_oid_ranges), pcb);
+ }
+
+ pcb = pcb->next;
+ }
+
+ /* did we find a next one? */
+ if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+ snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+ /* fill in object properties */
+ return udp_Table_get_cell_value_core((struct udp_pcb *)state.reference, column, value, value_len);
+ } else {
+ /* not found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+}
+
+#endif /* LWIP_IPV4 */
+
+static const struct snmp_scalar_node udp_inDatagrams = SNMP_SCALAR_CREATE_NODE_READONLY(1, SNMP_ASN1_TYPE_COUNTER, udp_get_value);
+static const struct snmp_scalar_node udp_noPorts = SNMP_SCALAR_CREATE_NODE_READONLY(2, SNMP_ASN1_TYPE_COUNTER, udp_get_value);
+static const struct snmp_scalar_node udp_inErrors = SNMP_SCALAR_CREATE_NODE_READONLY(3, SNMP_ASN1_TYPE_COUNTER, udp_get_value);
+static const struct snmp_scalar_node udp_outDatagrams = SNMP_SCALAR_CREATE_NODE_READONLY(4, SNMP_ASN1_TYPE_COUNTER, udp_get_value);
+#if LWIP_HAVE_INT64
+static const struct snmp_scalar_node udp_HCInDatagrams = SNMP_SCALAR_CREATE_NODE_READONLY(8, SNMP_ASN1_TYPE_COUNTER64, udp_get_value);
+static const struct snmp_scalar_node udp_HCOutDatagrams = SNMP_SCALAR_CREATE_NODE_READONLY(9, SNMP_ASN1_TYPE_COUNTER64, udp_get_value);
+#endif
+
+#if LWIP_IPV4
+static const struct snmp_table_simple_col_def udp_Table_columns[] = {
+ { 1, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* udpLocalAddress */
+ { 2, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 } /* udpLocalPort */
+};
+static const struct snmp_table_simple_node udp_Table = SNMP_TABLE_CREATE_SIMPLE(5, udp_Table_columns, udp_Table_get_cell_value, udp_Table_get_next_cell_instance_and_value);
+#endif /* LWIP_IPV4 */
+
+static const struct snmp_table_simple_col_def udp_endpointTable_columns[] = {
+ /* all items except udpEndpointProcess are declared as not-accessible */
+ { 8, SNMP_ASN1_TYPE_UNSIGNED32, SNMP_VARIANT_VALUE_TYPE_U32 } /* udpEndpointProcess */
+};
+
+static const struct snmp_table_simple_node udp_endpointTable = SNMP_TABLE_CREATE_SIMPLE(7, udp_endpointTable_columns, udp_endpointTable_get_cell_value, udp_endpointTable_get_next_cell_instance_and_value);
+
+/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */
+CREATE_LWIP_SYNC_NODE(1, udp_inDatagrams)
+CREATE_LWIP_SYNC_NODE(2, udp_noPorts)
+CREATE_LWIP_SYNC_NODE(3, udp_inErrors)
+CREATE_LWIP_SYNC_NODE(4, udp_outDatagrams)
+#if LWIP_IPV4
+CREATE_LWIP_SYNC_NODE(5, udp_Table)
+#endif /* LWIP_IPV4 */
+CREATE_LWIP_SYNC_NODE(7, udp_endpointTable)
+#if LWIP_HAVE_INT64
+CREATE_LWIP_SYNC_NODE(8, udp_HCInDatagrams)
+CREATE_LWIP_SYNC_NODE(9, udp_HCOutDatagrams)
+#endif
+
+static const struct snmp_node *const udp_nodes[] = {
+ &SYNC_NODE_NAME(udp_inDatagrams).node.node,
+ &SYNC_NODE_NAME(udp_noPorts).node.node,
+ &SYNC_NODE_NAME(udp_inErrors).node.node,
+ &SYNC_NODE_NAME(udp_outDatagrams).node.node,
+#if LWIP_IPV4
+ &SYNC_NODE_NAME(udp_Table).node.node,
+#endif /* LWIP_IPV4 */
+ &SYNC_NODE_NAME(udp_endpointTable).node.node
+#if LWIP_HAVE_INT64
+ ,
+ &SYNC_NODE_NAME(udp_HCInDatagrams).node.node,
+ &SYNC_NODE_NAME(udp_HCOutDatagrams).node.node
+#endif
+};
+
+const struct snmp_tree_node snmp_mib2_udp_root = SNMP_CREATE_TREE_NODE(7, udp_nodes);
+#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_UDP */
diff --git a/resources/ethernet/LwIP/apps/snmp/snmp_msg.c b/resources/ethernet/LwIP/apps/snmp/snmp_msg.c
new file mode 100644
index 00000000..a2e9057f
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/snmp/snmp_msg.c
@@ -0,0 +1,1951 @@
+/**
+ * @file
+ * SNMP message processing (RFC1157).
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * Copyright (c) 2016 Elias Oenal.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons
+ * Martin Hentschel
+ * Elias Oenal
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "snmp_msg.h"
+#include "snmp_asn1.h"
+#include "snmp_core_priv.h"
+#include "lwip/ip_addr.h"
+#include "lwip/stats.h"
+
+#if LWIP_SNMP_V3
+#include "lwip/apps/snmpv3.h"
+#include "snmpv3_priv.h"
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+#endif
+
+#include
+
+#define SNMP_V3_AUTH_FLAG 0x01
+#define SNMP_V3_PRIV_FLAG 0x02
+
+/* Security levels */
+#define SNMP_V3_NOAUTHNOPRIV 0x00
+#define SNMP_V3_AUTHNOPRIV SNMP_V3_AUTH_FLAG
+#define SNMP_V3_AUTHPRIV (SNMP_V3_AUTH_FLAG | SNMP_V3_PRIV_FLAG)
+
+/* public (non-static) constants */
+/** SNMP community string */
+const char *snmp_community = SNMP_COMMUNITY;
+/** SNMP community string for write access */
+const char *snmp_community_write = SNMP_COMMUNITY_WRITE;
+/** SNMP community string for sending traps */
+const char *snmp_community_trap = SNMP_COMMUNITY_TRAP;
+
+snmp_write_callback_fct snmp_write_callback = NULL;
+void *snmp_write_callback_arg = NULL;
+
+#if LWIP_SNMP_CONFIGURE_VERSIONS
+
+static u8_t v1_enabled = 1;
+static u8_t v2c_enabled = 1;
+static u8_t v3_enabled = 1;
+
+static u8_t
+snmp_version_enabled(u8_t version)
+{
+ if (version == SNMP_VERSION_1) {
+ return v1_enabled;
+ } else if (version == SNMP_VERSION_2c) {
+ return v2c_enabled;
+ }
+#if LWIP_SNMP_V3
+ else if (version == SNMP_VERSION_3) {
+ return v3_enabled;
+ }
+#endif
+ else {
+ LWIP_ASSERT("Invalid SNMP version", 0);
+ return 0;
+ }
+}
+
+u8_t
+snmp_v1_enabled(void)
+{
+ return snmp_version_enabled(SNMP_VERSION_1);
+}
+
+u8_t
+snmp_v2c_enabled(void)
+{
+ return snmp_version_enabled(SNMP_VERSION_2c);
+}
+
+u8_t
+snmp_v3_enabled(void)
+{
+ return snmp_version_enabled(SNMP_VERSION_3);
+}
+
+static void
+snmp_version_enable(u8_t version, u8_t enable)
+{
+ if (version == SNMP_VERSION_1) {
+ v1_enabled = enable;
+ } else if (version == SNMP_VERSION_2c) {
+ v2c_enabled = enable;
+ }
+#if LWIP_SNMP_V3
+ else if (version == SNMP_VERSION_3) {
+ v3_enabled = enable;
+ }
+#endif
+ else {
+ LWIP_ASSERT("Invalid SNMP version", 0);
+ }
+}
+
+void
+snmp_v1_enable(u8_t enable)
+{
+ snmp_version_enable(SNMP_VERSION_1, enable);
+}
+
+void
+snmp_v2c_enable(u8_t enable)
+{
+ snmp_version_enable(SNMP_VERSION_2c, enable);
+}
+
+void
+snmp_v3_enable(u8_t enable)
+{
+ snmp_version_enable(SNMP_VERSION_3, enable);
+}
+
+#endif
+
+/**
+ * @ingroup snmp_core
+ * Returns current SNMP community string.
+ * @return current SNMP community string
+ */
+const char *
+snmp_get_community(void)
+{
+ return snmp_community;
+}
+
+/**
+ * @ingroup snmp_core
+ * Sets SNMP community string.
+ * The string itself (its storage) must be valid throughout the whole life of
+ * program (or until it is changed to sth else).
+ *
+ * @param community is a pointer to new community string
+ */
+void
+snmp_set_community(const char *const community)
+{
+ LWIP_ASSERT_CORE_LOCKED();
+ LWIP_ASSERT("community string is too long!", strlen(community) <= SNMP_MAX_COMMUNITY_STR_LEN);
+ snmp_community = community;
+}
+
+/**
+ * @ingroup snmp_core
+ * Returns current SNMP write-access community string.
+ * @return current SNMP write-access community string
+ */
+const char *
+snmp_get_community_write(void)
+{
+ return snmp_community_write;
+}
+
+/**
+ * @ingroup snmp_traps
+ * Returns current SNMP community string used for sending traps.
+ * @return current SNMP community string used for sending traps
+ */
+const char *
+snmp_get_community_trap(void)
+{
+ return snmp_community_trap;
+}
+
+/**
+ * @ingroup snmp_core
+ * Sets SNMP community string for write-access.
+ * The string itself (its storage) must be valid throughout the whole life of
+ * program (or until it is changed to sth else).
+ *
+ * @param community is a pointer to new write-access community string
+ */
+void
+snmp_set_community_write(const char *const community)
+{
+ LWIP_ASSERT_CORE_LOCKED();
+ LWIP_ASSERT("community string must not be NULL", community != NULL);
+ LWIP_ASSERT("community string is too long!", strlen(community) <= SNMP_MAX_COMMUNITY_STR_LEN);
+ snmp_community_write = community;
+}
+
+/**
+ * @ingroup snmp_traps
+ * Sets SNMP community string used for sending traps.
+ * The string itself (its storage) must be valid throughout the whole life of
+ * program (or until it is changed to sth else).
+ *
+ * @param community is a pointer to new trap community string
+ */
+void
+snmp_set_community_trap(const char *const community)
+{
+ LWIP_ASSERT_CORE_LOCKED();
+ LWIP_ASSERT("community string is too long!", strlen(community) <= SNMP_MAX_COMMUNITY_STR_LEN);
+ snmp_community_trap = community;
+}
+
+/**
+ * @ingroup snmp_core
+ * Callback fired on every successful write access
+ */
+void
+snmp_set_write_callback(snmp_write_callback_fct write_callback, void *callback_arg)
+{
+ LWIP_ASSERT_CORE_LOCKED();
+ snmp_write_callback = write_callback;
+ snmp_write_callback_arg = callback_arg;
+}
+
+/* ----------------------------------------------------------------------- */
+/* forward declarations */
+/* ----------------------------------------------------------------------- */
+
+static err_t snmp_process_get_request(struct snmp_request *request);
+static err_t snmp_process_getnext_request(struct snmp_request *request);
+static err_t snmp_process_getbulk_request(struct snmp_request *request);
+static err_t snmp_process_set_request(struct snmp_request *request);
+
+static err_t snmp_parse_inbound_frame(struct snmp_request *request);
+static err_t snmp_prepare_outbound_frame(struct snmp_request *request);
+static err_t snmp_complete_outbound_frame(struct snmp_request *request);
+static void snmp_execute_write_callbacks(struct snmp_request *request);
+
+
+/* ----------------------------------------------------------------------- */
+/* implementation */
+/* ----------------------------------------------------------------------- */
+
+void
+snmp_receive(void *handle, struct pbuf *p, const ip_addr_t *source_ip, u16_t port)
+{
+ err_t err;
+ struct snmp_request request;
+
+ memset(&request, 0, sizeof(request));
+ request.handle = handle;
+ request.source_ip = source_ip;
+ request.source_port = port;
+ request.inbound_pbuf = p;
+
+ snmp_stats.inpkts++;
+
+ err = snmp_parse_inbound_frame(&request);
+ if (err == ERR_OK) {
+ err = snmp_prepare_outbound_frame(&request);
+ if (err == ERR_OK) {
+
+ if (request.error_status == SNMP_ERR_NOERROR) {
+ /* only process frame if we do not already have an error to return (e.g. all readonly) */
+ if (request.request_type == SNMP_ASN1_CONTEXT_PDU_GET_REQ) {
+ err = snmp_process_get_request(&request);
+ } else if (request.request_type == SNMP_ASN1_CONTEXT_PDU_GET_NEXT_REQ) {
+ err = snmp_process_getnext_request(&request);
+ } else if (request.request_type == SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ) {
+ err = snmp_process_getbulk_request(&request);
+ } else if (request.request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ) {
+ err = snmp_process_set_request(&request);
+ }
+ }
+#if LWIP_SNMP_V3
+ else {
+ struct snmp_varbind vb;
+
+ vb.next = NULL;
+ vb.prev = NULL;
+ vb.type = SNMP_ASN1_TYPE_COUNTER32;
+ vb.value_len = sizeof(u32_t);
+
+ switch (request.error_status) {
+ case SNMP_ERR_AUTHORIZATIONERROR: {
+ static const u32_t oid[] = { 1, 3, 6, 1, 6, 3, 15, 1, 1, 5, 0 };
+ snmp_oid_assign(&vb.oid, oid, LWIP_ARRAYSIZE(oid));
+ vb.value = &snmp_stats.wrongdigests;
+ }
+ break;
+ case SNMP_ERR_UNKNOWN_ENGINEID: {
+ static const u32_t oid[] = { 1, 3, 6, 1, 6, 3, 15, 1, 1, 4, 0 };
+ snmp_oid_assign(&vb.oid, oid, LWIP_ARRAYSIZE(oid));
+ vb.value = &snmp_stats.unknownengineids;
+ }
+ break;
+ case SNMP_ERR_UNKNOWN_SECURITYNAME: {
+ static const u32_t oid[] = { 1, 3, 6, 1, 6, 3, 15, 1, 1, 3, 0 };
+ snmp_oid_assign(&vb.oid, oid, LWIP_ARRAYSIZE(oid));
+ vb.value = &snmp_stats.unknownusernames;
+ }
+ break;
+ case SNMP_ERR_UNSUPPORTED_SECLEVEL: {
+ static const u32_t oid[] = { 1, 3, 6, 1, 6, 3, 15, 1, 1, 1, 0 };
+ snmp_oid_assign(&vb.oid, oid, LWIP_ARRAYSIZE(oid));
+ vb.value = &snmp_stats.unsupportedseclevels;
+ }
+ break;
+ case SNMP_ERR_NOTINTIMEWINDOW: {
+ static const u32_t oid[] = { 1, 3, 6, 1, 6, 3, 15, 1, 1, 2, 0 };
+ snmp_oid_assign(&vb.oid, oid, LWIP_ARRAYSIZE(oid));
+ vb.value = &snmp_stats.notintimewindows;
+ }
+ break;
+ case SNMP_ERR_DECRYIPTION_ERROR: {
+ static const u32_t oid[] = { 1, 3, 6, 1, 6, 3, 15, 1, 1, 6, 0 };
+ snmp_oid_assign(&vb.oid, oid, LWIP_ARRAYSIZE(oid));
+ vb.value = &snmp_stats.decryptionerrors;
+ }
+ break;
+ default:
+ /* Unknown or unhandled error_status */
+ err = ERR_ARG;
+ }
+
+ if (err == ERR_OK) {
+ snmp_append_outbound_varbind(&(request.outbound_pbuf_stream), &vb);
+ request.error_status = SNMP_ERR_NOERROR;
+ }
+
+ request.request_out_type = (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_REPORT);
+ request.request_id = request.msg_id;
+ }
+#endif
+
+ if (err == ERR_OK) {
+ err = snmp_complete_outbound_frame(&request);
+
+ if (err == ERR_OK) {
+ err = snmp_sendto(request.handle, request.outbound_pbuf, request.source_ip, request.source_port);
+
+ if ((request.request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ)
+ && (request.error_status == SNMP_ERR_NOERROR)
+ && (snmp_write_callback != NULL)) {
+ /* raise write notification for all written objects */
+ snmp_execute_write_callbacks(&request);
+ }
+ }
+ }
+ }
+
+ if (request.outbound_pbuf != NULL) {
+ pbuf_free(request.outbound_pbuf);
+ }
+ }
+}
+
+static u8_t
+snmp_msg_getnext_validate_node_inst(struct snmp_node_instance *node_instance, void *validate_arg)
+{
+ if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_READ) != SNMP_NODE_INSTANCE_ACCESS_READ) || (node_instance->get_value == NULL)) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+#if LWIP_HAVE_INT64
+ if ((node_instance->asn1_type == SNMP_ASN1_TYPE_COUNTER64) && (((struct snmp_request *)validate_arg)->version == SNMP_VERSION_1)) {
+ /* according to RFC 2089 skip Counter64 objects in GetNext requests from v1 clients */
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+#endif
+
+ return SNMP_ERR_NOERROR;
+}
+
+static void
+snmp_process_varbind(struct snmp_request *request, struct snmp_varbind *vb, u8_t get_next)
+{
+ err_t err;
+ struct snmp_node_instance node_instance;
+ memset(&node_instance, 0, sizeof(node_instance));
+
+ if (get_next) {
+ struct snmp_obj_id result_oid;
+ request->error_status = snmp_get_next_node_instance_from_oid(vb->oid.id, vb->oid.len, snmp_msg_getnext_validate_node_inst, request, &result_oid, &node_instance);
+
+ if (request->error_status == SNMP_ERR_NOERROR) {
+ snmp_oid_assign(&vb->oid, result_oid.id, result_oid.len);
+ }
+ } else {
+ request->error_status = snmp_get_node_instance_from_oid(vb->oid.id, vb->oid.len, &node_instance);
+
+ if (request->error_status == SNMP_ERR_NOERROR) {
+ /* use 'getnext_validate' method for validation to avoid code duplication (some checks have to be executed here) */
+ request->error_status = snmp_msg_getnext_validate_node_inst(&node_instance, request);
+
+ if (request->error_status != SNMP_ERR_NOERROR) {
+ if (node_instance.release_instance != NULL) {
+ node_instance.release_instance(&node_instance);
+ }
+ }
+ }
+ }
+
+ if (request->error_status != SNMP_ERR_NOERROR) {
+ if (request->error_status >= SNMP_VARBIND_EXCEPTION_OFFSET) {
+ if ((request->version == SNMP_VERSION_2c) || request->version == SNMP_VERSION_3) {
+ /* in SNMP v2c a varbind related exception is stored in varbind and not in frame header */
+ vb->type = (SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_CLASS_CONTEXT | (request->error_status & SNMP_VARBIND_EXCEPTION_MASK));
+ vb->value_len = 0;
+
+ err = snmp_append_outbound_varbind(&(request->outbound_pbuf_stream), vb);
+ if (err == ERR_OK) {
+ /* we stored the exception in varbind -> go on */
+ request->error_status = SNMP_ERR_NOERROR;
+ } else if (err == ERR_BUF) {
+ request->error_status = SNMP_ERR_TOOBIG;
+ } else {
+ request->error_status = SNMP_ERR_GENERROR;
+ }
+ }
+ } else {
+ /* according to RFC 1157/1905, all other errors only return genError */
+ request->error_status = SNMP_ERR_GENERROR;
+ }
+ } else {
+ s16_t len = node_instance.get_value(&node_instance, vb->value);
+
+ if (len >= 0) {
+ vb->value_len = (u16_t)len; /* cast is OK because we checked >= 0 above */
+ vb->type = node_instance.asn1_type;
+
+ LWIP_ASSERT("SNMP_MAX_VALUE_SIZE is configured too low", (vb->value_len & ~SNMP_GET_VALUE_RAW_DATA) <= SNMP_MAX_VALUE_SIZE);
+ err = snmp_append_outbound_varbind(&request->outbound_pbuf_stream, vb);
+
+ if (err == ERR_BUF) {
+ request->error_status = SNMP_ERR_TOOBIG;
+ } else if (err != ERR_OK) {
+ request->error_status = SNMP_ERR_GENERROR;
+ }
+ } else {
+ request->error_status = SNMP_ERR_GENERROR;
+ }
+
+ if (node_instance.release_instance != NULL) {
+ node_instance.release_instance(&node_instance);
+ }
+ }
+}
+
+
+/**
+ * Service an internal or external event for SNMP GET.
+ *
+ * @param request points to the associated message process state
+ */
+static err_t
+snmp_process_get_request(struct snmp_request *request)
+{
+ snmp_vb_enumerator_err_t err;
+ struct snmp_varbind vb;
+ vb.value = request->value_buffer;
+
+ LWIP_DEBUGF(SNMP_DEBUG, ("SNMP get request\n"));
+
+ while (request->error_status == SNMP_ERR_NOERROR) {
+ err = snmp_vb_enumerator_get_next(&request->inbound_varbind_enumerator, &vb);
+ if (err == SNMP_VB_ENUMERATOR_ERR_OK) {
+ if ((vb.type == SNMP_ASN1_TYPE_NULL) && (vb.value_len == 0)) {
+ snmp_process_varbind(request, &vb, 0);
+ } else {
+ request->error_status = SNMP_ERR_GENERROR;
+ }
+ } else if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) {
+ /* no more varbinds in request */
+ break;
+ } else if (err == SNMP_VB_ENUMERATOR_ERR_ASN1ERROR) {
+ /* malformed ASN.1, don't answer */
+ return ERR_ARG;
+ } else {
+ request->error_status = SNMP_ERR_GENERROR;
+ }
+ }
+
+ return ERR_OK;
+}
+
+/**
+ * Service an internal or external event for SNMP GET.
+ *
+ * @param request points to the associated message process state
+ */
+static err_t
+snmp_process_getnext_request(struct snmp_request *request)
+{
+ snmp_vb_enumerator_err_t err;
+ struct snmp_varbind vb;
+ vb.value = request->value_buffer;
+
+ LWIP_DEBUGF(SNMP_DEBUG, ("SNMP get-next request\n"));
+
+ while (request->error_status == SNMP_ERR_NOERROR) {
+ err = snmp_vb_enumerator_get_next(&request->inbound_varbind_enumerator, &vb);
+ if (err == SNMP_VB_ENUMERATOR_ERR_OK) {
+ if ((vb.type == SNMP_ASN1_TYPE_NULL) && (vb.value_len == 0)) {
+ snmp_process_varbind(request, &vb, 1);
+ } else {
+ request->error_status = SNMP_ERR_GENERROR;
+ }
+ } else if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) {
+ /* no more varbinds in request */
+ break;
+ } else if (err == SNMP_VB_ENUMERATOR_ERR_ASN1ERROR) {
+ /* malformed ASN.1, don't answer */
+ return ERR_ARG;
+ } else {
+ request->error_status = SNMP_ERR_GENERROR;
+ }
+ }
+
+ return ERR_OK;
+}
+
+/**
+ * Service an internal or external event for SNMP GETBULKT.
+ *
+ * @param request points to the associated message process state
+ */
+static err_t
+snmp_process_getbulk_request(struct snmp_request *request)
+{
+ snmp_vb_enumerator_err_t err;
+ s32_t non_repeaters = request->non_repeaters;
+ s32_t repetitions;
+ u16_t repetition_offset = 0;
+ struct snmp_varbind_enumerator repetition_varbind_enumerator;
+ struct snmp_varbind vb;
+ vb.value = request->value_buffer;
+
+ if (SNMP_LWIP_GETBULK_MAX_REPETITIONS > 0) {
+ repetitions = LWIP_MIN(request->max_repetitions, SNMP_LWIP_GETBULK_MAX_REPETITIONS);
+ } else {
+ repetitions = request->max_repetitions;
+ }
+
+ LWIP_DEBUGF(SNMP_DEBUG, ("SNMP get-bulk request\n"));
+
+ /* process non repeaters and first repetition */
+ while (request->error_status == SNMP_ERR_NOERROR) {
+ if (non_repeaters == 0) {
+ repetition_offset = request->outbound_pbuf_stream.offset;
+
+ if (repetitions == 0) {
+ /* do not resolve repeaters when repetitions is set to 0 */
+ break;
+ }
+ repetitions--;
+ }
+
+ err = snmp_vb_enumerator_get_next(&request->inbound_varbind_enumerator, &vb);
+ if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) {
+ /* no more varbinds in request */
+ break;
+ } else if (err == SNMP_VB_ENUMERATOR_ERR_ASN1ERROR) {
+ /* malformed ASN.1, don't answer */
+ return ERR_ARG;
+ } else if ((err != SNMP_VB_ENUMERATOR_ERR_OK) || (vb.type != SNMP_ASN1_TYPE_NULL) || (vb.value_len != 0)) {
+ request->error_status = SNMP_ERR_GENERROR;
+ } else {
+ snmp_process_varbind(request, &vb, 1);
+ non_repeaters--;
+ }
+ }
+
+ /* process repetitions > 1 */
+ while ((request->error_status == SNMP_ERR_NOERROR) && (repetitions > 0) && (request->outbound_pbuf_stream.offset != repetition_offset)) {
+
+ u8_t all_endofmibview = 1;
+
+ snmp_vb_enumerator_init(&repetition_varbind_enumerator, request->outbound_pbuf, repetition_offset, request->outbound_pbuf_stream.offset - repetition_offset);
+ repetition_offset = request->outbound_pbuf_stream.offset; /* for next loop */
+
+ while (request->error_status == SNMP_ERR_NOERROR) {
+ vb.value = NULL; /* do NOT decode value (we enumerate outbound buffer here, so all varbinds have values assigned) */
+ err = snmp_vb_enumerator_get_next(&repetition_varbind_enumerator, &vb);
+ if (err == SNMP_VB_ENUMERATOR_ERR_OK) {
+ vb.value = request->value_buffer;
+ snmp_process_varbind(request, &vb, 1);
+
+ if (request->error_status != SNMP_ERR_NOERROR) {
+ /* already set correct error-index (here it cannot be taken from inbound varbind enumerator) */
+ request->error_index = request->non_repeaters + repetition_varbind_enumerator.varbind_count;
+ } else if (vb.type != (SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTEXT_VARBIND_END_OF_MIB_VIEW)) {
+ all_endofmibview = 0;
+ }
+ } else if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) {
+ /* no more varbinds in request */
+ break;
+ } else {
+ LWIP_DEBUGF(SNMP_DEBUG, ("Very strange, we cannot parse the varbind output that we created just before!"));
+ request->error_status = SNMP_ERR_GENERROR;
+ request->error_index = request->non_repeaters + repetition_varbind_enumerator.varbind_count;
+ }
+ }
+
+ if ((request->error_status == SNMP_ERR_NOERROR) && all_endofmibview) {
+ /* stop when all varbinds in a loop return EndOfMibView */
+ break;
+ }
+
+ repetitions--;
+ }
+
+ if (request->error_status == SNMP_ERR_TOOBIG) {
+ /* for GetBulk it is ok, if not all requested variables fit into the response -> just return the varbinds added so far */
+ request->error_status = SNMP_ERR_NOERROR;
+ }
+
+ return ERR_OK;
+}
+
+/**
+ * Service an internal or external event for SNMP SET.
+ *
+ * @param request points to the associated message process state
+ */
+static err_t
+snmp_process_set_request(struct snmp_request *request)
+{
+ snmp_vb_enumerator_err_t err;
+ struct snmp_varbind vb;
+ vb.value = request->value_buffer;
+
+ LWIP_DEBUGF(SNMP_DEBUG, ("SNMP set request\n"));
+
+ /* perform set test on all objects */
+ while (request->error_status == SNMP_ERR_NOERROR) {
+ err = snmp_vb_enumerator_get_next(&request->inbound_varbind_enumerator, &vb);
+ if (err == SNMP_VB_ENUMERATOR_ERR_OK) {
+ struct snmp_node_instance node_instance;
+ memset(&node_instance, 0, sizeof(node_instance));
+
+ request->error_status = snmp_get_node_instance_from_oid(vb.oid.id, vb.oid.len, &node_instance);
+ if (request->error_status == SNMP_ERR_NOERROR) {
+ if (node_instance.asn1_type != vb.type) {
+ request->error_status = SNMP_ERR_WRONGTYPE;
+ } else if (((node_instance.access & SNMP_NODE_INSTANCE_ACCESS_WRITE) != SNMP_NODE_INSTANCE_ACCESS_WRITE) || (node_instance.set_value == NULL)) {
+ request->error_status = SNMP_ERR_NOTWRITABLE;
+ } else {
+ if (node_instance.set_test != NULL) {
+ request->error_status = node_instance.set_test(&node_instance, vb.value_len, vb.value);
+ }
+ }
+
+ if (node_instance.release_instance != NULL) {
+ node_instance.release_instance(&node_instance);
+ }
+ }
+ } else if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) {
+ /* no more varbinds in request */
+ break;
+ } else if (err == SNMP_VB_ENUMERATOR_ERR_INVALIDLENGTH) {
+ request->error_status = SNMP_ERR_WRONGLENGTH;
+ } else if (err == SNMP_VB_ENUMERATOR_ERR_ASN1ERROR) {
+ /* malformed ASN.1, don't answer */
+ return ERR_ARG;
+ } else {
+ request->error_status = SNMP_ERR_GENERROR;
+ }
+ }
+
+ /* perform real set operation on all objects */
+ if (request->error_status == SNMP_ERR_NOERROR) {
+ snmp_vb_enumerator_init(&request->inbound_varbind_enumerator, request->inbound_pbuf, request->inbound_varbind_offset, request->inbound_varbind_len);
+ while (request->error_status == SNMP_ERR_NOERROR) {
+ err = snmp_vb_enumerator_get_next(&request->inbound_varbind_enumerator, &vb);
+ if (err == SNMP_VB_ENUMERATOR_ERR_OK) {
+ struct snmp_node_instance node_instance;
+ memset(&node_instance, 0, sizeof(node_instance));
+ request->error_status = snmp_get_node_instance_from_oid(vb.oid.id, vb.oid.len, &node_instance);
+ if (request->error_status == SNMP_ERR_NOERROR) {
+ if (node_instance.set_value(&node_instance, vb.value_len, vb.value) != SNMP_ERR_NOERROR) {
+ if (request->inbound_varbind_enumerator.varbind_count == 1) {
+ request->error_status = SNMP_ERR_COMMITFAILED;
+ } else {
+ /* we cannot undo the set operations done so far */
+ request->error_status = SNMP_ERR_UNDOFAILED;
+ }
+ }
+
+ if (node_instance.release_instance != NULL) {
+ node_instance.release_instance(&node_instance);
+ }
+ }
+ } else if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) {
+ /* no more varbinds in request */
+ break;
+ } else {
+ /* first time enumerating varbinds work but second time not, although nothing should have changed in between ??? */
+ request->error_status = SNMP_ERR_GENERROR;
+ }
+ }
+ }
+
+ return ERR_OK;
+}
+
+#define PARSE_EXEC(code, retValue) \
+ if ((code) != ERR_OK) { \
+ LWIP_DEBUGF(SNMP_DEBUG, ("Malformed ASN.1 detected.\n")); \
+ snmp_stats.inasnparseerrs++; \
+ return retValue; \
+ }
+
+#define PARSE_ASSERT(cond, retValue) \
+ if (!(cond)) { \
+ LWIP_DEBUGF(SNMP_DEBUG, ("SNMP parse assertion failed!: " # cond)); \
+ snmp_stats.inasnparseerrs++; \
+ return retValue; \
+ }
+
+#define BUILD_EXEC(code, retValue) \
+ if ((code) != ERR_OK) { \
+ LWIP_DEBUGF(SNMP_DEBUG, ("SNMP error during creation of outbound frame!: " # code)); \
+ return retValue; \
+ }
+
+#define IF_PARSE_EXEC(code) PARSE_EXEC(code, ERR_ARG)
+#define IF_PARSE_ASSERT(code) PARSE_ASSERT(code, ERR_ARG)
+
+/**
+ * Checks and decodes incoming SNMP message header, logs header errors.
+ *
+ * @param request points to the current message request state return
+ * @return
+ * - ERR_OK SNMP header is sane and accepted
+ * - ERR_VAL SNMP header is either malformed or rejected
+ */
+static err_t
+snmp_parse_inbound_frame(struct snmp_request *request)
+{
+ struct snmp_pbuf_stream pbuf_stream;
+ struct snmp_asn1_tlv tlv;
+ s32_t parent_tlv_value_len;
+ s32_t s32_value;
+ err_t err;
+#if LWIP_SNMP_V3
+ snmpv3_auth_algo_t auth;
+ snmpv3_priv_algo_t priv;
+#endif
+
+ IF_PARSE_EXEC(snmp_pbuf_stream_init(&pbuf_stream, request->inbound_pbuf, 0, request->inbound_pbuf->tot_len));
+
+ /* decode main container consisting of version, community and PDU */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT((tlv.type == SNMP_ASN1_TYPE_SEQUENCE) && (tlv.value_len == pbuf_stream.length));
+ parent_tlv_value_len = tlv.value_len;
+
+ /* decode version */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+ IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value));
+
+ if (((s32_value != SNMP_VERSION_1) &&
+ (s32_value != SNMP_VERSION_2c)
+#if LWIP_SNMP_V3
+ && (s32_value != SNMP_VERSION_3)
+#endif
+ )
+#if LWIP_SNMP_CONFIGURE_VERSIONS
+ || (!snmp_version_enabled(s32_value))
+#endif
+ ) {
+ /* unsupported SNMP version */
+ snmp_stats.inbadversions++;
+ return ERR_ARG;
+ }
+ request->version = (u8_t)s32_value;
+
+#if LWIP_SNMP_V3
+ if (request->version == SNMP_VERSION_3) {
+ u16_t u16_value;
+ u16_t inbound_msgAuthenticationParameters_offset;
+
+ /* SNMPv3 doesn't use communities */
+ /* @todo: Differentiate read/write access */
+ strncpy((char *)request->community, snmp_community, SNMP_MAX_COMMUNITY_STR_LEN);
+ request->community[SNMP_MAX_COMMUNITY_STR_LEN] = 0; /* ensure NULL termination (strncpy does NOT guarantee it!) */
+ request->community_strlen = (u16_t)strnlen((char *)request->community, SNMP_MAX_COMMUNITY_STR_LEN);
+
+ /* RFC3414 globalData */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_SEQUENCE);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_HDR_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+ /* decode msgID */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+ IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value));
+ request->msg_id = s32_value;
+
+ /* decode msgMaxSize */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+ IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value));
+ request->msg_max_size = s32_value;
+
+ /* decode msgFlags */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+ IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value));
+ request->msg_flags = (u8_t)s32_value;
+
+ /* decode msgSecurityModel */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+ IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value));
+ request->msg_security_model = s32_value;
+
+ /* RFC3414 msgSecurityParameters
+ * The User-based Security Model defines the contents of the OCTET
+ * STRING as a SEQUENCE.
+ *
+ * We skip the protective dummy OCTET STRING header
+ * to access the SEQUENCE header.
+ */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_HDR_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+ /* msgSecurityParameters SEQUENCE header */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_SEQUENCE);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_HDR_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+ /* decode msgAuthoritativeEngineID */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+ IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->msg_authoritative_engine_id,
+ &u16_value, SNMP_V3_MAX_ENGINE_ID_LENGTH));
+ request->msg_authoritative_engine_id_len = (u8_t)u16_value;
+
+ /* msgAuthoritativeEngineBoots */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+ IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->msg_authoritative_engine_boots));
+
+ /* msgAuthoritativeEngineTime */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+ IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->msg_authoritative_engine_time));
+
+ /* msgUserName */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+ IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->msg_user_name,
+ &u16_value, SNMP_V3_MAX_USER_LENGTH));
+ request->msg_user_name_len = (u8_t)u16_value;
+
+ /* msgAuthenticationParameters */
+ memset(request->msg_authentication_parameters, 0, SNMP_V3_MAX_AUTH_PARAM_LENGTH);
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+ /* Remember position */
+ inbound_msgAuthenticationParameters_offset = pbuf_stream.offset;
+ LWIP_UNUSED_ARG(inbound_msgAuthenticationParameters_offset);
+ /* Read auth parameters */
+ /* IF_PARSE_ASSERT(tlv.value_len <= SNMP_V3_MAX_AUTH_PARAM_LENGTH); */
+ IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->msg_authentication_parameters,
+ &u16_value, tlv.value_len));
+ request->msg_authentication_parameters_len = (u8_t)u16_value;
+
+ /* msgPrivacyParameters */
+ memset(request->msg_privacy_parameters, 0, SNMP_V3_MAX_PRIV_PARAM_LENGTH);
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+ IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->msg_privacy_parameters,
+ &u16_value, SNMP_V3_MAX_PRIV_PARAM_LENGTH));
+ request->msg_privacy_parameters_len = (u8_t)u16_value;
+
+ /* validate securityParameters here (do this after decoding because we don't want to increase other counters for wrong frames)
+ * 1) securityParameters was correctly serialized if we reach here.
+ * 2) securityParameters are already cached.
+ * 3) if msgAuthoritativeEngineID is unknown, zero-length or too long:
+ b) https://tools.ietf.org/html/rfc3414#section-7
+ */
+ {
+ const char *eid;
+ u8_t eid_len;
+
+ snmpv3_get_engine_id(&eid, &eid_len);
+
+ if ((request->msg_authoritative_engine_id_len == 0) ||
+ (request->msg_authoritative_engine_id_len != eid_len) ||
+ (memcmp(eid, request->msg_authoritative_engine_id, eid_len) != 0)) {
+ snmp_stats.unknownengineids++;
+ request->msg_flags = 0; /* noauthnopriv */
+ request->error_status = SNMP_ERR_UNKNOWN_ENGINEID;
+ return ERR_OK;
+ }
+ }
+
+ /* 4) verify username */
+ if (snmpv3_get_user((char *)request->msg_user_name, &auth, NULL, &priv, NULL)) {
+ snmp_stats.unknownusernames++;
+ request->msg_flags = 0; /* noauthnopriv */
+ request->error_status = SNMP_ERR_UNKNOWN_SECURITYNAME;
+ return ERR_OK;
+ }
+
+ /* 5) verify security level */
+ switch (request->msg_flags & (SNMP_V3_AUTH_FLAG | SNMP_V3_PRIV_FLAG)) {
+ case SNMP_V3_NOAUTHNOPRIV:
+ if ((auth != SNMP_V3_AUTH_ALGO_INVAL) || (priv != SNMP_V3_PRIV_ALGO_INVAL)) {
+ /* Invalid security level for user */
+ snmp_stats.unsupportedseclevels++;
+ request->msg_flags = SNMP_V3_NOAUTHNOPRIV;
+ request->error_status = SNMP_ERR_UNSUPPORTED_SECLEVEL;
+ return ERR_OK;
+ }
+ break;
+#if LWIP_SNMP_V3_CRYPTO
+ case SNMP_V3_AUTHNOPRIV:
+ if ((auth == SNMP_V3_AUTH_ALGO_INVAL) || (priv != SNMP_V3_PRIV_ALGO_INVAL)) {
+ /* Invalid security level for user */
+ snmp_stats.unsupportedseclevels++;
+ request->msg_flags = SNMP_V3_NOAUTHNOPRIV;
+ request->error_status = SNMP_ERR_UNSUPPORTED_SECLEVEL;
+ return ERR_OK;
+ }
+ break;
+ case SNMP_V3_AUTHPRIV:
+ if ((auth == SNMP_V3_AUTH_ALGO_INVAL) || (priv == SNMP_V3_PRIV_ALGO_INVAL)) {
+ /* Invalid security level for user */
+ snmp_stats.unsupportedseclevels++;
+ request->msg_flags = SNMP_V3_NOAUTHNOPRIV;
+ request->error_status = SNMP_ERR_UNSUPPORTED_SECLEVEL;
+ return ERR_OK;
+ }
+ break;
+#endif
+ default:
+ snmp_stats.unsupportedseclevels++;
+ request->msg_flags = SNMP_V3_NOAUTHNOPRIV;
+ request->error_status = SNMP_ERR_UNSUPPORTED_SECLEVEL;
+ return ERR_OK;
+ }
+
+ /* 6) if securitylevel specifies authentication, authenticate message. */
+#if LWIP_SNMP_V3_CRYPTO
+ if (request->msg_flags & SNMP_V3_AUTH_FLAG) {
+ const u8_t zero_arr[SNMP_V3_MAX_AUTH_PARAM_LENGTH] = { 0 };
+ u8_t key[20];
+ u8_t hmac[LWIP_MAX(SNMP_V3_SHA_LEN, SNMP_V3_MD5_LEN)];
+ struct snmp_pbuf_stream auth_stream;
+
+ if (request->msg_authentication_parameters_len > SNMP_V3_MAX_AUTH_PARAM_LENGTH) {
+ snmp_stats.wrongdigests++;
+ request->msg_flags = SNMP_V3_NOAUTHNOPRIV;
+ request->error_status = SNMP_ERR_AUTHORIZATIONERROR;
+ return ERR_OK;
+ }
+
+ /* Rewind stream */
+ IF_PARSE_EXEC(snmp_pbuf_stream_init(&auth_stream, request->inbound_pbuf, 0, request->inbound_pbuf->tot_len));
+ IF_PARSE_EXEC(snmp_pbuf_stream_seek_abs(&auth_stream, inbound_msgAuthenticationParameters_offset));
+ /* Set auth parameters to zero for verification */
+ IF_PARSE_EXEC(snmp_asn1_enc_raw(&auth_stream, zero_arr, request->msg_authentication_parameters_len));
+
+ /* Verify authentication */
+ IF_PARSE_EXEC(snmp_pbuf_stream_init(&auth_stream, request->inbound_pbuf, 0, request->inbound_pbuf->tot_len));
+
+ IF_PARSE_EXEC(snmpv3_get_user((char *)request->msg_user_name, &auth, key, NULL, NULL));
+ IF_PARSE_EXEC(snmpv3_auth(&auth_stream, request->inbound_pbuf->tot_len, key, auth, hmac));
+
+ if (memcmp(request->msg_authentication_parameters, hmac, SNMP_V3_MAX_AUTH_PARAM_LENGTH)) {
+ snmp_stats.wrongdigests++;
+ request->msg_flags = SNMP_V3_NOAUTHNOPRIV;
+ request->error_status = SNMP_ERR_AUTHORIZATIONERROR;
+ return ERR_OK;
+ }
+
+ /* 7) if securitylevel specifies authentication, verify engineboots, enginetime and lastenginetime */
+ {
+ s32_t boots = snmpv3_get_engine_boots_internal();
+ if ((request->msg_authoritative_engine_boots != boots) || (boots == 2147483647UL)) {
+ snmp_stats.notintimewindows++;
+ request->msg_flags = SNMP_V3_AUTHNOPRIV;
+ request->error_status = SNMP_ERR_NOTINTIMEWINDOW;
+ return ERR_OK;
+ }
+ }
+ {
+ s32_t time = snmpv3_get_engine_time_internal();
+ if (request->msg_authoritative_engine_time > (time + 150)) {
+ snmp_stats.notintimewindows++;
+ request->msg_flags = SNMP_V3_AUTHNOPRIV;
+ request->error_status = SNMP_ERR_NOTINTIMEWINDOW;
+ return ERR_OK;
+ } else if (time > 150) {
+ if (request->msg_authoritative_engine_time < (time - 150)) {
+ snmp_stats.notintimewindows++;
+ request->msg_flags = SNMP_V3_AUTHNOPRIV;
+ request->error_status = SNMP_ERR_NOTINTIMEWINDOW;
+ return ERR_OK;
+ }
+ }
+ }
+ }
+#endif
+
+ /* 8) if securitylevel specifies privacy, decrypt message. */
+#if LWIP_SNMP_V3_CRYPTO
+ if (request->msg_flags & SNMP_V3_PRIV_FLAG) {
+ /* Decrypt message */
+
+ u8_t key[20];
+
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_HDR_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+ IF_PARSE_EXEC(snmpv3_get_user((char *)request->msg_user_name, NULL, NULL, &priv, key));
+ if (snmpv3_crypt(&pbuf_stream, tlv.value_len, key,
+ request->msg_privacy_parameters, request->msg_authoritative_engine_boots,
+ request->msg_authoritative_engine_time, priv, SNMP_V3_PRIV_MODE_DECRYPT) != ERR_OK) {
+ snmp_stats.decryptionerrors++;
+ request->msg_flags = SNMP_V3_AUTHNOPRIV;
+ request->error_status = SNMP_ERR_DECRYIPTION_ERROR;
+ return ERR_OK;
+ }
+ }
+#endif
+ /* 9) calculate max size of scoped pdu?
+ * 10) securityname for user is retrieved from usertable?
+ * 11) security data is cached?
+ * 12)
+ */
+
+ /* Scoped PDU
+ * Encryption context
+ */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_SEQUENCE);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_HDR_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+ /* contextEngineID */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+ IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->context_engine_id,
+ &u16_value, SNMP_V3_MAX_ENGINE_ID_LENGTH));
+ request->context_engine_id_len = (u8_t)u16_value;
+ /* TODO: do we need to verify this contextengineid too? */
+
+ /* contextName */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+ IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->context_name,
+ &u16_value, SNMP_V3_MAX_ENGINE_ID_LENGTH));
+ request->context_name_len = (u8_t)u16_value;
+ /* TODO: do we need to verify this contextname too? */
+ } else
+#endif
+ {
+ /* decode community */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+ err = snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->community, &request->community_strlen, SNMP_MAX_COMMUNITY_STR_LEN);
+ if (err == ERR_MEM) {
+ /* community string does not fit in our buffer -> its too long -> its invalid */
+ request->community_strlen = 0;
+ snmp_pbuf_stream_seek(&pbuf_stream, tlv.value_len);
+ } else {
+ IF_PARSE_ASSERT(err == ERR_OK);
+ }
+ /* add zero terminator */
+ request->community[request->community_strlen] = 0;
+ }
+
+ /* decode PDU type (next container level) */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.value_len <= pbuf_stream.length);
+ request->inbound_padding_len = pbuf_stream.length - tlv.value_len;
+ parent_tlv_value_len = tlv.value_len;
+
+ /* validate PDU type */
+ switch (tlv.type) {
+ case (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_REQ):
+ /* GetRequest PDU */
+ snmp_stats.ingetrequests++;
+ break;
+ case (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_NEXT_REQ):
+ /* GetNextRequest PDU */
+ snmp_stats.ingetnexts++;
+ break;
+ case (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ):
+ /* GetBulkRequest PDU */
+ if (request->version < SNMP_VERSION_2c) {
+ /* RFC2089: invalid, drop packet */
+ return ERR_ARG;
+ }
+ break;
+ case (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_SET_REQ):
+ /* SetRequest PDU */
+ snmp_stats.insetrequests++;
+ break;
+ default:
+ /* unsupported input PDU for this agent (no parse error) */
+ LWIP_DEBUGF(SNMP_DEBUG, ("Unknown/Invalid SNMP PDU type received: %d", tlv.type)); \
+ return ERR_ARG;
+ }
+ request->request_type = tlv.type & SNMP_ASN1_DATATYPE_MASK;
+ request->request_out_type = (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_RESP);
+
+ /* validate community (do this after decoding PDU type because we don't want to increase 'inbadcommunitynames' for wrong frame types */
+ if (request->community_strlen == 0) {
+ /* community string was too long or really empty*/
+ snmp_stats.inbadcommunitynames++;
+ snmp_authfail_trap();
+ return ERR_ARG;
+ } else if (request->request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ) {
+ if (snmp_community_write[0] == 0) {
+ /* our write community is empty, that means all our objects are readonly */
+ request->error_status = SNMP_ERR_NOTWRITABLE;
+ request->error_index = 1;
+ } else if (strncmp(snmp_community_write, (const char *)request->community, SNMP_MAX_COMMUNITY_STR_LEN) != 0) {
+ /* community name does not match */
+ snmp_stats.inbadcommunitynames++;
+ snmp_authfail_trap();
+ return ERR_ARG;
+ }
+ } else {
+ if (strncmp(snmp_community, (const char *)request->community, SNMP_MAX_COMMUNITY_STR_LEN) != 0) {
+ /* community name does not match */
+ snmp_stats.inbadcommunitynames++;
+ snmp_authfail_trap();
+ return ERR_ARG;
+ }
+ }
+
+ /* decode request ID */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+ IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->request_id));
+
+ /* decode error status / non-repeaters */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+ if (request->request_type == SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ) {
+ IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->non_repeaters));
+ if (request->non_repeaters < 0) {
+ /* RFC 1905, 4.2.3 */
+ request->non_repeaters = 0;
+ }
+ } else {
+ /* only check valid value, don't touch 'request->error_status', maybe a response error status was already set to above; */
+ IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value));
+ IF_PARSE_ASSERT(s32_value == SNMP_ERR_NOERROR);
+ }
+
+ /* decode error index / max-repetitions */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+ if (request->request_type == SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ) {
+ IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->max_repetitions));
+ if (request->max_repetitions < 0) {
+ /* RFC 1905, 4.2.3 */
+ request->max_repetitions = 0;
+ }
+ } else {
+ IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->error_index));
+ IF_PARSE_ASSERT(s32_value == 0);
+ }
+
+ /* decode varbind-list type (next container level) */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT((tlv.type == SNMP_ASN1_TYPE_SEQUENCE) && (tlv.value_len <= pbuf_stream.length));
+
+ request->inbound_varbind_offset = pbuf_stream.offset;
+ request->inbound_varbind_len = pbuf_stream.length - request->inbound_padding_len;
+ snmp_vb_enumerator_init(&(request->inbound_varbind_enumerator), request->inbound_pbuf, request->inbound_varbind_offset, request->inbound_varbind_len);
+
+ return ERR_OK;
+}
+
+#define OF_BUILD_EXEC(code) BUILD_EXEC(code, ERR_ARG)
+
+static err_t
+snmp_prepare_outbound_frame(struct snmp_request *request)
+{
+ struct snmp_asn1_tlv tlv;
+ struct snmp_pbuf_stream *pbuf_stream = &(request->outbound_pbuf_stream);
+
+ /* try allocating pbuf(s) for maximum response size */
+ request->outbound_pbuf = pbuf_alloc(PBUF_TRANSPORT, 1472, PBUF_RAM);
+ if (request->outbound_pbuf == NULL) {
+ return ERR_MEM;
+ }
+
+ snmp_pbuf_stream_init(pbuf_stream, request->outbound_pbuf, 0, request->outbound_pbuf->tot_len);
+
+ /* 'Message' sequence */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, 0);
+ OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+
+ /* version */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
+ snmp_asn1_enc_s32t_cnt(request->version, &tlv.value_len);
+ OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+ OF_BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->version) );
+
+#if LWIP_SNMP_V3
+ if (request->version < SNMP_VERSION_3) {
+#endif
+ /* community */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, request->community_strlen);
+ OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+ OF_BUILD_EXEC( snmp_asn1_enc_raw(pbuf_stream, request->community, request->community_strlen) );
+#if LWIP_SNMP_V3
+ } else {
+ const char *id;
+
+ /* globalData */
+ request->outbound_msg_global_data_offset = pbuf_stream->offset;
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 1, 0);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+
+ /* msgID */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 1);
+ snmp_asn1_enc_s32t_cnt(request->msg_id, &tlv.value_len);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+ OF_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->msg_id));
+
+ /* msgMaxSize */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 1);
+ snmp_asn1_enc_s32t_cnt(request->msg_max_size, &tlv.value_len);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+ OF_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->msg_max_size));
+
+ /* msgFlags */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, 1);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+ OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, &request->msg_flags, 1));
+
+ /* msgSecurityModel */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 1);
+ snmp_asn1_enc_s32t_cnt(request->msg_security_model, &tlv.value_len);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+ OF_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->msg_security_model));
+
+ /* end of msgGlobalData */
+ request->outbound_msg_global_data_end = pbuf_stream->offset;
+
+ /* msgSecurityParameters */
+ request->outbound_msg_security_parameters_str_offset = pbuf_stream->offset;
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 1, 0);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+
+ request->outbound_msg_security_parameters_seq_offset = pbuf_stream->offset;
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 1, 0);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+
+ /* msgAuthoritativeEngineID */
+ snmpv3_get_engine_id(&id, &request->msg_authoritative_engine_id_len);
+ MEMCPY(request->msg_authoritative_engine_id, id, request->msg_authoritative_engine_id_len);
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, request->msg_authoritative_engine_id_len);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+ OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->msg_authoritative_engine_id, request->msg_authoritative_engine_id_len));
+
+ request->msg_authoritative_engine_time = snmpv3_get_engine_time();
+ request->msg_authoritative_engine_boots = snmpv3_get_engine_boots();
+
+ /* msgAuthoritativeEngineBoots */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
+ snmp_asn1_enc_s32t_cnt(request->msg_authoritative_engine_boots, &tlv.value_len);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+ OF_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->msg_authoritative_engine_boots));
+
+ /* msgAuthoritativeEngineTime */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
+ snmp_asn1_enc_s32t_cnt(request->msg_authoritative_engine_time, &tlv.value_len);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+ OF_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->msg_authoritative_engine_time));
+
+ /* msgUserName */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, request->msg_user_name_len);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+ OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->msg_user_name, request->msg_user_name_len));
+
+#if LWIP_SNMP_V3_CRYPTO
+ /* msgAuthenticationParameters */
+ if (request->msg_flags & SNMP_V3_AUTH_FLAG) {
+ memset(request->msg_authentication_parameters, 0, SNMP_V3_MAX_AUTH_PARAM_LENGTH);
+ request->outbound_msg_authentication_parameters_offset = pbuf_stream->offset;
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 1, SNMP_V3_MAX_AUTH_PARAM_LENGTH);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+ OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->msg_authentication_parameters, SNMP_V3_MAX_AUTH_PARAM_LENGTH));
+ } else
+#endif
+ {
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, 0);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+ }
+
+#if LWIP_SNMP_V3_CRYPTO
+ /* msgPrivacyParameters */
+ if (request->msg_flags & SNMP_V3_PRIV_FLAG) {
+ snmpv3_build_priv_param(request->msg_privacy_parameters);
+
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, SNMP_V3_MAX_PRIV_PARAM_LENGTH);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+ OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->msg_privacy_parameters, SNMP_V3_MAX_PRIV_PARAM_LENGTH));
+ } else
+#endif
+ {
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, 0);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+ }
+
+ /* End of msgSecurityParameters, so we can calculate the length of this sequence later */
+ request->outbound_msg_security_parameters_end = pbuf_stream->offset;
+
+#if LWIP_SNMP_V3_CRYPTO
+ /* For encryption we have to encapsulate the payload in an octet string */
+ if (request->msg_flags & SNMP_V3_PRIV_FLAG) {
+ request->outbound_scoped_pdu_string_offset = pbuf_stream->offset;
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 3, 0);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+ }
+#endif
+ /* Scoped PDU
+ * Encryption context
+ */
+ request->outbound_scoped_pdu_seq_offset = pbuf_stream->offset;
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, 0);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+
+ /* contextEngineID */
+ snmpv3_get_engine_id(&id, &request->context_engine_id_len);
+ MEMCPY(request->context_engine_id, id, request->context_engine_id_len);
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, request->context_engine_id_len);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+ OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->context_engine_id, request->context_engine_id_len));
+
+ /* contextName */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, request->context_name_len);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+ OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->context_name, request->context_name_len));
+ }
+#endif
+
+ /* 'PDU' sequence */
+ request->outbound_pdu_offset = pbuf_stream->offset;
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, request->request_out_type, 3, 0);
+ OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+
+ /* request ID */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
+ snmp_asn1_enc_s32t_cnt(request->request_id, &tlv.value_len);
+ OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+ OF_BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->request_id) );
+
+ /* error status */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 1);
+ OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+ request->outbound_error_status_offset = pbuf_stream->offset;
+ OF_BUILD_EXEC( snmp_pbuf_stream_write(pbuf_stream, 0) );
+
+ /* error index */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 1);
+ OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+ request->outbound_error_index_offset = pbuf_stream->offset;
+ OF_BUILD_EXEC( snmp_pbuf_stream_write(pbuf_stream, 0) );
+
+ /* 'VarBindList' sequence */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, 0);
+ OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+
+ request->outbound_varbind_offset = pbuf_stream->offset;
+
+ return ERR_OK;
+}
+
+/** Calculate the length of a varbind list */
+err_t
+snmp_varbind_length(struct snmp_varbind *varbind, struct snmp_varbind_len *len)
+{
+ /* calculate required lengths */
+ snmp_asn1_enc_oid_cnt(varbind->oid.id, varbind->oid.len, &len->oid_value_len);
+ snmp_asn1_enc_length_cnt(len->oid_value_len, &len->oid_len_len);
+
+ if (varbind->value_len == 0) {
+ len->value_value_len = 0;
+ } else if (varbind->value_len & SNMP_GET_VALUE_RAW_DATA) {
+ len->value_value_len = varbind->value_len & (~SNMP_GET_VALUE_RAW_DATA);
+ } else {
+ switch (varbind->type) {
+ case SNMP_ASN1_TYPE_INTEGER:
+ if (varbind->value_len != sizeof (s32_t)) {
+ return ERR_VAL;
+ }
+ snmp_asn1_enc_s32t_cnt(*((s32_t *) varbind->value), &len->value_value_len);
+ break;
+ case SNMP_ASN1_TYPE_COUNTER:
+ case SNMP_ASN1_TYPE_GAUGE:
+ case SNMP_ASN1_TYPE_TIMETICKS:
+ if (varbind->value_len != sizeof (u32_t)) {
+ return ERR_VAL;
+ }
+ snmp_asn1_enc_u32t_cnt(*((u32_t *) varbind->value), &len->value_value_len);
+ break;
+ case SNMP_ASN1_TYPE_OCTET_STRING:
+ case SNMP_ASN1_TYPE_IPADDR:
+ case SNMP_ASN1_TYPE_OPAQUE:
+ len->value_value_len = varbind->value_len;
+ break;
+ case SNMP_ASN1_TYPE_NULL:
+ if (varbind->value_len != 0) {
+ return ERR_VAL;
+ }
+ len->value_value_len = 0;
+ break;
+ case SNMP_ASN1_TYPE_OBJECT_ID:
+ if ((varbind->value_len & 0x03) != 0) {
+ return ERR_VAL;
+ }
+ snmp_asn1_enc_oid_cnt((u32_t *) varbind->value, varbind->value_len >> 2, &len->value_value_len);
+ break;
+#if LWIP_HAVE_INT64
+ case SNMP_ASN1_TYPE_COUNTER64:
+ if (varbind->value_len != sizeof(u64_t)) {
+ return ERR_VAL;
+ }
+ snmp_asn1_enc_u64t_cnt(*(u64_t *)varbind->value, &len->value_value_len);
+ break;
+#endif
+ default:
+ /* unsupported type */
+ return ERR_VAL;
+ }
+ }
+ snmp_asn1_enc_length_cnt(len->value_value_len, &len->value_len_len);
+
+ len->vb_value_len = 1 + len->oid_len_len + len->oid_value_len + 1 + len->value_len_len + len->value_value_len;
+ snmp_asn1_enc_length_cnt(len->vb_value_len, &len->vb_len_len);
+
+ return ERR_OK;
+}
+
+#define OVB_BUILD_EXEC(code) BUILD_EXEC(code, ERR_ARG)
+
+err_t
+snmp_append_outbound_varbind(struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind *varbind)
+{
+ struct snmp_asn1_tlv tlv;
+ struct snmp_varbind_len len;
+ err_t err;
+
+ err = snmp_varbind_length(varbind, &len);
+
+ if (err != ERR_OK) {
+ return err;
+ }
+
+ /* check length already before adding first data because in case of GetBulk,
+ * data added so far is returned and therefore no partial data shall be added
+ */
+ if ((1 + len.vb_len_len + len.vb_value_len) > pbuf_stream->length) {
+ return ERR_BUF;
+ }
+
+ /* 'VarBind' sequence */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, len.vb_len_len, len.vb_value_len);
+ OVB_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+
+ /* VarBind OID */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OBJECT_ID, len.oid_len_len, len.oid_value_len);
+ OVB_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+ OVB_BUILD_EXEC(snmp_asn1_enc_oid(pbuf_stream, varbind->oid.id, varbind->oid.len));
+
+ /* VarBind value */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, varbind->type, len.value_len_len, len.value_value_len);
+ OVB_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+
+ if (len.value_value_len > 0) {
+ if (varbind->value_len & SNMP_GET_VALUE_RAW_DATA) {
+ OVB_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, (u8_t *) varbind->value, len.value_value_len));
+ } else {
+ switch (varbind->type) {
+ case SNMP_ASN1_TYPE_INTEGER:
+ OVB_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, len.value_value_len, *((s32_t *) varbind->value)));
+ break;
+ case SNMP_ASN1_TYPE_COUNTER:
+ case SNMP_ASN1_TYPE_GAUGE:
+ case SNMP_ASN1_TYPE_TIMETICKS:
+ OVB_BUILD_EXEC(snmp_asn1_enc_u32t(pbuf_stream, len.value_value_len, *((u32_t *) varbind->value)));
+ break;
+ case SNMP_ASN1_TYPE_OCTET_STRING:
+ case SNMP_ASN1_TYPE_IPADDR:
+ case SNMP_ASN1_TYPE_OPAQUE:
+ OVB_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, (u8_t *) varbind->value, len.value_value_len));
+ len.value_value_len = varbind->value_len;
+ break;
+ case SNMP_ASN1_TYPE_OBJECT_ID:
+ OVB_BUILD_EXEC(snmp_asn1_enc_oid(pbuf_stream, (u32_t *) varbind->value, varbind->value_len / sizeof (u32_t)));
+ break;
+#if LWIP_HAVE_INT64
+ case SNMP_ASN1_TYPE_COUNTER64:
+ OVB_BUILD_EXEC(snmp_asn1_enc_u64t(pbuf_stream, len.value_value_len, *(u64_t *) varbind->value));
+ break;
+#endif
+ default:
+ LWIP_ASSERT("Unknown variable type", 0);
+ break;
+ }
+ }
+ }
+
+ return ERR_OK;
+}
+
+static err_t
+snmp_complete_outbound_frame(struct snmp_request *request)
+{
+ struct snmp_asn1_tlv tlv;
+ u16_t frame_size;
+ u8_t outbound_padding = 0;
+
+ if (request->version == SNMP_VERSION_1) {
+ if (request->error_status != SNMP_ERR_NOERROR) {
+ /* map v2c error codes to v1 compliant error code (according to RFC 2089) */
+ switch (request->error_status) {
+ /* mapping of implementation specific "virtual" error codes
+ * (during processing of frame we already stored them in error_status field,
+ * so no need to check all varbinds here for those exceptions as suggested by RFC) */
+ case SNMP_ERR_NOSUCHINSTANCE:
+ case SNMP_ERR_NOSUCHOBJECT:
+ case SNMP_ERR_ENDOFMIBVIEW:
+ request->error_status = SNMP_ERR_NOSUCHNAME;
+ break;
+ /* mapping according to RFC */
+ case SNMP_ERR_WRONGVALUE:
+ case SNMP_ERR_WRONGENCODING:
+ case SNMP_ERR_WRONGTYPE:
+ case SNMP_ERR_WRONGLENGTH:
+ case SNMP_ERR_INCONSISTENTVALUE:
+ request->error_status = SNMP_ERR_BADVALUE;
+ break;
+ case SNMP_ERR_NOACCESS:
+ case SNMP_ERR_NOTWRITABLE:
+ case SNMP_ERR_NOCREATION:
+ case SNMP_ERR_INCONSISTENTNAME:
+ case SNMP_ERR_AUTHORIZATIONERROR:
+ request->error_status = SNMP_ERR_NOSUCHNAME;
+ break;
+ case SNMP_ERR_RESOURCEUNAVAILABLE:
+ case SNMP_ERR_COMMITFAILED:
+ case SNMP_ERR_UNDOFAILED:
+ default:
+ request->error_status = SNMP_ERR_GENERROR;
+ break;
+ }
+ }
+ } else {
+ if (request->request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ) {
+ /* map error codes to according to RFC 1905 (4.2.5. The SetRequest-PDU) return 'NotWritable' for unknown OIDs) */
+ switch (request->error_status) {
+ case SNMP_ERR_NOSUCHINSTANCE:
+ case SNMP_ERR_NOSUCHOBJECT:
+ case SNMP_ERR_ENDOFMIBVIEW:
+ request->error_status = SNMP_ERR_NOTWRITABLE;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (request->error_status >= SNMP_VARBIND_EXCEPTION_OFFSET) {
+ /* should never occur because v2 frames store exceptions directly inside varbinds and not as frame error_status */
+ LWIP_DEBUGF(SNMP_DEBUG, ("snmp_complete_outbound_frame() > Found v2 request with varbind exception code stored as error status!\n"));
+ return ERR_ARG;
+ }
+ }
+
+ if ((request->error_status != SNMP_ERR_NOERROR) || (request->request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ)) {
+ /* all inbound vars are returned in response without any modification for error responses and successful set requests*/
+ struct snmp_pbuf_stream inbound_stream;
+ OF_BUILD_EXEC( snmp_pbuf_stream_init(&inbound_stream, request->inbound_pbuf, request->inbound_varbind_offset, request->inbound_varbind_len) );
+ OF_BUILD_EXEC( snmp_pbuf_stream_init(&(request->outbound_pbuf_stream), request->outbound_pbuf, request->outbound_varbind_offset, request->outbound_pbuf->tot_len - request->outbound_varbind_offset) );
+ OF_BUILD_EXEC( snmp_pbuf_stream_writeto(&inbound_stream, &(request->outbound_pbuf_stream), 0) );
+ }
+
+ frame_size = request->outbound_pbuf_stream.offset;
+
+#if LWIP_SNMP_V3 && LWIP_SNMP_V3_CRYPTO
+ /* Calculate padding for encryption */
+ if (request->version == SNMP_VERSION_3 && (request->msg_flags & SNMP_V3_PRIV_FLAG)) {
+ u8_t i;
+ outbound_padding = (8 - (u8_t)((frame_size - request->outbound_scoped_pdu_seq_offset) & 0x07)) & 0x07;
+ for (i = 0; i < outbound_padding; i++) {
+ OF_BUILD_EXEC( snmp_pbuf_stream_write(&request->outbound_pbuf_stream, 0) );
+ }
+ }
+#endif
+
+ /* complete missing length in 'Message' sequence ; 'Message' tlv is located at the beginning (offset 0) */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, frame_size + outbound_padding - 1 - 3); /* - type - length_len(fixed, see snmp_prepare_outbound_frame()) */
+ OF_BUILD_EXEC( snmp_pbuf_stream_init(&(request->outbound_pbuf_stream), request->outbound_pbuf, 0, request->outbound_pbuf->tot_len) );
+ OF_BUILD_EXEC( snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv) );
+
+#if LWIP_SNMP_V3
+ if (request->version == SNMP_VERSION_3) {
+ /* complete missing length in 'globalData' sequence */
+ /* - type - length_len(fixed, see snmp_prepare_outbound_frame()) */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 1, request->outbound_msg_global_data_end
+ - request->outbound_msg_global_data_offset - 1 - 1);
+ OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_msg_global_data_offset));
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv));
+
+ /* complete missing length in 'msgSecurityParameters' sequence */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 1, request->outbound_msg_security_parameters_end
+ - request->outbound_msg_security_parameters_str_offset - 1 - 1);
+ OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_msg_security_parameters_str_offset));
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv));
+
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 1, request->outbound_msg_security_parameters_end
+ - request->outbound_msg_security_parameters_seq_offset - 1 - 1);
+ OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_msg_security_parameters_seq_offset));
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv));
+
+ /* complete missing length in scoped PDU sequence */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, frame_size - request->outbound_scoped_pdu_seq_offset - 1 - 3);
+ OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_scoped_pdu_seq_offset));
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv));
+ }
+#endif
+
+ /* complete missing length in 'PDU' sequence */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, request->request_out_type, 3,
+ frame_size - request->outbound_pdu_offset - 1 - 3); /* - type - length_len(fixed, see snmp_prepare_outbound_frame()) */
+ OF_BUILD_EXEC( snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_pdu_offset) );
+ OF_BUILD_EXEC( snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv) );
+
+ /* process and encode final error status */
+ if (request->error_status != 0) {
+ u16_t len;
+ snmp_asn1_enc_s32t_cnt(request->error_status, &len);
+ if (len != 1) {
+ /* error, we only reserved one byte for it */
+ return ERR_ARG;
+ }
+ OF_BUILD_EXEC( snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_error_status_offset) );
+ OF_BUILD_EXEC( snmp_asn1_enc_s32t(&(request->outbound_pbuf_stream), len, request->error_status) );
+
+ /* for compatibility to v1, log statistics; in v2 (RFC 1907) these statistics are obsoleted */
+ switch (request->error_status) {
+ case SNMP_ERR_TOOBIG:
+ snmp_stats.outtoobigs++;
+ break;
+ case SNMP_ERR_NOSUCHNAME:
+ snmp_stats.outnosuchnames++;
+ break;
+ case SNMP_ERR_BADVALUE:
+ snmp_stats.outbadvalues++;
+ break;
+ case SNMP_ERR_GENERROR:
+ default:
+ snmp_stats.outgenerrs++;
+ break;
+ }
+
+ if (request->error_status == SNMP_ERR_TOOBIG) {
+ request->error_index = 0; /* defined by RFC 1157 */
+ } else if (request->error_index == 0) {
+ /* set index to varbind where error occured (if not already set before, e.g. during GetBulk processing) */
+ request->error_index = request->inbound_varbind_enumerator.varbind_count;
+ }
+ } else {
+ if (request->request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ) {
+ snmp_stats.intotalsetvars += request->inbound_varbind_enumerator.varbind_count;
+ } else {
+ snmp_stats.intotalreqvars += request->inbound_varbind_enumerator.varbind_count;
+ }
+ }
+
+ /* encode final error index*/
+ if (request->error_index != 0) {
+ u16_t len;
+ snmp_asn1_enc_s32t_cnt(request->error_index, &len);
+ if (len != 1) {
+ /* error, we only reserved one byte for it */
+ return ERR_VAL;
+ }
+ OF_BUILD_EXEC( snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_error_index_offset) );
+ OF_BUILD_EXEC( snmp_asn1_enc_s32t(&(request->outbound_pbuf_stream), len, request->error_index) );
+ }
+
+ /* complete missing length in 'VarBindList' sequence ; 'VarBindList' tlv is located directly before varbind offset */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, frame_size - request->outbound_varbind_offset);
+ OF_BUILD_EXEC( snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_varbind_offset - 1 - 3) ); /* - type - length_len(fixed, see snmp_prepare_outbound_frame()) */
+ OF_BUILD_EXEC( snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv) );
+
+ /* Authenticate response */
+#if LWIP_SNMP_V3 && LWIP_SNMP_V3_CRYPTO
+ /* Encrypt response */
+ if (request->version == SNMP_VERSION_3 && (request->msg_flags & SNMP_V3_PRIV_FLAG)) {
+ u8_t key[20];
+ snmpv3_priv_algo_t algo;
+
+ /* complete missing length in PDU sequence */
+ OF_BUILD_EXEC(snmp_pbuf_stream_init(&request->outbound_pbuf_stream, request->outbound_pbuf, 0, request->outbound_pbuf->tot_len));
+ OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_scoped_pdu_string_offset));
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 3, frame_size + outbound_padding
+ - request->outbound_scoped_pdu_string_offset - 1 - 3);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv));
+
+ OF_BUILD_EXEC(snmpv3_get_user((char *)request->msg_user_name, NULL, NULL, &algo, key));
+
+ OF_BUILD_EXEC(snmpv3_crypt(&request->outbound_pbuf_stream, tlv.value_len, key,
+ request->msg_privacy_parameters, request->msg_authoritative_engine_boots,
+ request->msg_authoritative_engine_time, algo, SNMP_V3_PRIV_MODE_ENCRYPT));
+ }
+
+ if (request->version == SNMP_VERSION_3 && (request->msg_flags & SNMP_V3_AUTH_FLAG)) {
+ u8_t key[20];
+ snmpv3_auth_algo_t algo;
+ u8_t hmac[20];
+
+ OF_BUILD_EXEC(snmpv3_get_user((char *)request->msg_user_name, &algo, key, NULL, NULL));
+ OF_BUILD_EXEC(snmp_pbuf_stream_init(&(request->outbound_pbuf_stream),
+ request->outbound_pbuf, 0, request->outbound_pbuf->tot_len));
+ OF_BUILD_EXEC(snmpv3_auth(&request->outbound_pbuf_stream, frame_size + outbound_padding, key, algo, hmac));
+
+ MEMCPY(request->msg_authentication_parameters, hmac, SNMP_V3_MAX_AUTH_PARAM_LENGTH);
+ OF_BUILD_EXEC(snmp_pbuf_stream_init(&request->outbound_pbuf_stream,
+ request->outbound_pbuf, 0, request->outbound_pbuf->tot_len));
+ OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&request->outbound_pbuf_stream,
+ request->outbound_msg_authentication_parameters_offset));
+
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 1, SNMP_V3_MAX_AUTH_PARAM_LENGTH);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(&request->outbound_pbuf_stream, &tlv));
+ OF_BUILD_EXEC(snmp_asn1_enc_raw(&request->outbound_pbuf_stream,
+ request->msg_authentication_parameters, SNMP_V3_MAX_AUTH_PARAM_LENGTH));
+ }
+#endif
+
+ pbuf_realloc(request->outbound_pbuf, frame_size + outbound_padding);
+
+ snmp_stats.outgetresponses++;
+ snmp_stats.outpkts++;
+
+ return ERR_OK;
+}
+
+static void
+snmp_execute_write_callbacks(struct snmp_request *request)
+{
+ struct snmp_varbind_enumerator inbound_varbind_enumerator;
+ struct snmp_varbind vb;
+
+ snmp_vb_enumerator_init(&inbound_varbind_enumerator, request->inbound_pbuf, request->inbound_varbind_offset, request->inbound_varbind_len);
+ vb.value = NULL; /* do NOT decode value (we enumerate outbound buffer here, so all varbinds have values assigned, which we don't need here) */
+
+ while (snmp_vb_enumerator_get_next(&inbound_varbind_enumerator, &vb) == SNMP_VB_ENUMERATOR_ERR_OK) {
+ snmp_write_callback(vb.oid.id, vb.oid.len, snmp_write_callback_arg);
+ }
+}
+
+
+/* ----------------------------------------------------------------------- */
+/* VarBind enumerator methods */
+/* ----------------------------------------------------------------------- */
+
+void
+snmp_vb_enumerator_init(struct snmp_varbind_enumerator *enumerator, struct pbuf *p, u16_t offset, u16_t length)
+{
+ snmp_pbuf_stream_init(&(enumerator->pbuf_stream), p, offset, length);
+ enumerator->varbind_count = 0;
+}
+
+#define VB_PARSE_EXEC(code) PARSE_EXEC(code, SNMP_VB_ENUMERATOR_ERR_ASN1ERROR)
+#define VB_PARSE_ASSERT(code) PARSE_ASSERT(code, SNMP_VB_ENUMERATOR_ERR_ASN1ERROR)
+
+snmp_vb_enumerator_err_t
+snmp_vb_enumerator_get_next(struct snmp_varbind_enumerator *enumerator, struct snmp_varbind *varbind)
+{
+ struct snmp_asn1_tlv tlv;
+ u16_t varbind_len;
+ err_t err;
+
+ if (enumerator->pbuf_stream.length == 0) {
+ return SNMP_VB_ENUMERATOR_ERR_EOVB;
+ }
+ enumerator->varbind_count++;
+
+ /* decode varbind itself (parent container of a varbind) */
+ VB_PARSE_EXEC(snmp_asn1_dec_tlv(&(enumerator->pbuf_stream), &tlv));
+ VB_PARSE_ASSERT((tlv.type == SNMP_ASN1_TYPE_SEQUENCE) && (tlv.value_len <= enumerator->pbuf_stream.length));
+ varbind_len = tlv.value_len;
+
+ /* decode varbind name (object id) */
+ VB_PARSE_EXEC(snmp_asn1_dec_tlv(&(enumerator->pbuf_stream), &tlv));
+ VB_PARSE_ASSERT((tlv.type == SNMP_ASN1_TYPE_OBJECT_ID) && (SNMP_ASN1_TLV_LENGTH(tlv) < varbind_len) && (tlv.value_len < enumerator->pbuf_stream.length));
+
+ VB_PARSE_EXEC(snmp_asn1_dec_oid(&(enumerator->pbuf_stream), tlv.value_len, varbind->oid.id, &(varbind->oid.len), SNMP_MAX_OBJ_ID_LEN));
+ varbind_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+
+ /* decode varbind value (object id) */
+ VB_PARSE_EXEC(snmp_asn1_dec_tlv(&(enumerator->pbuf_stream), &tlv));
+ VB_PARSE_ASSERT((SNMP_ASN1_TLV_LENGTH(tlv) == varbind_len) && (tlv.value_len <= enumerator->pbuf_stream.length));
+ varbind->type = tlv.type;
+
+ /* shall the value be decoded ? */
+ if (varbind->value != NULL) {
+ switch (varbind->type) {
+ case SNMP_ASN1_TYPE_INTEGER:
+ VB_PARSE_EXEC(snmp_asn1_dec_s32t(&(enumerator->pbuf_stream), tlv.value_len, (s32_t *)varbind->value));
+ varbind->value_len = sizeof(s32_t);
+ break;
+ case SNMP_ASN1_TYPE_COUNTER:
+ case SNMP_ASN1_TYPE_GAUGE:
+ case SNMP_ASN1_TYPE_TIMETICKS:
+ VB_PARSE_EXEC(snmp_asn1_dec_u32t(&(enumerator->pbuf_stream), tlv.value_len, (u32_t *)varbind->value));
+ varbind->value_len = sizeof(u32_t);
+ break;
+ case SNMP_ASN1_TYPE_OCTET_STRING:
+ case SNMP_ASN1_TYPE_OPAQUE:
+ err = snmp_asn1_dec_raw(&(enumerator->pbuf_stream), tlv.value_len, (u8_t *)varbind->value, &varbind->value_len, SNMP_MAX_VALUE_SIZE);
+ if (err == ERR_MEM) {
+ return SNMP_VB_ENUMERATOR_ERR_INVALIDLENGTH;
+ }
+ VB_PARSE_ASSERT(err == ERR_OK);
+ break;
+ case SNMP_ASN1_TYPE_NULL:
+ varbind->value_len = 0;
+ break;
+ case SNMP_ASN1_TYPE_OBJECT_ID:
+ /* misuse tlv.length_len as OID_length transporter */
+ err = snmp_asn1_dec_oid(&(enumerator->pbuf_stream), tlv.value_len, (u32_t *)varbind->value, &tlv.length_len, SNMP_MAX_OBJ_ID_LEN);
+ if (err == ERR_MEM) {
+ return SNMP_VB_ENUMERATOR_ERR_INVALIDLENGTH;
+ }
+ VB_PARSE_ASSERT(err == ERR_OK);
+ varbind->value_len = tlv.length_len * sizeof(u32_t);
+ break;
+ case SNMP_ASN1_TYPE_IPADDR:
+ if (tlv.value_len == 4) {
+ /* must be exactly 4 octets! */
+ VB_PARSE_EXEC(snmp_asn1_dec_raw(&(enumerator->pbuf_stream), tlv.value_len, (u8_t *)varbind->value, &varbind->value_len, SNMP_MAX_VALUE_SIZE));
+ } else {
+ VB_PARSE_ASSERT(0);
+ }
+ break;
+#if LWIP_HAVE_INT64
+ case SNMP_ASN1_TYPE_COUNTER64:
+ VB_PARSE_EXEC(snmp_asn1_dec_u64t(&(enumerator->pbuf_stream), tlv.value_len, (u64_t *)varbind->value));
+ varbind->value_len = sizeof(u64_t);
+ break;
+#endif
+ default:
+ VB_PARSE_ASSERT(0);
+ break;
+ }
+ } else {
+ snmp_pbuf_stream_seek(&(enumerator->pbuf_stream), tlv.value_len);
+ varbind->value_len = tlv.value_len;
+ }
+
+ return SNMP_VB_ENUMERATOR_ERR_OK;
+}
+
+#endif /* LWIP_SNMP */
diff --git a/resources/ethernet/LwIP/apps/snmp/snmp_msg.h b/resources/ethernet/LwIP/apps/snmp/snmp_msg.h
new file mode 100644
index 00000000..a3b072f9
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/snmp/snmp_msg.h
@@ -0,0 +1,185 @@
+/**
+ * @file
+ * SNMP Agent message handling structures (internal API, do not use in client code).
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * Copyright (c) 2016 Elias Oenal.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons
+ * Martin Hentschel
+ * Elias Oenal
+ */
+
+#ifndef LWIP_HDR_APPS_SNMP_MSG_H
+#define LWIP_HDR_APPS_SNMP_MSG_H
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP
+
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "snmp_pbuf_stream.h"
+#include "lwip/ip_addr.h"
+#include "lwip/err.h"
+
+#if LWIP_SNMP_V3
+#include "snmpv3_priv.h"
+#endif
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* version defines used in PDU */
+#define SNMP_VERSION_1 0
+#define SNMP_VERSION_2c 1
+#define SNMP_VERSION_3 3
+
+struct snmp_varbind_enumerator {
+ struct snmp_pbuf_stream pbuf_stream;
+ u16_t varbind_count;
+};
+
+typedef enum {
+ SNMP_VB_ENUMERATOR_ERR_OK = 0,
+ SNMP_VB_ENUMERATOR_ERR_EOVB = 1,
+ SNMP_VB_ENUMERATOR_ERR_ASN1ERROR = 2,
+ SNMP_VB_ENUMERATOR_ERR_INVALIDLENGTH = 3
+} snmp_vb_enumerator_err_t;
+
+void snmp_vb_enumerator_init(struct snmp_varbind_enumerator *enumerator, struct pbuf *p, u16_t offset, u16_t length);
+snmp_vb_enumerator_err_t snmp_vb_enumerator_get_next(struct snmp_varbind_enumerator *enumerator, struct snmp_varbind *varbind);
+
+struct snmp_request {
+ /* Communication handle */
+ void *handle;
+ /* source IP address */
+ const ip_addr_t *source_ip;
+ /* source UDP port */
+ u16_t source_port;
+ /* incoming snmp version */
+ u8_t version;
+ /* community name (zero terminated) */
+ u8_t community[SNMP_MAX_COMMUNITY_STR_LEN + 1];
+ /* community string length (exclusive zero term) */
+ u16_t community_strlen;
+ /* request type */
+ u8_t request_type;
+ /* request ID */
+ s32_t request_id;
+ /* error status */
+ s32_t error_status;
+ /* error index */
+ s32_t error_index;
+ /* non-repeaters (getBulkRequest (SNMPv2c)) */
+ s32_t non_repeaters;
+ /* max-repetitions (getBulkRequest (SNMPv2c)) */
+ s32_t max_repetitions;
+
+ /* Usually response-pdu (2). When snmpv3 errors are detected report-pdu(8) */
+ u8_t request_out_type;
+
+#if LWIP_SNMP_V3
+ s32_t msg_id;
+ s32_t msg_max_size;
+ u8_t msg_flags;
+ s32_t msg_security_model;
+ u8_t msg_authoritative_engine_id[SNMP_V3_MAX_ENGINE_ID_LENGTH];
+ u8_t msg_authoritative_engine_id_len;
+ s32_t msg_authoritative_engine_boots;
+ s32_t msg_authoritative_engine_time;
+ u8_t msg_user_name[SNMP_V3_MAX_USER_LENGTH];
+ u8_t msg_user_name_len;
+ u8_t msg_authentication_parameters[SNMP_V3_MAX_AUTH_PARAM_LENGTH];
+ u8_t msg_authentication_parameters_len;
+ u8_t msg_privacy_parameters[SNMP_V3_MAX_PRIV_PARAM_LENGTH];
+ u8_t msg_privacy_parameters_len;
+ u8_t context_engine_id[SNMP_V3_MAX_ENGINE_ID_LENGTH];
+ u8_t context_engine_id_len;
+ u8_t context_name[SNMP_V3_MAX_ENGINE_ID_LENGTH];
+ u8_t context_name_len;
+#endif
+
+ struct pbuf *inbound_pbuf;
+ struct snmp_varbind_enumerator inbound_varbind_enumerator;
+ u16_t inbound_varbind_offset;
+ u16_t inbound_varbind_len;
+ u16_t inbound_padding_len;
+
+ struct pbuf *outbound_pbuf;
+ struct snmp_pbuf_stream outbound_pbuf_stream;
+ u16_t outbound_pdu_offset;
+ u16_t outbound_error_status_offset;
+ u16_t outbound_error_index_offset;
+ u16_t outbound_varbind_offset;
+#if LWIP_SNMP_V3
+ u16_t outbound_msg_global_data_offset;
+ u16_t outbound_msg_global_data_end;
+ u16_t outbound_msg_security_parameters_str_offset;
+ u16_t outbound_msg_security_parameters_seq_offset;
+ u16_t outbound_msg_security_parameters_end;
+ u16_t outbound_msg_authentication_parameters_offset;
+ u16_t outbound_scoped_pdu_seq_offset;
+ u16_t outbound_scoped_pdu_string_offset;
+#endif
+
+ u8_t value_buffer[SNMP_MAX_VALUE_SIZE];
+};
+
+/** A helper struct keeping length information about varbinds */
+struct snmp_varbind_len {
+ u8_t vb_len_len;
+ u16_t vb_value_len;
+ u8_t oid_len_len;
+ u16_t oid_value_len;
+ u8_t value_len_len;
+ u16_t value_value_len;
+};
+
+/** Agent community string */
+extern const char *snmp_community;
+/** Agent community string for write access */
+extern const char *snmp_community_write;
+/** handle for sending traps */
+extern void *snmp_traps_handle;
+
+void snmp_receive(void *handle, struct pbuf *p, const ip_addr_t *source_ip, u16_t port);
+err_t snmp_sendto(void *handle, struct pbuf *p, const ip_addr_t *dst, u16_t port);
+u8_t snmp_get_local_ip_for_dst(void *handle, const ip_addr_t *dst, ip_addr_t *result);
+err_t snmp_varbind_length(struct snmp_varbind *varbind, struct snmp_varbind_len *len);
+err_t snmp_append_outbound_varbind(struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind *varbind);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_SNMP */
+
+#endif /* LWIP_HDR_APPS_SNMP_MSG_H */
diff --git a/resources/ethernet/LwIP/apps/snmp/snmp_netconn.c b/resources/ethernet/LwIP/apps/snmp/snmp_netconn.c
new file mode 100644
index 00000000..57d98f6d
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/snmp/snmp_netconn.c
@@ -0,0 +1,123 @@
+/**
+ * @file
+ * SNMP netconn frontend.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Dirk Ziegelmeier
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP && SNMP_USE_NETCONN
+
+#include
+#include "lwip/api.h"
+#include "lwip/ip.h"
+#include "lwip/udp.h"
+#include "snmp_msg.h"
+#include "lwip/sys.h"
+#include "lwip/prot/iana.h"
+
+/** SNMP netconn API worker thread */
+static void
+snmp_netconn_thread(void *arg)
+{
+ struct netconn *conn;
+ struct netbuf *buf;
+ err_t err;
+ LWIP_UNUSED_ARG(arg);
+
+ /* Bind to SNMP port with default IP address */
+#if LWIP_IPV6
+ conn = netconn_new(NETCONN_UDP_IPV6);
+ netconn_bind(conn, IP6_ADDR_ANY, LWIP_IANA_PORT_SNMP);
+#else /* LWIP_IPV6 */
+ conn = netconn_new(NETCONN_UDP);
+ netconn_bind(conn, IP4_ADDR_ANY, LWIP_IANA_PORT_SNMP);
+#endif /* LWIP_IPV6 */
+ LWIP_ERROR("snmp_netconn: invalid conn", (conn != NULL), return;);
+
+ snmp_traps_handle = conn;
+
+ do {
+ err = netconn_recv(conn, &buf);
+
+ if (err == ERR_OK) {
+ snmp_receive(conn, buf->p, &buf->addr, buf->port);
+ }
+
+ if (buf != NULL) {
+ netbuf_delete(buf);
+ }
+ } while (1);
+}
+
+err_t
+snmp_sendto(void *handle, struct pbuf *p, const ip_addr_t *dst, u16_t port)
+{
+ err_t result;
+ struct netbuf buf;
+
+ memset(&buf, 0, sizeof(buf));
+ buf.p = p;
+ result = netconn_sendto((struct netconn *)handle, &buf, dst, port);
+
+ return result;
+}
+
+u8_t
+snmp_get_local_ip_for_dst(void *handle, const ip_addr_t *dst, ip_addr_t *result)
+{
+ struct netconn *conn = (struct netconn *)handle;
+ struct netif *dst_if;
+ const ip_addr_t *dst_ip;
+
+ LWIP_UNUSED_ARG(conn); /* unused in case of IPV4 only configuration */
+
+ ip_route_get_local_ip(&conn->pcb.udp->local_ip, dst, dst_if, dst_ip);
+
+ if ((dst_if != NULL) && (dst_ip != NULL)) {
+ ip_addr_copy(*result, *dst_ip);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/**
+ * Starts SNMP Agent.
+ */
+void
+snmp_init(void)
+{
+ LWIP_ASSERT_CORE_LOCKED();
+ sys_thread_new("snmp_netconn", snmp_netconn_thread, NULL, SNMP_STACK_SIZE, SNMP_THREAD_PRIO);
+}
+
+#endif /* LWIP_SNMP && SNMP_USE_NETCONN */
diff --git a/resources/ethernet/LwIP/apps/snmp/snmp_pbuf_stream.c b/resources/ethernet/LwIP/apps/snmp/snmp_pbuf_stream.c
new file mode 100644
index 00000000..e33c6ec9
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/snmp/snmp_pbuf_stream.c
@@ -0,0 +1,156 @@
+/**
+ * @file
+ * SNMP pbuf stream wrapper implementation (internal API, do not use in client code).
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Martin Hentschel
+ *
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "snmp_pbuf_stream.h"
+#include "lwip/def.h"
+#include
+
+err_t
+snmp_pbuf_stream_init(struct snmp_pbuf_stream *pbuf_stream, struct pbuf *p, u16_t offset, u16_t length)
+{
+ pbuf_stream->offset = offset;
+ pbuf_stream->length = length;
+ pbuf_stream->pbuf = p;
+
+ return ERR_OK;
+}
+
+err_t
+snmp_pbuf_stream_read(struct snmp_pbuf_stream *pbuf_stream, u8_t *data)
+{
+ if (pbuf_stream->length == 0) {
+ return ERR_BUF;
+ }
+
+ if (pbuf_copy_partial(pbuf_stream->pbuf, data, 1, pbuf_stream->offset) == 0) {
+ return ERR_BUF;
+ }
+
+ pbuf_stream->offset++;
+ pbuf_stream->length--;
+
+ return ERR_OK;
+}
+
+err_t
+snmp_pbuf_stream_write(struct snmp_pbuf_stream *pbuf_stream, u8_t data)
+{
+ return snmp_pbuf_stream_writebuf(pbuf_stream, &data, 1);
+}
+
+err_t
+snmp_pbuf_stream_writebuf(struct snmp_pbuf_stream *pbuf_stream, const void *buf, u16_t buf_len)
+{
+ if (pbuf_stream->length < buf_len) {
+ return ERR_BUF;
+ }
+
+ if (pbuf_take_at(pbuf_stream->pbuf, buf, buf_len, pbuf_stream->offset) != ERR_OK) {
+ return ERR_BUF;
+ }
+
+ pbuf_stream->offset += buf_len;
+ pbuf_stream->length -= buf_len;
+
+ return ERR_OK;
+}
+
+err_t
+snmp_pbuf_stream_writeto(struct snmp_pbuf_stream *pbuf_stream, struct snmp_pbuf_stream *target_pbuf_stream, u16_t len)
+{
+
+ if ((pbuf_stream == NULL) || (target_pbuf_stream == NULL)) {
+ return ERR_ARG;
+ }
+ if ((len > pbuf_stream->length) || (len > target_pbuf_stream->length)) {
+ return ERR_ARG;
+ }
+
+ if (len == 0) {
+ len = LWIP_MIN(pbuf_stream->length, target_pbuf_stream->length);
+ }
+
+ while (len > 0) {
+ u16_t chunk_len;
+ err_t err;
+ u16_t target_offset;
+ struct pbuf *pbuf = pbuf_skip(pbuf_stream->pbuf, pbuf_stream->offset, &target_offset);
+
+ if ((pbuf == NULL) || (pbuf->len == 0)) {
+ return ERR_BUF;
+ }
+
+ chunk_len = LWIP_MIN(len, pbuf->len);
+ err = snmp_pbuf_stream_writebuf(target_pbuf_stream, &((u8_t *)pbuf->payload)[target_offset], chunk_len);
+ if (err != ERR_OK) {
+ return err;
+ }
+
+ pbuf_stream->offset += chunk_len;
+ pbuf_stream->length -= chunk_len;
+ len -= chunk_len;
+ }
+
+ return ERR_OK;
+}
+
+err_t
+snmp_pbuf_stream_seek(struct snmp_pbuf_stream *pbuf_stream, s32_t offset)
+{
+ if ((offset < 0) || (offset > pbuf_stream->length)) {
+ /* we cannot seek backwards or forward behind stream end */
+ return ERR_ARG;
+ }
+
+ pbuf_stream->offset += (u16_t)offset;
+ pbuf_stream->length -= (u16_t)offset;
+
+ return ERR_OK;
+}
+
+err_t
+snmp_pbuf_stream_seek_abs(struct snmp_pbuf_stream *pbuf_stream, u32_t offset)
+{
+ s32_t rel_offset = offset - pbuf_stream->offset;
+ return snmp_pbuf_stream_seek(pbuf_stream, rel_offset);
+}
+
+#endif /* LWIP_SNMP */
diff --git a/resources/ethernet/LwIP/apps/snmp/snmp_pbuf_stream.h b/resources/ethernet/LwIP/apps/snmp/snmp_pbuf_stream.h
new file mode 100644
index 00000000..d094fb1c
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/snmp/snmp_pbuf_stream.h
@@ -0,0 +1,72 @@
+/**
+ * @file
+ * SNMP pbuf stream wrapper (internal API, do not use in client code).
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Martin Hentschel
+ *
+ */
+
+#ifndef LWIP_HDR_APPS_SNMP_PBUF_STREAM_H
+#define LWIP_HDR_APPS_SNMP_PBUF_STREAM_H
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP
+
+#include "lwip/err.h"
+#include "lwip/pbuf.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct snmp_pbuf_stream {
+ struct pbuf *pbuf;
+ u16_t offset;
+ u16_t length;
+};
+
+err_t snmp_pbuf_stream_init(struct snmp_pbuf_stream *pbuf_stream, struct pbuf *p, u16_t offset, u16_t length);
+err_t snmp_pbuf_stream_read(struct snmp_pbuf_stream *pbuf_stream, u8_t *data);
+err_t snmp_pbuf_stream_write(struct snmp_pbuf_stream *pbuf_stream, u8_t data);
+err_t snmp_pbuf_stream_writebuf(struct snmp_pbuf_stream *pbuf_stream, const void *buf, u16_t buf_len);
+err_t snmp_pbuf_stream_writeto(struct snmp_pbuf_stream *pbuf_stream, struct snmp_pbuf_stream *target_pbuf_stream, u16_t len);
+err_t snmp_pbuf_stream_seek(struct snmp_pbuf_stream *pbuf_stream, s32_t offset);
+err_t snmp_pbuf_stream_seek_abs(struct snmp_pbuf_stream *pbuf_stream, u32_t offset);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_SNMP */
+
+#endif /* LWIP_HDR_APPS_SNMP_PBUF_STREAM_H */
diff --git a/resources/ethernet/LwIP/apps/snmp/snmp_raw.c b/resources/ethernet/LwIP/apps/snmp/snmp_raw.c
new file mode 100644
index 00000000..912606fb
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/snmp/snmp_raw.c
@@ -0,0 +1,103 @@
+/**
+ * @file
+ * SNMP RAW API frontend.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Dirk Ziegelmeier
+ */
+
+#include "lwip/apps/snmp_opts.h"
+#include "lwip/ip_addr.h"
+
+#if LWIP_SNMP && SNMP_USE_RAW
+
+#include "lwip/udp.h"
+#include "lwip/ip.h"
+#include "lwip/prot/iana.h"
+#include "snmp_msg.h"
+
+/* lwIP UDP receive callback function */
+static void
+snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
+{
+ LWIP_UNUSED_ARG(arg);
+
+ snmp_receive(pcb, p, addr, port);
+
+ pbuf_free(p);
+}
+
+err_t
+snmp_sendto(void *handle, struct pbuf *p, const ip_addr_t *dst, u16_t port)
+{
+ return udp_sendto((struct udp_pcb *)handle, p, dst, port);
+}
+
+u8_t
+snmp_get_local_ip_for_dst(void *handle, const ip_addr_t *dst, ip_addr_t *result)
+{
+ struct udp_pcb *udp_pcb = (struct udp_pcb *)handle;
+ struct netif *dst_if;
+ const ip_addr_t *dst_ip;
+
+ LWIP_UNUSED_ARG(udp_pcb); /* unused in case of IPV4 only configuration */
+
+ ip_route_get_local_ip(&udp_pcb->local_ip, dst, dst_if, dst_ip);
+
+ if ((dst_if != NULL) && (dst_ip != NULL)) {
+ ip_addr_copy(*result, *dst_ip);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/**
+ * @ingroup snmp_core
+ * Starts SNMP Agent.
+ * Allocates UDP pcb and binds it to IP_ANY_TYPE port 161.
+ */
+void
+snmp_init(void)
+{
+ err_t err;
+
+ struct udp_pcb *snmp_pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
+ LWIP_ERROR("snmp_raw: no PCB", (snmp_pcb != NULL), return;);
+
+ LWIP_ASSERT_CORE_LOCKED();
+
+ snmp_traps_handle = snmp_pcb;
+
+ udp_recv(snmp_pcb, snmp_recv, NULL);
+ err = udp_bind(snmp_pcb, IP_ANY_TYPE, LWIP_IANA_PORT_SNMP);
+ LWIP_ERROR("snmp_raw: Unable to bind PCB", (err == ERR_OK), return;);
+}
+
+#endif /* LWIP_SNMP && SNMP_USE_RAW */
diff --git a/resources/ethernet/LwIP/apps/snmp/snmp_scalar.c b/resources/ethernet/LwIP/apps/snmp/snmp_scalar.c
new file mode 100644
index 00000000..51eaed10
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/snmp/snmp_scalar.c
@@ -0,0 +1,232 @@
+/**
+ * @file
+ * SNMP scalar node support implementation.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Martin Hentschel
+ *
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/snmp_scalar.h"
+#include "lwip/apps/snmp_core.h"
+
+static s16_t snmp_scalar_array_get_value(struct snmp_node_instance *instance, void *value);
+static snmp_err_t snmp_scalar_array_set_test(struct snmp_node_instance *instance, u16_t value_len, void *value);
+static snmp_err_t snmp_scalar_array_set_value(struct snmp_node_instance *instance, u16_t value_len, void *value);
+
+snmp_err_t
+snmp_scalar_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance *instance)
+{
+ const struct snmp_scalar_node *scalar_node = (const struct snmp_scalar_node *)(const void *)instance->node;
+
+ LWIP_UNUSED_ARG(root_oid);
+ LWIP_UNUSED_ARG(root_oid_len);
+
+ /* scalar only has one dedicated instance: .0 */
+ if ((instance->instance_oid.len != 1) || (instance->instance_oid.id[0] != 0)) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ instance->access = scalar_node->access;
+ instance->asn1_type = scalar_node->asn1_type;
+ instance->get_value = scalar_node->get_value;
+ instance->set_test = scalar_node->set_test;
+ instance->set_value = scalar_node->set_value;
+ return SNMP_ERR_NOERROR;
+}
+
+snmp_err_t
+snmp_scalar_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance *instance)
+{
+ /* because our only instance is .0 we can only return a next instance if no instance oid is passed */
+ if (instance->instance_oid.len == 0) {
+ instance->instance_oid.len = 1;
+ instance->instance_oid.id[0] = 0;
+
+ return snmp_scalar_get_instance(root_oid, root_oid_len, instance);
+ }
+
+ return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+
+snmp_err_t
+snmp_scalar_array_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance *instance)
+{
+ LWIP_UNUSED_ARG(root_oid);
+ LWIP_UNUSED_ARG(root_oid_len);
+
+ if ((instance->instance_oid.len == 2) && (instance->instance_oid.id[1] == 0)) {
+ const struct snmp_scalar_array_node *array_node = (const struct snmp_scalar_array_node *)(const void *)instance->node;
+ const struct snmp_scalar_array_node_def *array_node_def = array_node->array_nodes;
+ u32_t i = 0;
+
+ while (i < array_node->array_node_count) {
+ if (array_node_def->oid == instance->instance_oid.id[0]) {
+ break;
+ }
+
+ array_node_def++;
+ i++;
+ }
+
+ if (i < array_node->array_node_count) {
+ instance->access = array_node_def->access;
+ instance->asn1_type = array_node_def->asn1_type;
+ instance->get_value = snmp_scalar_array_get_value;
+ instance->set_test = snmp_scalar_array_set_test;
+ instance->set_value = snmp_scalar_array_set_value;
+ instance->reference.const_ptr = array_node_def;
+
+ return SNMP_ERR_NOERROR;
+ }
+ }
+
+ return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+snmp_err_t
+snmp_scalar_array_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance *instance)
+{
+ const struct snmp_scalar_array_node *array_node = (const struct snmp_scalar_array_node *)(const void *)instance->node;
+ const struct snmp_scalar_array_node_def *array_node_def = array_node->array_nodes;
+ const struct snmp_scalar_array_node_def *result = NULL;
+
+ LWIP_UNUSED_ARG(root_oid);
+ LWIP_UNUSED_ARG(root_oid_len);
+
+ if ((instance->instance_oid.len == 0) && (array_node->array_node_count > 0)) {
+ /* return node with lowest OID */
+ u16_t i = 0;
+
+ result = array_node_def;
+ array_node_def++;
+
+ for (i = 1; i < array_node->array_node_count; i++) {
+ if (array_node_def->oid < result->oid) {
+ result = array_node_def;
+ }
+ array_node_def++;
+ }
+ } else if (instance->instance_oid.len >= 1) {
+ if (instance->instance_oid.len == 1) {
+ /* if we have the requested OID we return its instance, otherwise we search for the next available */
+ u16_t i = 0;
+ while (i < array_node->array_node_count) {
+ if (array_node_def->oid == instance->instance_oid.id[0]) {
+ result = array_node_def;
+ break;
+ }
+
+ array_node_def++;
+ i++;
+ }
+ }
+ if (result == NULL) {
+ u32_t oid_dist = 0xFFFFFFFFUL;
+ u16_t i = 0;
+ array_node_def = array_node->array_nodes; /* may be already at the end when if case before was executed without result -> reinitialize to start */
+ while (i < array_node->array_node_count) {
+ if ((array_node_def->oid > instance->instance_oid.id[0]) &&
+ ((u32_t)(array_node_def->oid - instance->instance_oid.id[0]) < oid_dist)) {
+ result = array_node_def;
+ oid_dist = array_node_def->oid - instance->instance_oid.id[0];
+ }
+
+ array_node_def++;
+ i++;
+ }
+ }
+ }
+
+ if (result == NULL) {
+ /* nothing to return */
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ instance->instance_oid.len = 2;
+ instance->instance_oid.id[0] = result->oid;
+ instance->instance_oid.id[1] = 0;
+
+ instance->access = result->access;
+ instance->asn1_type = result->asn1_type;
+ instance->get_value = snmp_scalar_array_get_value;
+ instance->set_test = snmp_scalar_array_set_test;
+ instance->set_value = snmp_scalar_array_set_value;
+ instance->reference.const_ptr = result;
+
+ return SNMP_ERR_NOERROR;
+}
+
+static s16_t
+snmp_scalar_array_get_value(struct snmp_node_instance *instance, void *value)
+{
+ s16_t result = -1;
+ const struct snmp_scalar_array_node *array_node = (const struct snmp_scalar_array_node *)(const void *)instance->node;
+ const struct snmp_scalar_array_node_def *array_node_def = (const struct snmp_scalar_array_node_def *)instance->reference.const_ptr;
+
+ if (array_node->get_value != NULL) {
+ result = array_node->get_value(array_node_def, value);
+ }
+ return result;
+}
+
+static snmp_err_t
+snmp_scalar_array_set_test(struct snmp_node_instance *instance, u16_t value_len, void *value)
+{
+ snmp_err_t result = SNMP_ERR_NOTWRITABLE;
+ const struct snmp_scalar_array_node *array_node = (const struct snmp_scalar_array_node *)(const void *)instance->node;
+ const struct snmp_scalar_array_node_def *array_node_def = (const struct snmp_scalar_array_node_def *)instance->reference.const_ptr;
+
+ if (array_node->set_test != NULL) {
+ result = array_node->set_test(array_node_def, value_len, value);
+ }
+ return result;
+}
+
+static snmp_err_t
+snmp_scalar_array_set_value(struct snmp_node_instance *instance, u16_t value_len, void *value)
+{
+ snmp_err_t result = SNMP_ERR_NOTWRITABLE;
+ const struct snmp_scalar_array_node *array_node = (const struct snmp_scalar_array_node *)(const void *)instance->node;
+ const struct snmp_scalar_array_node_def *array_node_def = (const struct snmp_scalar_array_node_def *)instance->reference.const_ptr;
+
+ if (array_node->set_value != NULL) {
+ result = array_node->set_value(array_node_def, value_len, value);
+ }
+ return result;
+}
+
+#endif /* LWIP_SNMP */
diff --git a/resources/ethernet/LwIP/apps/snmp/snmp_snmpv2_framework.c b/resources/ethernet/LwIP/apps/snmp/snmp_snmpv2_framework.c
new file mode 100644
index 00000000..a19a26e7
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/snmp/snmp_snmpv2_framework.c
@@ -0,0 +1,90 @@
+/*
+Generated by LwipMibCompiler
+*/
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP && LWIP_SNMP_V3 /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/snmp_snmpv2_framework.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_scalar.h"
+#include "lwip/apps/snmp_table.h"
+#include "lwip/apps/snmpv3.h"
+#include "snmpv3_priv.h"
+
+#include "lwip/sys.h"
+
+#include
+
+const struct snmp_obj_id usmNoAuthProtocol = { 10, { 1, 3, 6, 1, 6, 3, 10, 1, 1, 1 } };
+const struct snmp_obj_id usmHMACMD5AuthProtocol = { 10, { 1, 3, 6, 1, 6, 3, 10, 1, 1, 2 } };
+const struct snmp_obj_id usmHMACSHAAuthProtocol = { 10, { 1, 3, 6, 1, 6, 3, 10, 1, 1, 3 } };
+/* .4 sha-224
+ * .5 sha-256
+ * .6 sha-384
+ * .7 sha-512
+ */
+
+const struct snmp_obj_id usmNoPrivProtocol = { 10, { 1, 3, 6, 1, 6, 3, 10, 1, 2, 1 } };
+const struct snmp_obj_id usmDESPrivProtocol = { 10, { 1, 3, 6, 1, 6, 3, 10, 1, 2, 2 } };
+/* .3 3des-ede */
+const struct snmp_obj_id usmAESPrivProtocol = { 10, { 1, 3, 6, 1, 6, 3, 10, 1, 2, 4 } };
+/* .5 unknown
+ * .6 unknown
+ * .7 unknown
+ */
+
+/* TODO: where should this value come from? */
+#define SNMP_FRAMEWORKMIB_SNMPENGINEMAXMESSAGESIZE 1500
+
+/* --- snmpFrameworkMIBObjects 1.3.6.1.6.3.10.2 ----------------------------------------------------- */
+static s16_t snmpengine_scalars_get_value(const struct snmp_scalar_array_node_def *node, void *value)
+{
+ const char *engineid;
+ u8_t engineid_len;
+
+ switch (node->oid) {
+ case 1: /* snmpEngineID */
+ snmpv3_get_engine_id(&engineid, &engineid_len);
+ MEMCPY(value, engineid, engineid_len);
+ return engineid_len;
+ case 2: /* snmpEngineBoots */
+ *(s32_t *)value = snmpv3_get_engine_boots_internal();
+ return sizeof(s32_t);
+ case 3: /* snmpEngineTime */
+ *(s32_t *)value = snmpv3_get_engine_time_internal();
+ return sizeof(s32_t);
+ case 4: /* snmpEngineMaxMessageSize */
+ *(s32_t *)value = SNMP_FRAMEWORKMIB_SNMPENGINEMAXMESSAGESIZE;
+ return sizeof(s32_t);
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG, ("snmpengine_scalars_get_value(): unknown id: %"S32_F"\n", node->oid));
+ return 0;
+ }
+}
+
+static const struct snmp_scalar_array_node_def snmpengine_scalars_nodes[] = {
+ {1, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpEngineID */
+ {2, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpEngineBoots */
+ {3, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpEngineTime */
+ {4, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpEngineMaxMessageSize */
+};
+static const struct snmp_scalar_array_node snmpengine_scalars = SNMP_SCALAR_CREATE_ARRAY_NODE(1, snmpengine_scalars_nodes, snmpengine_scalars_get_value, NULL, NULL);
+
+static const struct snmp_node *const snmpframeworkmibobjects_subnodes[] = {
+ &snmpengine_scalars.node.node
+};
+static const struct snmp_tree_node snmpframeworkmibobjects_treenode = SNMP_CREATE_TREE_NODE(2, snmpframeworkmibobjects_subnodes);
+
+/* --- snmpFrameworkMIB ----------------------------------------------------- */
+static const struct snmp_node *const snmpframeworkmib_subnodes[] = {
+ &snmpframeworkmibobjects_treenode.node
+};
+static const struct snmp_tree_node snmpframeworkmib_root = SNMP_CREATE_TREE_NODE(10, snmpframeworkmib_subnodes);
+static const u32_t snmpframeworkmib_base_oid[] = {1, 3, 6, 1, 6, 3, 10};
+const struct snmp_mib snmpframeworkmib = {snmpframeworkmib_base_oid, LWIP_ARRAYSIZE(snmpframeworkmib_base_oid), &snmpframeworkmib_root.node};
+
+/* --- snmpFrameworkMIB ----------------------------------------------------- */
+#endif /* LWIP_SNMP */
diff --git a/resources/ethernet/LwIP/apps/snmp/snmp_snmpv2_usm.c b/resources/ethernet/LwIP/apps/snmp/snmp_snmpv2_usm.c
new file mode 100644
index 00000000..940ca754
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/snmp/snmp_snmpv2_usm.c
@@ -0,0 +1,410 @@
+/*
+Generated by LwipMibCompiler
+*/
+
+#include "lwip/apps/snmp_opts.h"
+#if LWIP_SNMP && LWIP_SNMP_V3
+
+#include "lwip/apps/snmp_snmpv2_usm.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_scalar.h"
+#include "lwip/apps/snmp_table.h"
+#include "lwip/apps/snmpv3.h"
+#include "snmpv3_priv.h"
+
+#include "lwip/apps/snmp_snmpv2_framework.h"
+
+#include
+
+/* --- usmUser 1.3.6.1.6.3.15.1.2 ----------------------------------------------------- */
+
+static const struct snmp_oid_range usmUserTable_oid_ranges[] = {
+ { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff },
+ { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff },
+ { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff },
+ { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff },
+ { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff },
+ { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff },
+ { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff },
+ { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff }
+};
+
+static void snmp_engineid_to_oid(const char *engineid, u32_t *oid, u32_t len)
+{
+ u8_t i;
+
+ for (i = 0; i < len; i++) {
+ oid[i] = engineid[i];
+ }
+}
+
+static void snmp_oid_to_name(char *name, const u32_t *oid, size_t len)
+{
+ u8_t i;
+
+ for (i = 0; i < len; i++) {
+ name[i] = (char)oid[i];
+ }
+}
+
+static void snmp_name_to_oid(const char *name, u32_t *oid, size_t len)
+{
+ u8_t i;
+
+ for (i = 0; i < len; i++) {
+ oid[i] = name[i];
+ }
+}
+
+static const struct snmp_obj_id *snmp_auth_algo_to_oid(snmpv3_auth_algo_t algo)
+{
+ if (algo == SNMP_V3_AUTH_ALGO_MD5) {
+ return &usmHMACMD5AuthProtocol;
+ } else if (algo == SNMP_V3_AUTH_ALGO_SHA) {
+ return &usmHMACMD5AuthProtocol;
+ }
+
+ return &usmNoAuthProtocol;
+}
+
+static const struct snmp_obj_id *snmp_priv_algo_to_oid(snmpv3_priv_algo_t algo)
+{
+ if (algo == SNMP_V3_PRIV_ALGO_DES) {
+ return &usmDESPrivProtocol;
+ } else if (algo == SNMP_V3_PRIV_ALGO_AES) {
+ return &usmAESPrivProtocol;
+ }
+
+ return &usmNoPrivProtocol;
+}
+
+char username[32];
+
+static snmp_err_t usmusertable_get_instance(const u32_t *column, const u32_t *row_oid, u8_t row_oid_len, struct snmp_node_instance *cell_instance)
+{
+ const char *engineid;
+ u8_t eid_len;
+
+ u32_t engineid_oid[SNMP_V3_MAX_ENGINE_ID_LENGTH];
+
+ u8_t name_len;
+ u8_t engineid_len;
+
+ u8_t name_start;
+ u8_t engineid_start;
+
+ LWIP_UNUSED_ARG(column);
+
+ snmpv3_get_engine_id(&engineid, &eid_len);
+
+ engineid_len = (u8_t)row_oid[0];
+ engineid_start = 1;
+
+ if (engineid_len != eid_len) {
+ /* EngineID length does not match! */
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ if (engineid_len > row_oid_len) {
+ /* row OID doesn't contain enough data according to engineid_len.*/
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ /* check if incoming OID length and if values are in plausible range */
+ if (!snmp_oid_in_range(&row_oid[engineid_start], engineid_len, usmUserTable_oid_ranges, engineid_len)) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ snmp_engineid_to_oid(engineid, engineid_oid, engineid_len);
+
+ /* Verify EngineID */
+ if (snmp_oid_equal(&row_oid[engineid_start], engineid_len, engineid_oid, engineid_len)) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ name_len = (u8_t)row_oid[engineid_start + engineid_len];
+ name_start = engineid_start + engineid_len + 1;
+
+ if (name_len > SNMP_V3_MAX_USER_LENGTH) {
+ /* specified name is too long */
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ if (1 + engineid_len + 1 + name_len != row_oid_len) {
+ /* Length of EngineID and name does not match row oid length. (+2 for length fields)*/
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ /* check if incoming OID length and if values are in plausible range */
+ if (!snmp_oid_in_range(&row_oid[name_start], name_len, usmUserTable_oid_ranges, name_len)) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ /* Verify if user exists */
+ memset(username, 0, sizeof(username));
+ snmp_oid_to_name(username, &row_oid[name_start], name_len);
+ if (snmpv3_get_user(username, NULL, NULL, NULL, NULL) != ERR_OK) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ /* Save name in reference pointer to make it easier to handle later on */
+ cell_instance->reference.ptr = username;
+ cell_instance->reference_len = name_len;
+
+ /* user was found */
+ return SNMP_ERR_NOERROR;
+}
+
+/*
+ * valid oid options
+ *
+ * .
+ * ..
+ * ..
+ * ...
+ * ....
+ * ....
+ *
+ */
+static snmp_err_t usmusertable_get_next_instance(const u32_t *column, struct snmp_obj_id *row_oid, struct snmp_node_instance *cell_instance)
+{
+ const char *engineid;
+ u8_t eid_len;
+
+ u32_t engineid_oid[SNMP_V3_MAX_ENGINE_ID_LENGTH];
+
+ u8_t name_len;
+ u8_t engineid_len;
+
+ u8_t name_start;
+ u8_t engineid_start = 1;
+ u8_t i;
+
+ struct snmp_next_oid_state state;
+
+ u32_t result_temp[LWIP_ARRAYSIZE(usmUserTable_oid_ranges)];
+
+ LWIP_UNUSED_ARG(column);
+
+ snmpv3_get_engine_id(&engineid, &eid_len);
+
+ /* If EngineID might be given */
+ if (row_oid->len > 0) {
+ engineid_len = (u8_t)row_oid->id[0];
+ engineid_start = 1;
+
+ if (engineid_len != eid_len) {
+ /* EngineID length does not match! */
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ if (engineid_len > row_oid->len) {
+ /* Verify partial EngineID */
+ snmp_engineid_to_oid(engineid, engineid_oid, row_oid->len - 1);
+ if (!snmp_oid_equal(&row_oid->id[engineid_start], row_oid->len - 1, engineid_oid, row_oid->len - 1)) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+ } else {
+ /* Verify complete EngineID */
+ snmp_engineid_to_oid(engineid, engineid_oid, engineid_len);
+ if (!snmp_oid_equal(&row_oid->id[engineid_start], engineid_len, engineid_oid, engineid_len)) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+ }
+
+ /* At this point, the given EngineID (partially) matches the local EngineID.*/
+
+ /* If name might also be given */
+ if (row_oid->len > engineid_start + engineid_len) {
+ name_len = (u8_t)row_oid->id[engineid_start + engineid_len];
+ name_start = engineid_start + engineid_len + 1;
+
+ if (name_len > SNMP_V3_MAX_USER_LENGTH) {
+ /* specified name is too long, max length is 32 according to mib file.*/
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ if (row_oid->len < engineid_len + name_len + 2) {
+ /* Partial name given according to oid.*/
+ u8_t tmplen = row_oid->len - engineid_len - 2;
+ if (!snmp_oid_in_range(&row_oid->id[name_start], tmplen, usmUserTable_oid_ranges, tmplen)) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+ } else {
+ /* Full name given according to oid. Also test for too much data.*/
+ u8_t tmplen = row_oid->len - engineid_len - 2;
+ if (!snmp_oid_in_range(&row_oid->id[name_start], name_len, usmUserTable_oid_ranges, tmplen)) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+ }
+
+ /* At this point the EngineID and (partial) UserName match the local EngineID and UserName.*/
+ }
+ }
+
+ /* init struct to search next oid */
+ snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(usmUserTable_oid_ranges));
+
+ for (i = 0; i < snmpv3_get_amount_of_users(); i++) {
+ u32_t test_oid[LWIP_ARRAYSIZE(usmUserTable_oid_ranges)];
+
+ test_oid[0] = eid_len;
+ snmp_engineid_to_oid(engineid, &test_oid[1], eid_len);
+
+ snmpv3_get_username(username, i);
+
+ test_oid[1 + eid_len] = strlen(username);
+ snmp_name_to_oid(username, &test_oid[2 + eid_len], strlen(username));
+
+ /* check generated OID: is it a candidate for the next one? */
+ snmp_next_oid_check(&state, test_oid, (u8_t)(1 + eid_len + 1 + strlen(username)), LWIP_PTR_NUMERIC_CAST(void *, i));
+ }
+
+ /* did we find a next one? */
+ if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+ snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+ /* store username for subsequent operations (get/test/set) */
+ memset(username, 0, sizeof(username));
+ snmpv3_get_username(username, LWIP_PTR_NUMERIC_CAST(u8_t, state.reference));
+ cell_instance->reference.ptr = username;
+ cell_instance->reference_len = strlen(username);
+ return SNMP_ERR_NOERROR;
+ }
+
+ /* not found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static s16_t usmusertable_get_value(struct snmp_node_instance *cell_instance, void *value)
+{
+ snmpv3_user_storagetype_t storage_type;
+
+ switch (SNMP_TABLE_GET_COLUMN_FROM_OID(cell_instance->instance_oid.id)) {
+ case 3: /* usmUserSecurityName */
+ MEMCPY(value, cell_instance->reference.ptr, cell_instance->reference_len);
+ return (s16_t)cell_instance->reference_len;
+ case 4: /* usmUserCloneFrom */
+ MEMCPY(value, snmp_zero_dot_zero.id, snmp_zero_dot_zero.len * sizeof(u32_t));
+ return snmp_zero_dot_zero.len * sizeof(u32_t);
+ case 5: { /* usmUserAuthProtocol */
+ const struct snmp_obj_id *auth_algo;
+ snmpv3_auth_algo_t auth_algo_val;
+ snmpv3_get_user((const char *)cell_instance->reference.ptr, &auth_algo_val, NULL, NULL, NULL);
+ auth_algo = snmp_auth_algo_to_oid(auth_algo_val);
+ MEMCPY(value, auth_algo->id, auth_algo->len * sizeof(u32_t));
+ return auth_algo->len * sizeof(u32_t);
+ }
+ case 6: /* usmUserAuthKeyChange */
+ return 0;
+ case 7: /* usmUserOwnAuthKeyChange */
+ return 0;
+ case 8: { /* usmUserPrivProtocol */
+ const struct snmp_obj_id *priv_algo;
+ snmpv3_priv_algo_t priv_algo_val;
+ snmpv3_get_user((const char *)cell_instance->reference.ptr, NULL, NULL, &priv_algo_val, NULL);
+ priv_algo = snmp_priv_algo_to_oid(priv_algo_val);
+ MEMCPY(value, priv_algo->id, priv_algo->len * sizeof(u32_t));
+ return priv_algo->len * sizeof(u32_t);
+ }
+ case 9: /* usmUserPrivKeyChange */
+ return 0;
+ case 10: /* usmUserOwnPrivKeyChange */
+ return 0;
+ case 11: /* usmUserPublic */
+ /* TODO: Implement usmUserPublic */
+ return 0;
+ case 12: /* usmUserStorageType */
+ snmpv3_get_user_storagetype((const char *)cell_instance->reference.ptr, &storage_type);
+ *(s32_t *)value = storage_type;
+ return sizeof(s32_t);
+ case 13: /* usmUserStatus */
+ *(s32_t *)value = 1; /* active */
+ return sizeof(s32_t);
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG, ("usmusertable_get_value(): unknown id: %"S32_F"\n", SNMP_TABLE_GET_COLUMN_FROM_OID(cell_instance->instance_oid.id)));
+ return 0;
+ }
+}
+
+/* --- usmMIBObjects 1.3.6.1.6.3.15.1 ----------------------------------------------------- */
+static s16_t usmstats_scalars_get_value(const struct snmp_scalar_array_node_def *node, void *value)
+{
+ u32_t *uint_ptr = (u32_t *)value;
+ switch (node->oid) {
+ case 1: /* usmStatsUnsupportedSecLevels */
+ *uint_ptr = snmp_stats.unsupportedseclevels;
+ break;
+ case 2: /* usmStatsNotInTimeWindows */
+ *uint_ptr = snmp_stats.notintimewindows;
+ break;
+ case 3: /* usmStatsUnknownUserNames */
+ *uint_ptr = snmp_stats.unknownusernames;
+ break;
+ case 4: /* usmStatsUnknownEngineIDs */
+ *uint_ptr = snmp_stats.unknownengineids;
+ break;
+ case 5: /* usmStatsWrongDigests */
+ *uint_ptr = snmp_stats.wrongdigests;
+ break;
+ case 6: /* usmStatsDecryptionErrors */
+ *uint_ptr = snmp_stats.decryptionerrors;
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG, ("usmstats_scalars_get_value(): unknown id: %"S32_F"\n", node->oid));
+ return 0;
+ }
+
+ return sizeof(*uint_ptr);
+}
+
+/* --- snmpUsmMIB ----------------------------------------------------- */
+
+/* --- usmUser 1.3.6.1.6.3.15.1.2 ----------------------------------------------------- */
+
+static const struct snmp_table_col_def usmusertable_columns[] = {
+ {3, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserSecurityName */
+ {4, SNMP_ASN1_TYPE_OBJECT_ID, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserCloneFrom */
+ {5, SNMP_ASN1_TYPE_OBJECT_ID, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserAuthProtocol */
+ {6, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserAuthKeyChange */
+ {7, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserOwnAuthKeyChange */
+ {8, SNMP_ASN1_TYPE_OBJECT_ID, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserPrivProtocol */
+ {9, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserPrivKeyChange */
+ {10, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserOwnPrivKeyChange */
+ {11, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserPublic */
+ {12, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserStorageType */
+ {13, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserStatus */
+};
+static const struct snmp_table_node usmusertable = SNMP_TABLE_CREATE(2, usmusertable_columns, usmusertable_get_instance, usmusertable_get_next_instance, usmusertable_get_value, NULL, NULL);
+
+static const struct snmp_node *const usmuser_subnodes[] = {
+ &usmusertable.node.node
+};
+static const struct snmp_tree_node usmuser_treenode = SNMP_CREATE_TREE_NODE(2, usmuser_subnodes);
+
+/* --- usmMIBObjects 1.3.6.1.6.3.15.1 ----------------------------------------------------- */
+static const struct snmp_scalar_array_node_def usmstats_scalars_nodes[] = {
+ {1, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmStatsUnsupportedSecLevels */
+ {2, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmStatsNotInTimeWindows */
+ {3, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmStatsUnknownUserNames */
+ {4, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmStatsUnknownEngineIDs */
+ {5, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmStatsWrongDigests */
+ {6, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmStatsDecryptionErrors */
+};
+static const struct snmp_scalar_array_node usmstats_scalars = SNMP_SCALAR_CREATE_ARRAY_NODE(1, usmstats_scalars_nodes, usmstats_scalars_get_value, NULL, NULL);
+
+static const struct snmp_node *const usmmibobjects_subnodes[] = {
+ &usmstats_scalars.node.node,
+ &usmuser_treenode.node
+};
+static const struct snmp_tree_node usmmibobjects_treenode = SNMP_CREATE_TREE_NODE(1, usmmibobjects_subnodes);
+
+/* --- snmpUsmMIB ----------------------------------------------------- */
+static const struct snmp_node *const snmpusmmib_subnodes[] = {
+ &usmmibobjects_treenode.node
+};
+static const struct snmp_tree_node snmpusmmib_root = SNMP_CREATE_TREE_NODE(15, snmpusmmib_subnodes);
+static const u32_t snmpusmmib_base_oid[] = {1, 3, 6, 1, 6, 3, 15};
+const struct snmp_mib snmpusmmib = {snmpusmmib_base_oid, LWIP_ARRAYSIZE(snmpusmmib_base_oid), &snmpusmmib_root.node};
+
+#endif /* LWIP_SNMP */
diff --git a/resources/ethernet/LwIP/apps/snmp/snmp_table.c b/resources/ethernet/LwIP/apps/snmp/snmp_table.c
new file mode 100644
index 00000000..55d744b5
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/snmp/snmp_table.c
@@ -0,0 +1,342 @@
+/**
+ * @file
+ * SNMP table support implementation.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Martin Hentschel
+ *
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_table.h"
+#include
+
+snmp_err_t snmp_table_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance *instance)
+{
+ snmp_err_t ret = SNMP_ERR_NOSUCHINSTANCE;
+ const struct snmp_table_node *table_node = (const struct snmp_table_node *)(const void *)instance->node;
+
+ LWIP_UNUSED_ARG(root_oid);
+ LWIP_UNUSED_ARG(root_oid_len);
+
+ /* check min. length (fixed row entry definition, column, row instance oid with at least one entry */
+ /* fixed row entry always has oid 1 */
+ if ((instance->instance_oid.len >= 3) && (instance->instance_oid.id[0] == 1)) {
+ /* search column */
+ const struct snmp_table_col_def *col_def = table_node->columns;
+ u16_t i = table_node->column_count;
+ while (i > 0) {
+ if (col_def->index == instance->instance_oid.id[1]) {
+ break;
+ }
+
+ col_def++;
+ i--;
+ }
+
+ if (i > 0) {
+ /* everything may be overwritten by get_cell_instance_method() in order to implement special handling for single columns/cells */
+ instance->asn1_type = col_def->asn1_type;
+ instance->access = col_def->access;
+ instance->get_value = table_node->get_value;
+ instance->set_test = table_node->set_test;
+ instance->set_value = table_node->set_value;
+
+ ret = table_node->get_cell_instance(
+ &(instance->instance_oid.id[1]),
+ &(instance->instance_oid.id[2]),
+ instance->instance_oid.len - 2,
+ instance);
+ }
+ }
+
+ return ret;
+}
+
+snmp_err_t snmp_table_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance *instance)
+{
+ const struct snmp_table_node *table_node = (const struct snmp_table_node *)(const void *)instance->node;
+ const struct snmp_table_col_def *col_def;
+ struct snmp_obj_id row_oid;
+ u32_t column = 0;
+ snmp_err_t result;
+
+ LWIP_UNUSED_ARG(root_oid);
+ LWIP_UNUSED_ARG(root_oid_len);
+
+ /* check that first part of id is 0 or 1, referencing fixed row entry */
+ if ((instance->instance_oid.len > 0) && (instance->instance_oid.id[0] > 1)) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+ if (instance->instance_oid.len > 1) {
+ column = instance->instance_oid.id[1];
+ }
+ if (instance->instance_oid.len > 2) {
+ snmp_oid_assign(&row_oid, &(instance->instance_oid.id[2]), instance->instance_oid.len - 2);
+ } else {
+ row_oid.len = 0;
+ }
+
+ instance->get_value = table_node->get_value;
+ instance->set_test = table_node->set_test;
+ instance->set_value = table_node->set_value;
+
+ /* resolve column and value */
+ do {
+ u16_t i;
+ const struct snmp_table_col_def *next_col_def = NULL;
+ col_def = table_node->columns;
+
+ for (i = 0; i < table_node->column_count; i++) {
+ if (col_def->index == column) {
+ next_col_def = col_def;
+ break;
+ } else if ((col_def->index > column) && ((next_col_def == NULL) || (col_def->index < next_col_def->index))) {
+ next_col_def = col_def;
+ }
+ col_def++;
+ }
+
+ if (next_col_def == NULL) {
+ /* no further column found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ instance->asn1_type = next_col_def->asn1_type;
+ instance->access = next_col_def->access;
+
+ result = table_node->get_next_cell_instance(
+ &next_col_def->index,
+ &row_oid,
+ instance);
+
+ if (result == SNMP_ERR_NOERROR) {
+ col_def = next_col_def;
+ break;
+ }
+
+ row_oid.len = 0; /* reset row_oid because we switch to next column and start with the first entry there */
+ column = next_col_def->index + 1;
+ } while (1);
+
+ /* build resulting oid */
+ instance->instance_oid.len = 2;
+ instance->instance_oid.id[0] = 1;
+ instance->instance_oid.id[1] = col_def->index;
+ snmp_oid_append(&instance->instance_oid, row_oid.id, row_oid.len);
+
+ return SNMP_ERR_NOERROR;
+}
+
+
+snmp_err_t snmp_table_simple_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance *instance)
+{
+ snmp_err_t ret = SNMP_ERR_NOSUCHINSTANCE;
+ const struct snmp_table_simple_node *table_node = (const struct snmp_table_simple_node *)(const void *)instance->node;
+
+ LWIP_UNUSED_ARG(root_oid);
+ LWIP_UNUSED_ARG(root_oid_len);
+
+ /* check min. length (fixed row entry definition, column, row instance oid with at least one entry */
+ /* fixed row entry always has oid 1 */
+ if ((instance->instance_oid.len >= 3) && (instance->instance_oid.id[0] == 1)) {
+ ret = table_node->get_cell_value(
+ &(instance->instance_oid.id[1]),
+ &(instance->instance_oid.id[2]),
+ instance->instance_oid.len - 2,
+ &instance->reference,
+ &instance->reference_len);
+
+ if (ret == SNMP_ERR_NOERROR) {
+ /* search column */
+ const struct snmp_table_simple_col_def *col_def = table_node->columns;
+ u32_t i = table_node->column_count;
+ while (i > 0) {
+ if (col_def->index == instance->instance_oid.id[1]) {
+ break;
+ }
+
+ col_def++;
+ i--;
+ }
+
+ if (i > 0) {
+ instance->asn1_type = col_def->asn1_type;
+ instance->access = SNMP_NODE_INSTANCE_READ_ONLY;
+ instance->set_test = NULL;
+ instance->set_value = NULL;
+
+ switch (col_def->data_type) {
+ case SNMP_VARIANT_VALUE_TYPE_U32:
+ instance->get_value = snmp_table_extract_value_from_u32ref;
+ break;
+ case SNMP_VARIANT_VALUE_TYPE_S32:
+ instance->get_value = snmp_table_extract_value_from_s32ref;
+ break;
+ case SNMP_VARIANT_VALUE_TYPE_PTR: /* fall through */
+ case SNMP_VARIANT_VALUE_TYPE_CONST_PTR:
+ instance->get_value = snmp_table_extract_value_from_refconstptr;
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_DEBUG, ("snmp_table_simple_get_instance(): unknown column data_type: %d\n", col_def->data_type));
+ return SNMP_ERR_GENERROR;
+ }
+
+ ret = SNMP_ERR_NOERROR;
+ } else {
+ ret = SNMP_ERR_NOSUCHINSTANCE;
+ }
+ }
+ }
+
+ return ret;
+}
+
+snmp_err_t snmp_table_simple_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance *instance)
+{
+ const struct snmp_table_simple_node *table_node = (const struct snmp_table_simple_node *)(const void *)instance->node;
+ const struct snmp_table_simple_col_def *col_def;
+ struct snmp_obj_id row_oid;
+ u32_t column = 0;
+ snmp_err_t result;
+
+ LWIP_UNUSED_ARG(root_oid);
+ LWIP_UNUSED_ARG(root_oid_len);
+
+ /* check that first part of id is 0 or 1, referencing fixed row entry */
+ if ((instance->instance_oid.len > 0) && (instance->instance_oid.id[0] > 1)) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+ if (instance->instance_oid.len > 1) {
+ column = instance->instance_oid.id[1];
+ }
+ if (instance->instance_oid.len > 2) {
+ snmp_oid_assign(&row_oid, &(instance->instance_oid.id[2]), instance->instance_oid.len - 2);
+ } else {
+ row_oid.len = 0;
+ }
+
+ /* resolve column and value */
+ do {
+ u32_t i;
+ const struct snmp_table_simple_col_def *next_col_def = NULL;
+ col_def = table_node->columns;
+
+ for (i = 0; i < table_node->column_count; i++) {
+ if (col_def->index == column) {
+ next_col_def = col_def;
+ break;
+ } else if ((col_def->index > column) && ((next_col_def == NULL) ||
+ (col_def->index < next_col_def->index))) {
+ next_col_def = col_def;
+ }
+ col_def++;
+ }
+
+ if (next_col_def == NULL) {
+ /* no further column found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ result = table_node->get_next_cell_instance_and_value(
+ &next_col_def->index,
+ &row_oid,
+ &instance->reference,
+ &instance->reference_len);
+
+ if (result == SNMP_ERR_NOERROR) {
+ col_def = next_col_def;
+ break;
+ }
+
+ row_oid.len = 0; /* reset row_oid because we switch to next column and start with the first entry there */
+ column = next_col_def->index + 1;
+ } while (1);
+
+ instance->asn1_type = col_def->asn1_type;
+ instance->access = SNMP_NODE_INSTANCE_READ_ONLY;
+ instance->set_test = NULL;
+ instance->set_value = NULL;
+
+ switch (col_def->data_type) {
+ case SNMP_VARIANT_VALUE_TYPE_U32:
+ instance->get_value = snmp_table_extract_value_from_u32ref;
+ break;
+ case SNMP_VARIANT_VALUE_TYPE_S32:
+ instance->get_value = snmp_table_extract_value_from_s32ref;
+ break;
+ case SNMP_VARIANT_VALUE_TYPE_PTR: /* fall through */
+ case SNMP_VARIANT_VALUE_TYPE_CONST_PTR:
+ instance->get_value = snmp_table_extract_value_from_refconstptr;
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_DEBUG, ("snmp_table_simple_get_instance(): unknown column data_type: %d\n", col_def->data_type));
+ return SNMP_ERR_GENERROR;
+ }
+
+ /* build resulting oid */
+ instance->instance_oid.len = 2;
+ instance->instance_oid.id[0] = 1;
+ instance->instance_oid.id[1] = col_def->index;
+ snmp_oid_append(&instance->instance_oid, row_oid.id, row_oid.len);
+
+ return SNMP_ERR_NOERROR;
+}
+
+
+s16_t
+snmp_table_extract_value_from_s32ref(struct snmp_node_instance *instance, void *value)
+{
+ s32_t *dst = (s32_t *)value;
+ *dst = instance->reference.s32;
+ return sizeof(*dst);
+}
+
+s16_t
+snmp_table_extract_value_from_u32ref(struct snmp_node_instance *instance, void *value)
+{
+ u32_t *dst = (u32_t *)value;
+ *dst = instance->reference.u32;
+ return sizeof(*dst);
+}
+
+s16_t
+snmp_table_extract_value_from_refconstptr(struct snmp_node_instance *instance, void *value)
+{
+ MEMCPY(value, instance->reference.const_ptr, instance->reference_len);
+ return (u16_t)instance->reference_len;
+}
+
+#endif /* LWIP_SNMP */
diff --git a/resources/ethernet/LwIP/apps/snmp/snmp_threadsync.c b/resources/ethernet/LwIP/apps/snmp/snmp_threadsync.c
new file mode 100644
index 00000000..764856dd
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/snmp/snmp_threadsync.c
@@ -0,0 +1,231 @@
+/**
+ * @file
+ * SNMP thread synchronization implementation.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Dirk Ziegelmeier
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP && (NO_SYS == 0) /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/snmp_threadsync.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/sys.h"
+#include
+
+static void
+call_synced_function(struct threadsync_data *call_data, snmp_threadsync_called_fn fn)
+{
+ sys_mutex_lock(&call_data->threadsync_node->instance->sem_usage_mutex);
+ call_data->threadsync_node->instance->sync_fn(fn, call_data);
+ sys_sem_wait(&call_data->threadsync_node->instance->sem);
+ sys_mutex_unlock(&call_data->threadsync_node->instance->sem_usage_mutex);
+}
+
+static void
+threadsync_get_value_synced(void *ctx)
+{
+ struct threadsync_data *call_data = (struct threadsync_data *)ctx;
+
+ if (call_data->proxy_instance.get_value != NULL) {
+ call_data->retval.s16 = call_data->proxy_instance.get_value(&call_data->proxy_instance, call_data->arg1.value);
+ } else {
+ call_data->retval.s16 = -1;
+ }
+
+ sys_sem_signal(&call_data->threadsync_node->instance->sem);
+}
+
+static s16_t
+threadsync_get_value(struct snmp_node_instance *instance, void *value)
+{
+ struct threadsync_data *call_data = (struct threadsync_data *)instance->reference.ptr;
+
+ call_data->arg1.value = value;
+ call_synced_function(call_data, threadsync_get_value_synced);
+
+ return call_data->retval.s16;
+}
+
+static void
+threadsync_set_test_synced(void *ctx)
+{
+ struct threadsync_data *call_data = (struct threadsync_data *)ctx;
+
+ if (call_data->proxy_instance.set_test != NULL) {
+ call_data->retval.err = call_data->proxy_instance.set_test(&call_data->proxy_instance, call_data->arg2.len, call_data->arg1.value);
+ } else {
+ call_data->retval.err = SNMP_ERR_NOTWRITABLE;
+ }
+
+ sys_sem_signal(&call_data->threadsync_node->instance->sem);
+}
+
+static snmp_err_t
+threadsync_set_test(struct snmp_node_instance *instance, u16_t len, void *value)
+{
+ struct threadsync_data *call_data = (struct threadsync_data *)instance->reference.ptr;
+
+ call_data->arg1.value = value;
+ call_data->arg2.len = len;
+ call_synced_function(call_data, threadsync_set_test_synced);
+
+ return call_data->retval.err;
+}
+
+static void
+threadsync_set_value_synced(void *ctx)
+{
+ struct threadsync_data *call_data = (struct threadsync_data *)ctx;
+
+ if (call_data->proxy_instance.set_value != NULL) {
+ call_data->retval.err = call_data->proxy_instance.set_value(&call_data->proxy_instance, call_data->arg2.len, call_data->arg1.value);
+ } else {
+ call_data->retval.err = SNMP_ERR_NOTWRITABLE;
+ }
+
+ sys_sem_signal(&call_data->threadsync_node->instance->sem);
+}
+
+static snmp_err_t
+threadsync_set_value(struct snmp_node_instance *instance, u16_t len, void *value)
+{
+ struct threadsync_data *call_data = (struct threadsync_data *)instance->reference.ptr;
+
+ call_data->arg1.value = value;
+ call_data->arg2.len = len;
+ call_synced_function(call_data, threadsync_set_value_synced);
+
+ return call_data->retval.err;
+}
+
+static void
+threadsync_release_instance_synced(void *ctx)
+{
+ struct threadsync_data *call_data = (struct threadsync_data *)ctx;
+
+ call_data->proxy_instance.release_instance(&call_data->proxy_instance);
+
+ sys_sem_signal(&call_data->threadsync_node->instance->sem);
+}
+
+static void
+threadsync_release_instance(struct snmp_node_instance *instance)
+{
+ struct threadsync_data *call_data = (struct threadsync_data *)instance->reference.ptr;
+
+ if (call_data->proxy_instance.release_instance != NULL) {
+ call_synced_function(call_data, threadsync_release_instance_synced);
+ }
+}
+
+static void
+get_instance_synced(void *ctx)
+{
+ struct threadsync_data *call_data = (struct threadsync_data *)ctx;
+ const struct snmp_leaf_node *leaf = (const struct snmp_leaf_node *)(const void *)call_data->proxy_instance.node;
+
+ call_data->retval.err = leaf->get_instance(call_data->arg1.root_oid, call_data->arg2.root_oid_len, &call_data->proxy_instance);
+
+ sys_sem_signal(&call_data->threadsync_node->instance->sem);
+}
+
+static void
+get_next_instance_synced(void *ctx)
+{
+ struct threadsync_data *call_data = (struct threadsync_data *)ctx;
+ const struct snmp_leaf_node *leaf = (const struct snmp_leaf_node *)(const void *)call_data->proxy_instance.node;
+
+ call_data->retval.err = leaf->get_next_instance(call_data->arg1.root_oid, call_data->arg2.root_oid_len, &call_data->proxy_instance);
+
+ sys_sem_signal(&call_data->threadsync_node->instance->sem);
+}
+
+static snmp_err_t
+do_sync(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance *instance, snmp_threadsync_called_fn fn)
+{
+ const struct snmp_threadsync_node *threadsync_node = (const struct snmp_threadsync_node *)(const void *)instance->node;
+ struct threadsync_data *call_data = &threadsync_node->instance->data;
+
+ if (threadsync_node->node.node.oid != threadsync_node->target->node.oid) {
+ LWIP_DEBUGF(SNMP_DEBUG, ("Sync node OID does not match target node OID"));
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ memset(&call_data->proxy_instance, 0, sizeof(call_data->proxy_instance));
+
+ instance->reference.ptr = call_data;
+ snmp_oid_assign(&call_data->proxy_instance.instance_oid, instance->instance_oid.id, instance->instance_oid.len);
+
+ call_data->proxy_instance.node = &threadsync_node->target->node;
+ call_data->threadsync_node = threadsync_node;
+
+ call_data->arg1.root_oid = root_oid;
+ call_data->arg2.root_oid_len = root_oid_len;
+ call_synced_function(call_data, fn);
+
+ if (call_data->retval.err == SNMP_ERR_NOERROR) {
+ instance->access = call_data->proxy_instance.access;
+ instance->asn1_type = call_data->proxy_instance.asn1_type;
+ instance->release_instance = threadsync_release_instance;
+ instance->get_value = (call_data->proxy_instance.get_value != NULL) ? threadsync_get_value : NULL;
+ instance->set_value = (call_data->proxy_instance.set_value != NULL) ? threadsync_set_value : NULL;
+ instance->set_test = (call_data->proxy_instance.set_test != NULL) ? threadsync_set_test : NULL;
+ snmp_oid_assign(&instance->instance_oid, call_data->proxy_instance.instance_oid.id, call_data->proxy_instance.instance_oid.len);
+ }
+
+ return call_data->retval.err;
+}
+
+snmp_err_t
+snmp_threadsync_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance *instance)
+{
+ return do_sync(root_oid, root_oid_len, instance, get_instance_synced);
+}
+
+snmp_err_t
+snmp_threadsync_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance *instance)
+{
+ return do_sync(root_oid, root_oid_len, instance, get_next_instance_synced);
+}
+
+/** Initializes thread synchronization instance */
+void snmp_threadsync_init(struct snmp_threadsync_instance *instance, snmp_threadsync_synchronizer_fn sync_fn)
+{
+ err_t err = sys_mutex_new(&instance->sem_usage_mutex);
+ LWIP_ASSERT("Failed to set up mutex", err == ERR_OK);
+ err = sys_sem_new(&instance->sem, 0);
+ LWIP_UNUSED_ARG(err); /* in case of LWIP_NOASSERT */
+ LWIP_ASSERT("Failed to set up semaphore", err == ERR_OK);
+ instance->sync_fn = sync_fn;
+}
+
+#endif /* LWIP_SNMP */
diff --git a/resources/ethernet/LwIP/apps/snmp/snmp_traps.c b/resources/ethernet/LwIP/apps/snmp/snmp_traps.c
new file mode 100644
index 00000000..002596a1
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/snmp/snmp_traps.c
@@ -0,0 +1,458 @@
+/**
+ * @file
+ * SNMPv1 traps implementation.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Martin Hentschel
+ * Christiaan Simons
+ *
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include
+
+#include "lwip/snmp.h"
+#include "lwip/sys.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/prot/iana.h"
+#include "snmp_msg.h"
+#include "snmp_asn1.h"
+#include "snmp_core_priv.h"
+
+struct snmp_msg_trap {
+ /* source enterprise ID (sysObjectID) */
+ const struct snmp_obj_id *enterprise;
+ /* source IP address, raw network order format */
+ ip_addr_t sip;
+ /* generic trap code */
+ u32_t gen_trap;
+ /* specific trap code */
+ u32_t spc_trap;
+ /* timestamp */
+ u32_t ts;
+ /* snmp_version */
+ u32_t snmp_version;
+
+ /* output trap lengths used in ASN encoding */
+ /* encoding pdu length */
+ u16_t pdulen;
+ /* encoding community length */
+ u16_t comlen;
+ /* encoding sequence length */
+ u16_t seqlen;
+ /* encoding varbinds sequence length */
+ u16_t vbseqlen;
+};
+
+static u16_t snmp_trap_varbind_sum(struct snmp_msg_trap *trap, struct snmp_varbind *varbinds);
+static u16_t snmp_trap_header_sum(struct snmp_msg_trap *trap, u16_t vb_len);
+static err_t snmp_trap_header_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream);
+static err_t snmp_trap_varbind_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind *varbinds);
+
+#define BUILD_EXEC(code) \
+ if ((code) != ERR_OK) { \
+ LWIP_DEBUGF(SNMP_DEBUG, ("SNMP error during creation of outbound trap frame!")); \
+ return ERR_ARG; \
+ }
+
+/** Agent community string for sending traps */
+extern const char *snmp_community_trap;
+
+void *snmp_traps_handle;
+
+struct snmp_trap_dst {
+ /* destination IP address in network order */
+ ip_addr_t dip;
+ /* set to 0 when disabled, >0 when enabled */
+ u8_t enable;
+};
+static struct snmp_trap_dst trap_dst[SNMP_TRAP_DESTINATIONS];
+
+static u8_t snmp_auth_traps_enabled = 0;
+
+/**
+ * @ingroup snmp_traps
+ * Sets enable switch for this trap destination.
+ * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1
+ * @param enable switch if 0 destination is disabled >0 enabled.
+ */
+void
+snmp_trap_dst_enable(u8_t dst_idx, u8_t enable)
+{
+ LWIP_ASSERT_CORE_LOCKED();
+ if (dst_idx < SNMP_TRAP_DESTINATIONS) {
+ trap_dst[dst_idx].enable = enable;
+ }
+}
+
+/**
+ * @ingroup snmp_traps
+ * Sets IPv4 address for this trap destination.
+ * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1
+ * @param dst IPv4 address in host order.
+ */
+void
+snmp_trap_dst_ip_set(u8_t dst_idx, const ip_addr_t *dst)
+{
+ LWIP_ASSERT_CORE_LOCKED();
+ if (dst_idx < SNMP_TRAP_DESTINATIONS) {
+ ip_addr_set(&trap_dst[dst_idx].dip, dst);
+ }
+}
+
+/**
+ * @ingroup snmp_traps
+ * Enable/disable authentication traps
+ */
+void
+snmp_set_auth_traps_enabled(u8_t enable)
+{
+ snmp_auth_traps_enabled = enable;
+}
+
+/**
+ * @ingroup snmp_traps
+ * Get authentication traps enabled state
+ */
+u8_t
+snmp_get_auth_traps_enabled(void)
+{
+ return snmp_auth_traps_enabled;
+}
+
+
+/**
+ * @ingroup snmp_traps
+ * Sends a generic or enterprise specific trap message.
+ *
+ * @param eoid points to enterprise object identifier
+ * @param generic_trap is the trap code
+ * @param specific_trap used for enterprise traps when generic_trap == 6
+ * @param varbinds linked list of varbinds to be sent
+ * @return ERR_OK when success, ERR_MEM if we're out of memory
+ *
+ * @note the use of the enterprise identifier field
+ * is per RFC1215.
+ * Use .iso.org.dod.internet.mgmt.mib-2.snmp for generic traps
+ * and .iso.org.dod.internet.private.enterprises.yourenterprise
+ * (sysObjectID) for specific traps.
+ */
+err_t
+snmp_send_trap(const struct snmp_obj_id *eoid, s32_t generic_trap, s32_t specific_trap, struct snmp_varbind *varbinds)
+{
+ struct snmp_msg_trap trap_msg;
+ struct snmp_trap_dst *td;
+ struct pbuf *p;
+ u16_t i, tot_len;
+ err_t err = ERR_OK;
+
+ LWIP_ASSERT_CORE_LOCKED();
+
+ trap_msg.snmp_version = 0;
+
+ for (i = 0, td = &trap_dst[0]; i < SNMP_TRAP_DESTINATIONS; i++, td++) {
+ if ((td->enable != 0) && !ip_addr_isany(&td->dip)) {
+ /* lookup current source address for this dst */
+ if (snmp_get_local_ip_for_dst(snmp_traps_handle, &td->dip, &trap_msg.sip)) {
+ if (eoid == NULL) {
+ trap_msg.enterprise = snmp_get_device_enterprise_oid();
+ } else {
+ trap_msg.enterprise = eoid;
+ }
+
+ trap_msg.gen_trap = generic_trap;
+ if (generic_trap == SNMP_GENTRAP_ENTERPRISE_SPECIFIC) {
+ trap_msg.spc_trap = specific_trap;
+ } else {
+ trap_msg.spc_trap = 0;
+ }
+
+ MIB2_COPY_SYSUPTIME_TO(&trap_msg.ts);
+
+ /* pass 0, calculate length fields */
+ tot_len = snmp_trap_varbind_sum(&trap_msg, varbinds);
+ tot_len = snmp_trap_header_sum(&trap_msg, tot_len);
+
+ /* allocate pbuf(s) */
+ p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_RAM);
+ if (p != NULL) {
+ struct snmp_pbuf_stream pbuf_stream;
+ snmp_pbuf_stream_init(&pbuf_stream, p, 0, tot_len);
+
+ /* pass 1, encode packet into the pbuf(s) */
+ snmp_trap_header_enc(&trap_msg, &pbuf_stream);
+ snmp_trap_varbind_enc(&trap_msg, &pbuf_stream, varbinds);
+
+ snmp_stats.outtraps++;
+ snmp_stats.outpkts++;
+
+ /** send to the TRAP destination */
+ snmp_sendto(snmp_traps_handle, p, &td->dip, LWIP_IANA_PORT_SNMP_TRAP);
+ pbuf_free(p);
+ } else {
+ err = ERR_MEM;
+ }
+ } else {
+ /* routing error */
+ err = ERR_RTE;
+ }
+ }
+ }
+ return err;
+}
+
+/**
+ * @ingroup snmp_traps
+ * Send generic SNMP trap
+ */
+err_t
+snmp_send_trap_generic(s32_t generic_trap)
+{
+ static const struct snmp_obj_id oid = { 7, { 1, 3, 6, 1, 2, 1, 11 } };
+ return snmp_send_trap(&oid, generic_trap, 0, NULL);
+}
+
+/**
+ * @ingroup snmp_traps
+ * Send specific SNMP trap with variable bindings
+ */
+err_t
+snmp_send_trap_specific(s32_t specific_trap, struct snmp_varbind *varbinds)
+{
+ return snmp_send_trap(NULL, SNMP_GENTRAP_ENTERPRISE_SPECIFIC, specific_trap, varbinds);
+}
+
+/**
+ * @ingroup snmp_traps
+ * Send coldstart trap
+ */
+void
+snmp_coldstart_trap(void)
+{
+ snmp_send_trap_generic(SNMP_GENTRAP_COLDSTART);
+}
+
+/**
+ * @ingroup snmp_traps
+ * Send authentication failure trap (used internally by agent)
+ */
+void
+snmp_authfail_trap(void)
+{
+ if (snmp_auth_traps_enabled != 0) {
+ snmp_send_trap_generic(SNMP_GENTRAP_AUTH_FAILURE);
+ }
+}
+
+static u16_t
+snmp_trap_varbind_sum(struct snmp_msg_trap *trap, struct snmp_varbind *varbinds)
+{
+ struct snmp_varbind *varbind;
+ u16_t tot_len;
+ u8_t tot_len_len;
+
+ tot_len = 0;
+ varbind = varbinds;
+ while (varbind != NULL) {
+ struct snmp_varbind_len len;
+
+ if (snmp_varbind_length(varbind, &len) == ERR_OK) {
+ tot_len += 1 + len.vb_len_len + len.vb_value_len;
+ }
+
+ varbind = varbind->next;
+ }
+
+ trap->vbseqlen = tot_len;
+ snmp_asn1_enc_length_cnt(trap->vbseqlen, &tot_len_len);
+ tot_len += 1 + tot_len_len;
+
+ return tot_len;
+}
+
+/**
+ * Sums trap header field lengths from tail to head and
+ * returns trap_header_lengths for second encoding pass.
+ *
+ * @param trap Trap message
+ * @param vb_len varbind-list length
+ * @return the required length for encoding the trap header
+ */
+static u16_t
+snmp_trap_header_sum(struct snmp_msg_trap *trap, u16_t vb_len)
+{
+ u16_t tot_len;
+ u16_t len;
+ u8_t lenlen;
+
+ tot_len = vb_len;
+
+ snmp_asn1_enc_u32t_cnt(trap->ts, &len);
+ snmp_asn1_enc_length_cnt(len, &lenlen);
+ tot_len += 1 + len + lenlen;
+
+ snmp_asn1_enc_s32t_cnt(trap->spc_trap, &len);
+ snmp_asn1_enc_length_cnt(len, &lenlen);
+ tot_len += 1 + len + lenlen;
+
+ snmp_asn1_enc_s32t_cnt(trap->gen_trap, &len);
+ snmp_asn1_enc_length_cnt(len, &lenlen);
+ tot_len += 1 + len + lenlen;
+
+ if (IP_IS_V6_VAL(trap->sip)) {
+#if LWIP_IPV6
+ len = sizeof(ip_2_ip6(&trap->sip)->addr);
+#endif
+ } else {
+#if LWIP_IPV4
+ len = sizeof(ip_2_ip4(&trap->sip)->addr);
+#endif
+ }
+ snmp_asn1_enc_length_cnt(len, &lenlen);
+ tot_len += 1 + len + lenlen;
+
+ snmp_asn1_enc_oid_cnt(trap->enterprise->id, trap->enterprise->len, &len);
+ snmp_asn1_enc_length_cnt(len, &lenlen);
+ tot_len += 1 + len + lenlen;
+
+ trap->pdulen = tot_len;
+ snmp_asn1_enc_length_cnt(trap->pdulen, &lenlen);
+ tot_len += 1 + lenlen;
+
+ trap->comlen = (u16_t)LWIP_MIN(strlen(snmp_community_trap), 0xFFFF);
+ snmp_asn1_enc_length_cnt(trap->comlen, &lenlen);
+ tot_len += 1 + lenlen + trap->comlen;
+
+ snmp_asn1_enc_s32t_cnt(trap->snmp_version, &len);
+ snmp_asn1_enc_length_cnt(len, &lenlen);
+ tot_len += 1 + len + lenlen;
+
+ trap->seqlen = tot_len;
+ snmp_asn1_enc_length_cnt(trap->seqlen, &lenlen);
+ tot_len += 1 + lenlen;
+
+ return tot_len;
+}
+
+static err_t
+snmp_trap_varbind_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind *varbinds)
+{
+ struct snmp_asn1_tlv tlv;
+ struct snmp_varbind *varbind;
+
+ varbind = varbinds;
+
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 0, trap->vbseqlen);
+ BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+
+ while (varbind != NULL) {
+ BUILD_EXEC( snmp_append_outbound_varbind(pbuf_stream, varbind) );
+
+ varbind = varbind->next;
+ }
+
+ return ERR_OK;
+}
+
+/**
+ * Encodes trap header from head to tail.
+ */
+static err_t
+snmp_trap_header_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream)
+{
+ struct snmp_asn1_tlv tlv;
+
+ /* 'Message' sequence */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 0, trap->seqlen);
+ BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+
+ /* version */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
+ snmp_asn1_enc_s32t_cnt(trap->snmp_version, &tlv.value_len);
+ BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+ BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->snmp_version) );
+
+ /* community */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, trap->comlen);
+ BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+ BUILD_EXEC( snmp_asn1_enc_raw(pbuf_stream, (const u8_t *)snmp_community_trap, trap->comlen) );
+
+ /* 'PDU' sequence */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_TRAP), 0, trap->pdulen);
+ BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+
+ /* object ID */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OBJECT_ID, 0, 0);
+ snmp_asn1_enc_oid_cnt(trap->enterprise->id, trap->enterprise->len, &tlv.value_len);
+ BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+ BUILD_EXEC( snmp_asn1_enc_oid(pbuf_stream, trap->enterprise->id, trap->enterprise->len) );
+
+ /* IP addr */
+ if (IP_IS_V6_VAL(trap->sip)) {
+#if LWIP_IPV6
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_IPADDR, 0, sizeof(ip_2_ip6(&trap->sip)->addr));
+ BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+ BUILD_EXEC( snmp_asn1_enc_raw(pbuf_stream, (const u8_t *)&ip_2_ip6(&trap->sip)->addr, sizeof(ip_2_ip6(&trap->sip)->addr)) );
+#endif
+ } else {
+#if LWIP_IPV4
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_IPADDR, 0, sizeof(ip_2_ip4(&trap->sip)->addr));
+ BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+ BUILD_EXEC( snmp_asn1_enc_raw(pbuf_stream, (const u8_t *)&ip_2_ip4(&trap->sip)->addr, sizeof(ip_2_ip4(&trap->sip)->addr)) );
+#endif
+ }
+
+ /* trap length */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
+ snmp_asn1_enc_s32t_cnt(trap->gen_trap, &tlv.value_len);
+ BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+ BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->gen_trap) );
+
+ /* specific trap */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
+ snmp_asn1_enc_s32t_cnt(trap->spc_trap, &tlv.value_len);
+ BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+ BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->spc_trap) );
+
+ /* timestamp */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_TIMETICKS, 0, 0);
+ snmp_asn1_enc_s32t_cnt(trap->ts, &tlv.value_len);
+ BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+ BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->ts) );
+
+ return ERR_OK;
+}
+
+#endif /* LWIP_SNMP */
diff --git a/resources/ethernet/LwIP/apps/snmp/snmpv3.c b/resources/ethernet/LwIP/apps/snmp/snmpv3.c
new file mode 100644
index 00000000..df7bb5cf
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/snmp/snmpv3.c
@@ -0,0 +1,136 @@
+/**
+ * @file
+ * Additional SNMPv3 functionality RFC3414 and RFC3826.
+ */
+
+/*
+ * Copyright (c) 2016 Elias Oenal.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Elias Oenal
+ */
+
+#include "snmpv3_priv.h"
+#include "lwip/apps/snmpv3.h"
+#include "lwip/sys.h"
+#include
+
+#if LWIP_SNMP && LWIP_SNMP_V3
+
+#ifdef LWIP_SNMPV3_INCLUDE_ENGINE
+#include LWIP_SNMPV3_INCLUDE_ENGINE
+#endif
+
+#define SNMP_MAX_TIME_BOOT 2147483647UL
+
+/** Call this if engine has been changed. Has to reset boots, see below */
+void
+snmpv3_engine_id_changed(void)
+{
+ snmpv3_set_engine_boots(0);
+}
+
+/** According to RFC3414 2.2.2.
+ *
+ * The number of times that the SNMP engine has
+ * (re-)initialized itself since snmpEngineID
+ * was last configured.
+ */
+s32_t
+snmpv3_get_engine_boots_internal(void)
+{
+ if (snmpv3_get_engine_boots() == 0 ||
+ snmpv3_get_engine_boots() < SNMP_MAX_TIME_BOOT) {
+ return snmpv3_get_engine_boots();
+ }
+
+ snmpv3_set_engine_boots(SNMP_MAX_TIME_BOOT);
+ return snmpv3_get_engine_boots();
+}
+
+/** RFC3414 2.2.2.
+ *
+ * Once the timer reaches 2147483647 it gets reset to zero and the
+ * engine boot ups get incremented.
+ */
+s32_t
+snmpv3_get_engine_time_internal(void)
+{
+ if (snmpv3_get_engine_time() >= SNMP_MAX_TIME_BOOT) {
+ snmpv3_reset_engine_time();
+
+ if (snmpv3_get_engine_boots() < SNMP_MAX_TIME_BOOT - 1) {
+ snmpv3_set_engine_boots(snmpv3_get_engine_boots() + 1);
+ } else {
+ snmpv3_set_engine_boots(SNMP_MAX_TIME_BOOT);
+ }
+ }
+
+ return snmpv3_get_engine_time();
+}
+
+#if LWIP_SNMP_V3_CRYPTO
+
+/* This function ignores the byte order suggestion in RFC3414
+ * since it simply doesn't influence the effectiveness of an IV.
+ *
+ * Implementing RFC3826 priv param algorithm if LWIP_RAND is available.
+ *
+ * @todo: This is a potential thread safety issue.
+ */
+err_t
+snmpv3_build_priv_param(u8_t *priv_param)
+{
+#ifdef LWIP_RAND /* Based on RFC3826 */
+ static u8_t init;
+ static u32_t priv1, priv2;
+
+ /* Lazy initialisation */
+ if (init == 0) {
+ init = 1;
+ priv1 = LWIP_RAND();
+ priv2 = LWIP_RAND();
+ }
+
+ SMEMCPY(&priv_param[0], &priv1, sizeof(priv1));
+ SMEMCPY(&priv_param[4], &priv2, sizeof(priv2));
+
+ /* Emulate 64bit increment */
+ priv1++;
+ if (!priv1) { /* Overflow */
+ priv2++;
+ }
+#else /* Based on RFC3414 */
+ static u32_t ctr;
+ u32_t boots = snmpv3_get_engine_boots_internal();
+ SMEMCPY(&priv_param[0], &boots, 4);
+ SMEMCPY(&priv_param[4], &ctr, 4);
+ ctr++;
+#endif
+ return ERR_OK;
+}
+#endif /* LWIP_SNMP_V3_CRYPTO */
+
+#endif
diff --git a/resources/ethernet/LwIP/apps/snmp/snmpv3_mbedtls.c b/resources/ethernet/LwIP/apps/snmp/snmpv3_mbedtls.c
new file mode 100644
index 00000000..fe20f788
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/snmp/snmpv3_mbedtls.c
@@ -0,0 +1,342 @@
+/**
+ * @file
+ * SNMPv3 crypto/auth functions implemented for ARM mbedtls.
+ */
+
+/*
+ * Copyright (c) 2016 Elias Oenal and Dirk Ziegelmeier.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Elias Oenal
+ * Dirk Ziegelmeier
+ */
+
+#include "lwip/apps/snmpv3.h"
+#include "snmpv3_priv.h"
+#include "lwip/arch.h"
+#include "snmp_msg.h"
+#include "lwip/sys.h"
+#include
+
+#if LWIP_SNMP && LWIP_SNMP_V3 && LWIP_SNMP_V3_MBEDTLS
+
+#include "mbedtls/md.h"
+#include "mbedtls/cipher.h"
+
+#include "mbedtls/md5.h"
+#include "mbedtls/sha1.h"
+
+err_t
+snmpv3_auth(struct snmp_pbuf_stream *stream, u16_t length,
+ const u8_t *key, snmpv3_auth_algo_t algo, u8_t *hmac_out)
+{
+ u32_t i;
+ u8_t key_len;
+ const mbedtls_md_info_t *md_info;
+ mbedtls_md_context_t ctx;
+ struct snmp_pbuf_stream read_stream;
+ snmp_pbuf_stream_init(&read_stream, stream->pbuf, stream->offset, stream->length);
+
+ if (algo == SNMP_V3_AUTH_ALGO_MD5) {
+ md_info = mbedtls_md_info_from_type(MBEDTLS_MD_MD5);
+ key_len = SNMP_V3_MD5_LEN;
+ } else if (algo == SNMP_V3_AUTH_ALGO_SHA) {
+ md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
+ key_len = SNMP_V3_SHA_LEN;
+ } else {
+ return ERR_ARG;
+ }
+
+ mbedtls_md_init(&ctx);
+ if (mbedtls_md_setup(&ctx, md_info, 1) != 0) {
+ return ERR_ARG;
+ }
+
+ if (mbedtls_md_hmac_starts(&ctx, key, key_len) != 0) {
+ goto free_md;
+ }
+
+ for (i = 0; i < length; i++) {
+ u8_t byte;
+
+ if (snmp_pbuf_stream_read(&read_stream, &byte)) {
+ goto free_md;
+ }
+
+ if (mbedtls_md_hmac_update(&ctx, &byte, 1) != 0) {
+ goto free_md;
+ }
+ }
+
+ if (mbedtls_md_hmac_finish(&ctx, hmac_out) != 0) {
+ goto free_md;
+ }
+
+ mbedtls_md_free(&ctx);
+ return ERR_OK;
+
+free_md:
+ mbedtls_md_free(&ctx);
+ return ERR_ARG;
+}
+
+#if LWIP_SNMP_V3_CRYPTO
+
+err_t
+snmpv3_crypt(struct snmp_pbuf_stream *stream, u16_t length,
+ const u8_t *key, const u8_t *priv_param, const u32_t engine_boots,
+ const u32_t engine_time, snmpv3_priv_algo_t algo, snmpv3_priv_mode_t mode)
+{
+ size_t i;
+ mbedtls_cipher_context_t ctx;
+ const mbedtls_cipher_info_t *cipher_info;
+
+ struct snmp_pbuf_stream read_stream;
+ struct snmp_pbuf_stream write_stream;
+ snmp_pbuf_stream_init(&read_stream, stream->pbuf, stream->offset, stream->length);
+ snmp_pbuf_stream_init(&write_stream, stream->pbuf, stream->offset, stream->length);
+ mbedtls_cipher_init(&ctx);
+
+ if (algo == SNMP_V3_PRIV_ALGO_DES) {
+ u8_t iv_local[8];
+ u8_t out_bytes[8];
+ size_t out_len;
+
+ /* RFC 3414 mandates padding for DES */
+ if ((length & 0x07) != 0) {
+ return ERR_ARG;
+ }
+
+ cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_DES_CBC);
+ if (mbedtls_cipher_setup(&ctx, cipher_info) != 0) {
+ return ERR_ARG;
+ }
+ if (mbedtls_cipher_set_padding_mode(&ctx, MBEDTLS_PADDING_NONE) != 0) {
+ return ERR_ARG;
+ }
+ if (mbedtls_cipher_setkey(&ctx, key, 8 * 8, (mode == SNMP_V3_PRIV_MODE_ENCRYPT) ? MBEDTLS_ENCRYPT : MBEDTLS_DECRYPT) != 0) {
+ goto error;
+ }
+
+ /* Prepare IV */
+ for (i = 0; i < LWIP_ARRAYSIZE(iv_local); i++) {
+ iv_local[i] = priv_param[i] ^ key[i + 8];
+ }
+ if (mbedtls_cipher_set_iv(&ctx, iv_local, LWIP_ARRAYSIZE(iv_local)) != 0) {
+ goto error;
+ }
+
+ for (i = 0; i < length; i += 8) {
+ size_t j;
+ u8_t in_bytes[8];
+ out_len = LWIP_ARRAYSIZE(out_bytes) ;
+
+ for (j = 0; j < LWIP_ARRAYSIZE(in_bytes); j++) {
+ if (snmp_pbuf_stream_read(&read_stream, &in_bytes[j]) != ERR_OK) {
+ goto error;
+ }
+ }
+
+ if (mbedtls_cipher_update(&ctx, in_bytes, LWIP_ARRAYSIZE(in_bytes), out_bytes, &out_len) != 0) {
+ goto error;
+ }
+
+ if (snmp_pbuf_stream_writebuf(&write_stream, out_bytes, (u16_t)out_len) != ERR_OK) {
+ goto error;
+ }
+ }
+
+ out_len = LWIP_ARRAYSIZE(out_bytes);
+ if (mbedtls_cipher_finish(&ctx, out_bytes, &out_len) != 0) {
+ goto error;
+ }
+
+ if (snmp_pbuf_stream_writebuf(&write_stream, out_bytes, (u16_t)out_len) != ERR_OK) {
+ goto error;
+ }
+ } else if (algo == SNMP_V3_PRIV_ALGO_AES) {
+ u8_t iv_local[16];
+
+ cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_128_CFB128);
+ if (mbedtls_cipher_setup(&ctx, cipher_info) != 0) {
+ return ERR_ARG;
+ }
+ if (mbedtls_cipher_setkey(&ctx, key, 16 * 8, (mode == SNMP_V3_PRIV_MODE_ENCRYPT) ? MBEDTLS_ENCRYPT : MBEDTLS_DECRYPT) != 0) {
+ goto error;
+ }
+
+ /*
+ * IV is the big endian concatenation of boots,
+ * uptime and priv param - see RFC3826.
+ */
+ iv_local[0 + 0] = (engine_boots >> 24) & 0xFF;
+ iv_local[0 + 1] = (engine_boots >> 16) & 0xFF;
+ iv_local[0 + 2] = (engine_boots >> 8) & 0xFF;
+ iv_local[0 + 3] = (engine_boots >> 0) & 0xFF;
+ iv_local[4 + 0] = (engine_time >> 24) & 0xFF;
+ iv_local[4 + 1] = (engine_time >> 16) & 0xFF;
+ iv_local[4 + 2] = (engine_time >> 8) & 0xFF;
+ iv_local[4 + 3] = (engine_time >> 0) & 0xFF;
+ SMEMCPY(iv_local + 8, priv_param, 8);
+ if (mbedtls_cipher_set_iv(&ctx, iv_local, LWIP_ARRAYSIZE(iv_local)) != 0) {
+ goto error;
+ }
+
+ for (i = 0; i < length; i++) {
+ u8_t in_byte;
+ u8_t out_byte;
+ size_t out_len = sizeof(out_byte);
+
+ if (snmp_pbuf_stream_read(&read_stream, &in_byte) != ERR_OK) {
+ goto error;
+ }
+ if (mbedtls_cipher_update(&ctx, &in_byte, sizeof(in_byte), &out_byte, &out_len) != 0) {
+ goto error;
+ }
+ if (snmp_pbuf_stream_write(&write_stream, out_byte) != ERR_OK) {
+ goto error;
+ }
+ }
+ } else {
+ return ERR_ARG;
+ }
+
+ mbedtls_cipher_free(&ctx);
+ return ERR_OK;
+
+error:
+ mbedtls_cipher_free(&ctx);
+ return ERR_OK;
+}
+
+#endif /* LWIP_SNMP_V3_CRYPTO */
+
+/* A.2.1. Password to Key Sample Code for MD5 */
+void
+snmpv3_password_to_key_md5(
+ const u8_t *password, /* IN */
+ size_t passwordlen, /* IN */
+ const u8_t *engineID, /* IN - pointer to snmpEngineID */
+ u8_t engineLength,/* IN - length of snmpEngineID */
+ u8_t *key) /* OUT - pointer to caller 16-octet buffer */
+{
+ mbedtls_md5_context MD;
+ u8_t *cp, password_buf[64];
+ u32_t password_index = 0;
+ u8_t i;
+ u32_t count = 0;
+
+ mbedtls_md5_init(&MD); /* initialize MD5 */
+ mbedtls_md5_starts(&MD);
+
+ /**********************************************/
+ /* Use while loop until we've done 1 Megabyte */
+ /**********************************************/
+ while (count < 1048576) {
+ cp = password_buf;
+ for (i = 0; i < 64; i++) {
+ /*************************************************/
+ /* Take the next octet of the password, wrapping */
+ /* to the beginning of the password as necessary.*/
+ /*************************************************/
+ *cp++ = password[password_index++ % passwordlen];
+ }
+ mbedtls_md5_update(&MD, password_buf, 64);
+ count += 64;
+ }
+ mbedtls_md5_finish(&MD, key); /* tell MD5 we're done */
+
+ /*****************************************************/
+ /* Now localize the key with the engineID and pass */
+ /* through MD5 to produce final key */
+ /* May want to ensure that engineLength <= 32, */
+ /* otherwise need to use a buffer larger than 64 */
+ /*****************************************************/
+ SMEMCPY(password_buf, key, 16);
+ MEMCPY(password_buf + 16, engineID, engineLength);
+ SMEMCPY(password_buf + 16 + engineLength, key, 16);
+
+ mbedtls_md5_starts(&MD);
+ mbedtls_md5_update(&MD, password_buf, 32 + engineLength);
+ mbedtls_md5_finish(&MD, key);
+
+ mbedtls_md5_free(&MD);
+ return;
+}
+
+/* A.2.2. Password to Key Sample Code for SHA */
+void
+snmpv3_password_to_key_sha(
+ const u8_t *password, /* IN */
+ size_t passwordlen, /* IN */
+ const u8_t *engineID, /* IN - pointer to snmpEngineID */
+ u8_t engineLength,/* IN - length of snmpEngineID */
+ u8_t *key) /* OUT - pointer to caller 20-octet buffer */
+{
+ mbedtls_sha1_context SH;
+ u8_t *cp, password_buf[72];
+ u32_t password_index = 0;
+ u8_t i;
+ u32_t count = 0;
+
+ mbedtls_sha1_init(&SH); /* initialize SHA */
+ mbedtls_sha1_starts(&SH);
+
+ /**********************************************/
+ /* Use while loop until we've done 1 Megabyte */
+ /**********************************************/
+ while (count < 1048576) {
+ cp = password_buf;
+ for (i = 0; i < 64; i++) {
+ /*************************************************/
+ /* Take the next octet of the password, wrapping */
+ /* to the beginning of the password as necessary.*/
+ /*************************************************/
+ *cp++ = password[password_index++ % passwordlen];
+ }
+ mbedtls_sha1_update(&SH, password_buf, 64);
+ count += 64;
+ }
+ mbedtls_sha1_finish(&SH, key); /* tell SHA we're done */
+
+ /*****************************************************/
+ /* Now localize the key with the engineID and pass */
+ /* through SHA to produce final key */
+ /* May want to ensure that engineLength <= 32, */
+ /* otherwise need to use a buffer larger than 72 */
+ /*****************************************************/
+ SMEMCPY(password_buf, key, 20);
+ MEMCPY(password_buf + 20, engineID, engineLength);
+ SMEMCPY(password_buf + 20 + engineLength, key, 20);
+
+ mbedtls_sha1_starts(&SH);
+ mbedtls_sha1_update(&SH, password_buf, 40 + engineLength);
+ mbedtls_sha1_finish(&SH, key);
+
+ mbedtls_sha1_free(&SH);
+ return;
+}
+
+#endif /* LWIP_SNMP && LWIP_SNMP_V3 && LWIP_SNMP_V3_MBEDTLS */
diff --git a/resources/ethernet/LwIP/apps/snmp/snmpv3_priv.h b/resources/ethernet/LwIP/apps/snmp/snmpv3_priv.h
new file mode 100644
index 00000000..8ea710ac
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/snmp/snmpv3_priv.h
@@ -0,0 +1,69 @@
+/**
+ * @file
+ * Additional SNMPv3 functionality RFC3414 and RFC3826 (internal API, do not use in client code).
+ */
+
+/*
+ * Copyright (c) 2016 Elias Oenal.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Elias Oenal
+ */
+
+#ifndef LWIP_HDR_APPS_SNMP_V3_PRIV_H
+#define LWIP_HDR_APPS_SNMP_V3_PRIV_H
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP && LWIP_SNMP_V3
+
+#include "lwip/apps/snmpv3.h"
+#include "snmp_pbuf_stream.h"
+
+/* According to RFC 3411 */
+#define SNMP_V3_MAX_ENGINE_ID_LENGTH 32
+#define SNMP_V3_MAX_USER_LENGTH 32
+
+#define SNMP_V3_MAX_AUTH_PARAM_LENGTH 12
+#define SNMP_V3_MAX_PRIV_PARAM_LENGTH 8
+
+#define SNMP_V3_MD5_LEN 16
+#define SNMP_V3_SHA_LEN 20
+
+typedef enum {
+ SNMP_V3_PRIV_MODE_DECRYPT = 0,
+ SNMP_V3_PRIV_MODE_ENCRYPT = 1
+} snmpv3_priv_mode_t;
+
+s32_t snmpv3_get_engine_boots_internal(void);
+err_t snmpv3_auth(struct snmp_pbuf_stream *stream, u16_t length, const u8_t *key, snmpv3_auth_algo_t algo, u8_t *hmac_out);
+err_t snmpv3_crypt(struct snmp_pbuf_stream *stream, u16_t length, const u8_t *key,
+ const u8_t *priv_param, const u32_t engine_boots, const u32_t engine_time, snmpv3_priv_algo_t algo, snmpv3_priv_mode_t mode);
+err_t snmpv3_build_priv_param(u8_t *priv_param);
+void snmpv3_enginetime_timer(void *arg);
+
+#endif
+
+#endif /* LWIP_HDR_APPS_SNMP_V3_PRIV_H */
diff --git a/resources/ethernet/LwIP/apps/sntp/sntp.c b/resources/ethernet/LwIP/apps/sntp/sntp.c
new file mode 100644
index 00000000..0952a633
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/sntp/sntp.c
@@ -0,0 +1,869 @@
+/**
+ * @file
+ * SNTP client module
+ */
+
+/*
+ * Copyright (c) 2007-2009 Frédéric Bernon, Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Frédéric Bernon, Simon Goldschmidt
+ */
+
+
+/**
+ * @defgroup sntp SNTP
+ * @ingroup apps
+ *
+ * This is simple "SNTP" client for the lwIP raw API.
+ * It is a minimal implementation of SNTPv4 as specified in RFC 4330.
+ *
+ * For a list of some public NTP servers, see this link:
+ * http://support.ntp.org/bin/view/Servers/NTPPoolServers
+ *
+ * @todo:
+ * - complete SNTP_CHECK_RESPONSE checks 3 and 4
+ */
+
+#include "lwip/apps/sntp.h"
+
+#include "lwip/opt.h"
+#include "lwip/timeouts.h"
+#include "lwip/udp.h"
+#include "lwip/dns.h"
+#include "lwip/ip_addr.h"
+#include "lwip/pbuf.h"
+#include "lwip/dhcp.h"
+
+#include
+#include
+
+#if LWIP_UDP
+
+/* Handle support for more than one server via SNTP_MAX_SERVERS */
+#if SNTP_MAX_SERVERS > 1
+#define SNTP_SUPPORT_MULTIPLE_SERVERS 1
+#else /* NTP_MAX_SERVERS > 1 */
+#define SNTP_SUPPORT_MULTIPLE_SERVERS 0
+#endif /* NTP_MAX_SERVERS > 1 */
+
+#ifndef SNTP_SUPPRESS_DELAY_CHECK
+#if SNTP_UPDATE_DELAY < 15000
+#error "SNTPv4 RFC 4330 enforces a minimum update time of 15 seconds (define SNTP_SUPPRESS_DELAY_CHECK to disable this error)!"
+#endif
+#endif
+
+/* the various debug levels for this file */
+#define SNTP_DEBUG_TRACE (SNTP_DEBUG | LWIP_DBG_TRACE)
+#define SNTP_DEBUG_STATE (SNTP_DEBUG | LWIP_DBG_STATE)
+#define SNTP_DEBUG_WARN (SNTP_DEBUG | LWIP_DBG_LEVEL_WARNING)
+#define SNTP_DEBUG_WARN_STATE (SNTP_DEBUG | LWIP_DBG_LEVEL_WARNING | LWIP_DBG_STATE)
+#define SNTP_DEBUG_SERIOUS (SNTP_DEBUG | LWIP_DBG_LEVEL_SERIOUS)
+
+#define SNTP_ERR_KOD 1
+
+/* SNTP protocol defines */
+#define SNTP_MSG_LEN 48
+
+#define SNTP_OFFSET_LI_VN_MODE 0
+#define SNTP_LI_MASK 0xC0
+#define SNTP_LI_NO_WARNING (0x00 << 6)
+#define SNTP_LI_LAST_MINUTE_61_SEC (0x01 << 6)
+#define SNTP_LI_LAST_MINUTE_59_SEC (0x02 << 6)
+#define SNTP_LI_ALARM_CONDITION (0x03 << 6) /* (clock not synchronized) */
+
+#define SNTP_VERSION_MASK 0x38
+#define SNTP_VERSION (4/* NTP Version 4*/<<3)
+
+#define SNTP_MODE_MASK 0x07
+#define SNTP_MODE_CLIENT 0x03
+#define SNTP_MODE_SERVER 0x04
+#define SNTP_MODE_BROADCAST 0x05
+
+#define SNTP_OFFSET_STRATUM 1
+#define SNTP_STRATUM_KOD 0x00
+
+#define SNTP_OFFSET_ORIGINATE_TIME 24
+#define SNTP_OFFSET_RECEIVE_TIME 32
+#define SNTP_OFFSET_TRANSMIT_TIME 40
+
+/* Number of seconds between 1970 and Feb 7, 2036 06:28:16 UTC (epoch 1) */
+#define DIFF_SEC_1970_2036 ((u32_t)2085978496L)
+
+/** Convert NTP timestamp fraction to microseconds.
+ */
+#ifndef SNTP_FRAC_TO_US
+# if LWIP_HAVE_INT64
+# define SNTP_FRAC_TO_US(f) ((u32_t)(((u64_t)(f) * 1000000UL) >> 32))
+# else
+# define SNTP_FRAC_TO_US(f) ((u32_t)(f) / 4295)
+# endif
+#endif /* !SNTP_FRAC_TO_US */
+
+/* Configure behaviour depending on native, microsecond or second precision.
+ * Treat NTP timestamps as signed two's-complement integers. This way,
+ * timestamps that have the MSB set simply become negative offsets from
+ * the epoch (Feb 7, 2036 06:28:16 UTC). Representable dates range from
+ * 1968 to 2104.
+ */
+#ifndef SNTP_SET_SYSTEM_TIME_NTP
+# ifdef SNTP_SET_SYSTEM_TIME_US
+# define SNTP_SET_SYSTEM_TIME_NTP(s, f) \
+ SNTP_SET_SYSTEM_TIME_US((u32_t)((s) + DIFF_SEC_1970_2036), SNTP_FRAC_TO_US(f))
+# else
+# define SNTP_SET_SYSTEM_TIME_NTP(s, f) \
+ SNTP_SET_SYSTEM_TIME((u32_t)((s) + DIFF_SEC_1970_2036))
+# endif
+#endif /* !SNTP_SET_SYSTEM_TIME_NTP */
+
+/* Get the system time either natively as NTP timestamp or convert from
+ * Unix time in seconds and microseconds. Take care to avoid overflow if the
+ * microsecond value is at the maximum of 999999. Also add 0.5 us fudge to
+ * avoid special values like 0, and to mask round-off errors that would
+ * otherwise break round-trip conversion identity.
+ */
+#ifndef SNTP_GET_SYSTEM_TIME_NTP
+# define SNTP_GET_SYSTEM_TIME_NTP(s, f) do { \
+ u32_t sec_, usec_; \
+ SNTP_GET_SYSTEM_TIME(sec_, usec_); \
+ (s) = (s32_t)(sec_ - DIFF_SEC_1970_2036); \
+ (f) = usec_ * 4295 - ((usec_ * 2143) >> 16) + 2147; \
+ } while (0)
+#endif /* !SNTP_GET_SYSTEM_TIME_NTP */
+
+/* Start offset of the timestamps to extract from the SNTP packet */
+#define SNTP_OFFSET_TIMESTAMPS \
+ (SNTP_OFFSET_TRANSMIT_TIME + 8 - sizeof(struct sntp_timestamps))
+
+/* Round-trip delay arithmetic helpers */
+#if SNTP_COMP_ROUNDTRIP
+# if !LWIP_HAVE_INT64
+# error "SNTP round-trip delay compensation requires 64-bit arithmetic"
+# endif
+# define SNTP_SEC_FRAC_TO_S64(s, f) \
+ ((s64_t)(((u64_t)(s) << 32) | (u32_t)(f)))
+# define SNTP_TIMESTAMP_TO_S64(t) \
+ SNTP_SEC_FRAC_TO_S64(lwip_ntohl((t).sec), lwip_ntohl((t).frac))
+#endif /* SNTP_COMP_ROUNDTRIP */
+
+/**
+ * 64-bit NTP timestamp, in network byte order.
+ */
+struct sntp_time {
+ u32_t sec;
+ u32_t frac;
+};
+
+/**
+ * Timestamps to be extracted from the NTP header.
+ */
+struct sntp_timestamps {
+#if SNTP_COMP_ROUNDTRIP || SNTP_CHECK_RESPONSE >= 2
+ struct sntp_time orig;
+ struct sntp_time recv;
+#endif
+ struct sntp_time xmit;
+};
+
+/**
+ * SNTP packet format (without optional fields)
+ * Timestamps are coded as 64 bits:
+ * - signed 32 bits seconds since Feb 07, 2036, 06:28:16 UTC (epoch 1)
+ * - unsigned 32 bits seconds fraction (2^32 = 1 second)
+ */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct sntp_msg {
+ PACK_STRUCT_FLD_8(u8_t li_vn_mode);
+ PACK_STRUCT_FLD_8(u8_t stratum);
+ PACK_STRUCT_FLD_8(u8_t poll);
+ PACK_STRUCT_FLD_8(u8_t precision);
+ PACK_STRUCT_FIELD(u32_t root_delay);
+ PACK_STRUCT_FIELD(u32_t root_dispersion);
+ PACK_STRUCT_FIELD(u32_t reference_identifier);
+ PACK_STRUCT_FIELD(u32_t reference_timestamp[2]);
+ PACK_STRUCT_FIELD(u32_t originate_timestamp[2]);
+ PACK_STRUCT_FIELD(u32_t receive_timestamp[2]);
+ PACK_STRUCT_FIELD(u32_t transmit_timestamp[2]);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/* function prototypes */
+static void sntp_request(void *arg);
+
+/** The operating mode */
+static u8_t sntp_opmode;
+
+/** The UDP pcb used by the SNTP client */
+static struct udp_pcb *sntp_pcb;
+/** Names/Addresses of servers */
+struct sntp_server {
+#if SNTP_SERVER_DNS
+ const char *name;
+#endif /* SNTP_SERVER_DNS */
+ ip_addr_t addr;
+#if SNTP_MONITOR_SERVER_REACHABILITY
+ /** Reachability shift register as described in RFC 5905 */
+ u8_t reachability;
+#endif /* SNTP_MONITOR_SERVER_REACHABILITY */
+};
+static struct sntp_server sntp_servers[SNTP_MAX_SERVERS];
+
+#if SNTP_GET_SERVERS_FROM_DHCP
+static u8_t sntp_set_servers_from_dhcp;
+#endif /* SNTP_GET_SERVERS_FROM_DHCP */
+#if SNTP_SUPPORT_MULTIPLE_SERVERS
+/** The currently used server (initialized to 0) */
+static u8_t sntp_current_server;
+#else /* SNTP_SUPPORT_MULTIPLE_SERVERS */
+#define sntp_current_server 0
+#endif /* SNTP_SUPPORT_MULTIPLE_SERVERS */
+
+#if SNTP_RETRY_TIMEOUT_EXP
+#define SNTP_RESET_RETRY_TIMEOUT() sntp_retry_timeout = SNTP_RETRY_TIMEOUT
+/** Retry time, initialized with SNTP_RETRY_TIMEOUT and doubled with each retry. */
+static u32_t sntp_retry_timeout;
+#else /* SNTP_RETRY_TIMEOUT_EXP */
+#define SNTP_RESET_RETRY_TIMEOUT()
+#define sntp_retry_timeout SNTP_RETRY_TIMEOUT
+#endif /* SNTP_RETRY_TIMEOUT_EXP */
+
+#if SNTP_CHECK_RESPONSE >= 1
+/** Saves the last server address to compare with response */
+static ip_addr_t sntp_last_server_address;
+#endif /* SNTP_CHECK_RESPONSE >= 1 */
+
+#if SNTP_CHECK_RESPONSE >= 2
+/** Saves the last timestamp sent (which is sent back by the server)
+ * to compare against in response. Stored in network byte order. */
+static struct sntp_time sntp_last_timestamp_sent;
+#endif /* SNTP_CHECK_RESPONSE >= 2 */
+
+#if defined(LWIP_DEBUG) && !defined(sntp_format_time)
+/* Debug print helper. */
+static const char *
+sntp_format_time(s32_t sec)
+{
+ time_t ut;
+ ut = (u32_t)((u32_t)sec + DIFF_SEC_1970_2036);
+ return ctime(&ut);
+}
+#endif /* LWIP_DEBUG && !sntp_format_time */
+
+/**
+ * SNTP processing of received timestamp
+ */
+static void
+sntp_process(const struct sntp_timestamps *timestamps)
+{
+ s32_t sec;
+ u32_t frac;
+
+ sec = (s32_t)lwip_ntohl(timestamps->xmit.sec);
+ frac = lwip_ntohl(timestamps->xmit.frac);
+
+#if SNTP_COMP_ROUNDTRIP
+# if SNTP_CHECK_RESPONSE >= 2
+ if (timestamps->recv.sec != 0 || timestamps->recv.frac != 0)
+# endif
+ {
+ s32_t dest_sec;
+ u32_t dest_frac;
+ u32_t step_sec;
+
+ /* Get the destination time stamp, i.e. the current system time */
+ SNTP_GET_SYSTEM_TIME_NTP(dest_sec, dest_frac);
+
+ step_sec = (dest_sec < sec) ? ((u32_t)sec - (u32_t)dest_sec)
+ : ((u32_t)dest_sec - (u32_t)sec);
+ /* In order to avoid overflows, skip the compensation if the clock step
+ * is larger than about 34 years. */
+ if ((step_sec >> 30) == 0) {
+ s64_t t1, t2, t3, t4;
+
+ t4 = SNTP_SEC_FRAC_TO_S64(dest_sec, dest_frac);
+ t3 = SNTP_SEC_FRAC_TO_S64(sec, frac);
+ t1 = SNTP_TIMESTAMP_TO_S64(timestamps->orig);
+ t2 = SNTP_TIMESTAMP_TO_S64(timestamps->recv);
+ /* Clock offset calculation according to RFC 4330 */
+ t4 += ((t2 - t1) + (t3 - t4)) / 2;
+
+ sec = (s32_t)((u64_t)t4 >> 32);
+ frac = (u32_t)((u64_t)t4);
+ }
+ }
+#endif /* SNTP_COMP_ROUNDTRIP */
+
+ SNTP_SET_SYSTEM_TIME_NTP(sec, frac);
+ LWIP_UNUSED_ARG(frac); /* might be unused if only seconds are set */
+ LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_process: %s, %" U32_F " us\n",
+ sntp_format_time(sec), SNTP_FRAC_TO_US(frac)));
+}
+
+/**
+ * Initialize request struct to be sent to server.
+ */
+static void
+sntp_initialize_request(struct sntp_msg *req)
+{
+ memset(req, 0, SNTP_MSG_LEN);
+ req->li_vn_mode = SNTP_LI_NO_WARNING | SNTP_VERSION | SNTP_MODE_CLIENT;
+
+#if SNTP_CHECK_RESPONSE >= 2 || SNTP_COMP_ROUNDTRIP
+ {
+ s32_t secs;
+ u32_t sec, frac;
+ /* Get the transmit timestamp */
+ SNTP_GET_SYSTEM_TIME_NTP(secs, frac);
+ sec = lwip_htonl((u32_t)secs);
+ frac = lwip_htonl(frac);
+
+# if SNTP_CHECK_RESPONSE >= 2
+ sntp_last_timestamp_sent.sec = sec;
+ sntp_last_timestamp_sent.frac = frac;
+# endif
+ req->transmit_timestamp[0] = sec;
+ req->transmit_timestamp[1] = frac;
+ }
+#endif /* SNTP_CHECK_RESPONSE >= 2 || SNTP_COMP_ROUNDTRIP */
+}
+
+/**
+ * Retry: send a new request (and increase retry timeout).
+ *
+ * @param arg is unused (only necessary to conform to sys_timeout)
+ */
+static void
+sntp_retry(void *arg)
+{
+ LWIP_UNUSED_ARG(arg);
+
+ LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_retry: Next request will be sent in %"U32_F" ms\n",
+ sntp_retry_timeout));
+
+ /* set up a timer to send a retry and increase the retry delay */
+ sys_timeout(sntp_retry_timeout, sntp_request, NULL);
+
+#if SNTP_RETRY_TIMEOUT_EXP
+ {
+ u32_t new_retry_timeout;
+ /* increase the timeout for next retry */
+ new_retry_timeout = sntp_retry_timeout << 1;
+ /* limit to maximum timeout and prevent overflow */
+ if ((new_retry_timeout <= SNTP_RETRY_TIMEOUT_MAX) &&
+ (new_retry_timeout > sntp_retry_timeout)) {
+ sntp_retry_timeout = new_retry_timeout;
+ }
+ }
+#endif /* SNTP_RETRY_TIMEOUT_EXP */
+}
+
+#if SNTP_SUPPORT_MULTIPLE_SERVERS
+/**
+ * If Kiss-of-Death is received (or another packet parsing error),
+ * try the next server or retry the current server and increase the retry
+ * timeout if only one server is available.
+ * (implicitly, SNTP_MAX_SERVERS > 1)
+ *
+ * @param arg is unused (only necessary to conform to sys_timeout)
+ */
+static void
+sntp_try_next_server(void *arg)
+{
+ u8_t old_server, i;
+ LWIP_UNUSED_ARG(arg);
+
+ old_server = sntp_current_server;
+ for (i = 0; i < SNTP_MAX_SERVERS - 1; i++) {
+ sntp_current_server++;
+ if (sntp_current_server >= SNTP_MAX_SERVERS) {
+ sntp_current_server = 0;
+ }
+ if (!ip_addr_isany(&sntp_servers[sntp_current_server].addr)
+#if SNTP_SERVER_DNS
+ || (sntp_servers[sntp_current_server].name != NULL)
+#endif
+ ) {
+ LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_try_next_server: Sending request to server %"U16_F"\n",
+ (u16_t)sntp_current_server));
+ /* new server: reset retry timeout */
+ SNTP_RESET_RETRY_TIMEOUT();
+ /* instantly send a request to the next server */
+ sntp_request(NULL);
+ return;
+ }
+ }
+ /* no other valid server found */
+ sntp_current_server = old_server;
+ sntp_retry(NULL);
+}
+#else /* SNTP_SUPPORT_MULTIPLE_SERVERS */
+/* Always retry on error if only one server is supported */
+#define sntp_try_next_server sntp_retry
+#endif /* SNTP_SUPPORT_MULTIPLE_SERVERS */
+
+/** UDP recv callback for the sntp pcb */
+static void
+sntp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
+{
+ struct sntp_timestamps timestamps;
+ u8_t mode;
+ u8_t stratum;
+ err_t err;
+
+ LWIP_UNUSED_ARG(arg);
+ LWIP_UNUSED_ARG(pcb);
+
+ err = ERR_ARG;
+#if SNTP_CHECK_RESPONSE >= 1
+ /* check server address and port */
+ if (((sntp_opmode != SNTP_OPMODE_POLL) || ip_addr_cmp(addr, &sntp_last_server_address)) &&
+ (port == SNTP_PORT))
+#else /* SNTP_CHECK_RESPONSE >= 1 */
+ LWIP_UNUSED_ARG(addr);
+ LWIP_UNUSED_ARG(port);
+#endif /* SNTP_CHECK_RESPONSE >= 1 */
+ {
+ /* process the response */
+ if (p->tot_len == SNTP_MSG_LEN) {
+ mode = pbuf_get_at(p, SNTP_OFFSET_LI_VN_MODE) & SNTP_MODE_MASK;
+ /* if this is a SNTP response... */
+ if (((sntp_opmode == SNTP_OPMODE_POLL) && (mode == SNTP_MODE_SERVER)) ||
+ ((sntp_opmode == SNTP_OPMODE_LISTENONLY) && (mode == SNTP_MODE_BROADCAST))) {
+ stratum = pbuf_get_at(p, SNTP_OFFSET_STRATUM);
+
+ if (stratum == SNTP_STRATUM_KOD) {
+ /* Kiss-of-death packet. Use another server or increase UPDATE_DELAY. */
+ err = SNTP_ERR_KOD;
+ LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_recv: Received Kiss-of-Death\n"));
+ } else {
+ pbuf_copy_partial(p, ×tamps, sizeof(timestamps), SNTP_OFFSET_TIMESTAMPS);
+#if SNTP_CHECK_RESPONSE >= 2
+ /* check originate_timetamp against sntp_last_timestamp_sent */
+ if (timestamps.orig.sec != sntp_last_timestamp_sent.sec ||
+ timestamps.orig.frac != sntp_last_timestamp_sent.frac) {
+ LWIP_DEBUGF(SNTP_DEBUG_WARN,
+ ("sntp_recv: Invalid originate timestamp in response\n"));
+ } else
+#endif /* SNTP_CHECK_RESPONSE >= 2 */
+ /* @todo: add code for SNTP_CHECK_RESPONSE >= 3 and >= 4 here */
+ {
+ /* correct answer */
+ err = ERR_OK;
+ }
+ }
+ } else {
+ LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid mode in response: %"U16_F"\n", (u16_t)mode));
+ /* wait for correct response */
+ err = ERR_TIMEOUT;
+ }
+ } else {
+ LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid packet length: %"U16_F"\n", p->tot_len));
+ }
+ }
+#if SNTP_CHECK_RESPONSE >= 1
+ else {
+ /* packet from wrong remote address or port, wait for correct response */
+ err = ERR_TIMEOUT;
+ }
+#endif /* SNTP_CHECK_RESPONSE >= 1 */
+
+ pbuf_free(p);
+
+ if (err == ERR_OK) {
+ /* correct packet received: process it it */
+ sntp_process(×tamps);
+
+#if SNTP_MONITOR_SERVER_REACHABILITY
+ /* indicate that server responded */
+ sntp_servers[sntp_current_server].reachability |= 1;
+#endif /* SNTP_MONITOR_SERVER_REACHABILITY */
+ /* Set up timeout for next request (only if poll response was received)*/
+ if (sntp_opmode == SNTP_OPMODE_POLL) {
+ u32_t sntp_update_delay;
+ sys_untimeout(sntp_try_next_server, NULL);
+ sys_untimeout(sntp_request, NULL);
+
+ /* Correct response, reset retry timeout */
+ SNTP_RESET_RETRY_TIMEOUT();
+
+ sntp_update_delay = (u32_t)SNTP_UPDATE_DELAY;
+ sys_timeout(sntp_update_delay, sntp_request, NULL);
+ LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_recv: Scheduled next time request: %"U32_F" ms\n",
+ sntp_update_delay));
+ }
+ } else if (err == SNTP_ERR_KOD) {
+ /* KOD errors are only processed in case of an explicit poll response */
+ if (sntp_opmode == SNTP_OPMODE_POLL) {
+ /* Kiss-of-death packet. Use another server or increase UPDATE_DELAY. */
+ sntp_try_next_server(NULL);
+ }
+ } else {
+ /* ignore any broken packet, poll mode: retry after timeout to avoid flooding */
+ }
+}
+
+/** Actually send an sntp request to a server.
+ *
+ * @param server_addr resolved IP address of the SNTP server
+ */
+static void
+sntp_send_request(const ip_addr_t *server_addr)
+{
+ struct pbuf *p;
+
+ LWIP_ASSERT("server_addr != NULL", server_addr != NULL);
+
+ p = pbuf_alloc(PBUF_TRANSPORT, SNTP_MSG_LEN, PBUF_RAM);
+ if (p != NULL) {
+ struct sntp_msg *sntpmsg = (struct sntp_msg *)p->payload;
+ LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_send_request: Sending request to server\n"));
+ /* initialize request message */
+ sntp_initialize_request(sntpmsg);
+ /* send request */
+ udp_sendto(sntp_pcb, p, server_addr, SNTP_PORT);
+ /* free the pbuf after sending it */
+ pbuf_free(p);
+#if SNTP_MONITOR_SERVER_REACHABILITY
+ /* indicate new packet has been sent */
+ sntp_servers[sntp_current_server].reachability <<= 1;
+#endif /* SNTP_MONITOR_SERVER_REACHABILITY */
+ /* set up receive timeout: try next server or retry on timeout */
+ sys_timeout((u32_t)SNTP_RECV_TIMEOUT, sntp_try_next_server, NULL);
+#if SNTP_CHECK_RESPONSE >= 1
+ /* save server address to verify it in sntp_recv */
+ ip_addr_copy(sntp_last_server_address, *server_addr);
+#endif /* SNTP_CHECK_RESPONSE >= 1 */
+ } else {
+ LWIP_DEBUGF(SNTP_DEBUG_SERIOUS, ("sntp_send_request: Out of memory, trying again in %"U32_F" ms\n",
+ (u32_t)SNTP_RETRY_TIMEOUT));
+ /* out of memory: set up a timer to send a retry */
+ sys_timeout((u32_t)SNTP_RETRY_TIMEOUT, sntp_request, NULL);
+ }
+}
+
+#if SNTP_SERVER_DNS
+/**
+ * DNS found callback when using DNS names as server address.
+ */
+static void
+sntp_dns_found(const char *hostname, const ip_addr_t *ipaddr, void *arg)
+{
+ LWIP_UNUSED_ARG(hostname);
+ LWIP_UNUSED_ARG(arg);
+
+ if (ipaddr != NULL) {
+ /* Address resolved, send request */
+ LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_dns_found: Server address resolved, sending request\n"));
+ sntp_servers[sntp_current_server].addr = *ipaddr;
+ sntp_send_request(ipaddr);
+ } else {
+ /* DNS resolving failed -> try another server */
+ LWIP_DEBUGF(SNTP_DEBUG_WARN_STATE, ("sntp_dns_found: Failed to resolve server address resolved, trying next server\n"));
+ sntp_try_next_server(NULL);
+ }
+}
+#endif /* SNTP_SERVER_DNS */
+
+/**
+ * Send out an sntp request.
+ *
+ * @param arg is unused (only necessary to conform to sys_timeout)
+ */
+static void
+sntp_request(void *arg)
+{
+ ip_addr_t sntp_server_address;
+ err_t err;
+
+ LWIP_UNUSED_ARG(arg);
+
+ /* initialize SNTP server address */
+#if SNTP_SERVER_DNS
+ if (sntp_servers[sntp_current_server].name) {
+ /* always resolve the name and rely on dns-internal caching & timeout */
+ ip_addr_set_zero(&sntp_servers[sntp_current_server].addr);
+ err = dns_gethostbyname(sntp_servers[sntp_current_server].name, &sntp_server_address,
+ sntp_dns_found, NULL);
+ if (err == ERR_INPROGRESS) {
+ /* DNS request sent, wait for sntp_dns_found being called */
+ LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_request: Waiting for server address to be resolved.\n"));
+ return;
+ } else if (err == ERR_OK) {
+ sntp_servers[sntp_current_server].addr = sntp_server_address;
+ }
+ } else
+#endif /* SNTP_SERVER_DNS */
+ {
+ sntp_server_address = sntp_servers[sntp_current_server].addr;
+ err = (ip_addr_isany_val(sntp_server_address)) ? ERR_ARG : ERR_OK;
+ }
+
+ if (err == ERR_OK) {
+ LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_request: current server address is %s\n",
+ ipaddr_ntoa(&sntp_server_address)));
+ sntp_send_request(&sntp_server_address);
+ } else {
+ /* address conversion failed, try another server */
+ LWIP_DEBUGF(SNTP_DEBUG_WARN_STATE, ("sntp_request: Invalid server address, trying next server.\n"));
+ sys_timeout((u32_t)SNTP_RETRY_TIMEOUT, sntp_try_next_server, NULL);
+ }
+}
+
+/**
+ * @ingroup sntp
+ * Initialize this module.
+ * Send out request instantly or after SNTP_STARTUP_DELAY(_FUNC).
+ */
+void
+sntp_init(void)
+{
+ /* LWIP_ASSERT_CORE_LOCKED(); is checked by udp_new() */
+
+#ifdef SNTP_SERVER_ADDRESS
+#if SNTP_SERVER_DNS
+ sntp_setservername(0, SNTP_SERVER_ADDRESS);
+#else
+#error SNTP_SERVER_ADDRESS string not supported SNTP_SERVER_DNS==0
+#endif
+#endif /* SNTP_SERVER_ADDRESS */
+
+ if (sntp_pcb == NULL) {
+ sntp_pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
+ LWIP_ASSERT("Failed to allocate udp pcb for sntp client", sntp_pcb != NULL);
+ if (sntp_pcb != NULL) {
+ udp_recv(sntp_pcb, sntp_recv, NULL);
+
+ if (sntp_opmode == SNTP_OPMODE_POLL) {
+ SNTP_RESET_RETRY_TIMEOUT();
+#if SNTP_STARTUP_DELAY
+ sys_timeout((u32_t)SNTP_STARTUP_DELAY_FUNC, sntp_request, NULL);
+#else
+ sntp_request(NULL);
+#endif
+ } else if (sntp_opmode == SNTP_OPMODE_LISTENONLY) {
+ ip_set_option(sntp_pcb, SOF_BROADCAST);
+ udp_bind(sntp_pcb, IP_ANY_TYPE, SNTP_PORT);
+ }
+ }
+ }
+}
+
+/**
+ * @ingroup sntp
+ * Stop this module.
+ */
+void
+sntp_stop(void)
+{
+ LWIP_ASSERT_CORE_LOCKED();
+ if (sntp_pcb != NULL) {
+#if SNTP_MONITOR_SERVER_REACHABILITY
+ u8_t i;
+ for (i = 0; i < SNTP_MAX_SERVERS; i++) {
+ sntp_servers[i].reachability = 0;
+ }
+#endif /* SNTP_MONITOR_SERVER_REACHABILITY */
+ sys_untimeout(sntp_request, NULL);
+ sys_untimeout(sntp_try_next_server, NULL);
+ udp_remove(sntp_pcb);
+ sntp_pcb = NULL;
+ }
+}
+
+/**
+ * @ingroup sntp
+ * Get enabled state.
+ */
+u8_t sntp_enabled(void)
+{
+ return (sntp_pcb != NULL) ? 1 : 0;
+}
+
+/**
+ * @ingroup sntp
+ * Sets the operating mode.
+ * @param operating_mode one of the available operating modes
+ */
+void
+sntp_setoperatingmode(u8_t operating_mode)
+{
+ LWIP_ASSERT_CORE_LOCKED();
+ LWIP_ASSERT("Invalid operating mode", operating_mode <= SNTP_OPMODE_LISTENONLY);
+ LWIP_ASSERT("Operating mode must not be set while SNTP client is running", sntp_pcb == NULL);
+ sntp_opmode = operating_mode;
+}
+
+/**
+ * @ingroup sntp
+ * Gets the operating mode.
+ */
+u8_t
+sntp_getoperatingmode(void)
+{
+ return sntp_opmode;
+}
+
+#if SNTP_MONITOR_SERVER_REACHABILITY
+/**
+ * @ingroup sntp
+ * Gets the server reachability shift register as described in RFC 5905.
+ *
+ * @param idx the index of the NTP server
+ */
+u8_t
+sntp_getreachability(u8_t idx)
+{
+ if (idx < SNTP_MAX_SERVERS) {
+ return sntp_servers[idx].reachability;
+ }
+ return 0;
+}
+#endif /* SNTP_MONITOR_SERVER_REACHABILITY */
+
+#if SNTP_GET_SERVERS_FROM_DHCP
+/**
+ * Config SNTP server handling by IP address, name, or DHCP; clear table
+ * @param set_servers_from_dhcp enable or disable getting server addresses from dhcp
+ */
+void
+sntp_servermode_dhcp(int set_servers_from_dhcp)
+{
+ u8_t new_mode = set_servers_from_dhcp ? 1 : 0;
+ LWIP_ASSERT_CORE_LOCKED();
+ if (sntp_set_servers_from_dhcp != new_mode) {
+ sntp_set_servers_from_dhcp = new_mode;
+ }
+}
+#endif /* SNTP_GET_SERVERS_FROM_DHCP */
+
+/**
+ * @ingroup sntp
+ * Initialize one of the NTP servers by IP address
+ *
+ * @param idx the index of the NTP server to set must be < SNTP_MAX_SERVERS
+ * @param server IP address of the NTP server to set
+ */
+void
+sntp_setserver(u8_t idx, const ip_addr_t *server)
+{
+ LWIP_ASSERT_CORE_LOCKED();
+ if (idx < SNTP_MAX_SERVERS) {
+ if (server != NULL) {
+ sntp_servers[idx].addr = (*server);
+ } else {
+ ip_addr_set_zero(&sntp_servers[idx].addr);
+ }
+#if SNTP_SERVER_DNS
+ sntp_servers[idx].name = NULL;
+#endif
+ }
+}
+
+#if LWIP_DHCP && SNTP_GET_SERVERS_FROM_DHCP
+/**
+ * Initialize one of the NTP servers by IP address, required by DHCP
+ *
+ * @param num the index of the NTP server to set must be < SNTP_MAX_SERVERS
+ * @param server IP address of the NTP server to set
+ */
+void
+dhcp_set_ntp_servers(u8_t num, const ip4_addr_t *server)
+{
+ LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp: %s %u.%u.%u.%u as NTP server #%u via DHCP\n",
+ (sntp_set_servers_from_dhcp ? "Got" : "Rejected"),
+ ip4_addr1(server), ip4_addr2(server), ip4_addr3(server), ip4_addr4(server), num));
+ if (sntp_set_servers_from_dhcp && num) {
+ u8_t i;
+ for (i = 0; (i < num) && (i < SNTP_MAX_SERVERS); i++) {
+ ip_addr_t addr;
+ ip_addr_copy_from_ip4(addr, server[i]);
+ sntp_setserver(i, &addr);
+ }
+ for (i = num; i < SNTP_MAX_SERVERS; i++) {
+ sntp_setserver(i, NULL);
+ }
+ }
+}
+#endif /* LWIP_DHCP && SNTP_GET_SERVERS_FROM_DHCP */
+
+/**
+ * @ingroup sntp
+ * Obtain one of the currently configured by IP address (or DHCP) NTP servers
+ *
+ * @param idx the index of the NTP server
+ * @return IP address of the indexed NTP server or "ip_addr_any" if the NTP
+ * server has not been configured by address (or at all).
+ */
+const ip_addr_t *
+sntp_getserver(u8_t idx)
+{
+ if (idx < SNTP_MAX_SERVERS) {
+ return &sntp_servers[idx].addr;
+ }
+ return IP_ADDR_ANY;
+}
+
+#if SNTP_SERVER_DNS
+/**
+ * Initialize one of the NTP servers by name
+ *
+ * @param idx the index of the NTP server to set must be < SNTP_MAX_SERVERS
+ * @param server DNS name of the NTP server to set, to be resolved at contact time
+ */
+void
+sntp_setservername(u8_t idx, const char *server)
+{
+ LWIP_ASSERT_CORE_LOCKED();
+ if (idx < SNTP_MAX_SERVERS) {
+ sntp_servers[idx].name = server;
+ }
+}
+
+/**
+ * Obtain one of the currently configured by name NTP servers.
+ *
+ * @param idx the index of the NTP server
+ * @return IP address of the indexed NTP server or NULL if the NTP
+ * server has not been configured by name (or at all)
+ */
+const char *
+sntp_getservername(u8_t idx)
+{
+ if (idx < SNTP_MAX_SERVERS) {
+ return sntp_servers[idx].name;
+ }
+ return NULL;
+}
+#endif /* SNTP_SERVER_DNS */
+
+#endif /* LWIP_UDP */
diff --git a/resources/ethernet/LwIP/apps/tftp/tftp_server.c b/resources/ethernet/LwIP/apps/tftp/tftp_server.c
new file mode 100644
index 00000000..d6c75b4e
--- /dev/null
+++ b/resources/ethernet/LwIP/apps/tftp/tftp_server.c
@@ -0,0 +1,435 @@
+/**
+ *
+ * @file tftp_server.c
+ *
+ * @author Logan Gunthorpe
+ * Dirk Ziegelmeier
+ *
+ * @brief Trivial File Transfer Protocol (RFC 1350)
+ *
+ * Copyright (c) Deltatee Enterprises Ltd. 2013
+ * All rights reserved.
+ *
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification,are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Author: Logan Gunthorpe
+ * Dirk Ziegelmeier
+ *
+ */
+
+/**
+ * @defgroup tftp TFTP server
+ * @ingroup apps
+ *
+ * This is simple TFTP server for the lwIP raw API.
+ */
+
+#include "lwip/apps/tftp_server.h"
+
+#if LWIP_UDP
+
+#include "lwip/udp.h"
+#include "lwip/timeouts.h"
+#include "lwip/debug.h"
+
+#define TFTP_MAX_PAYLOAD_SIZE 512
+#define TFTP_HEADER_LENGTH 4
+
+#define TFTP_RRQ 1
+#define TFTP_WRQ 2
+#define TFTP_DATA 3
+#define TFTP_ACK 4
+#define TFTP_ERROR 5
+
+enum tftp_error {
+ TFTP_ERROR_FILE_NOT_FOUND = 1,
+ TFTP_ERROR_ACCESS_VIOLATION = 2,
+ TFTP_ERROR_DISK_FULL = 3,
+ TFTP_ERROR_ILLEGAL_OPERATION = 4,
+ TFTP_ERROR_UNKNOWN_TRFR_ID = 5,
+ TFTP_ERROR_FILE_EXISTS = 6,
+ TFTP_ERROR_NO_SUCH_USER = 7
+};
+
+#include
+
+struct tftp_state {
+ const struct tftp_context *ctx;
+ void *handle;
+ struct pbuf *last_data;
+ struct udp_pcb *upcb;
+ ip_addr_t addr;
+ u16_t port;
+ int timer;
+ int last_pkt;
+ u16_t blknum;
+ u8_t retries;
+ u8_t mode_write;
+};
+
+static struct tftp_state tftp_state;
+
+static void tftp_tmr(void *arg);
+
+static void
+close_handle(void)
+{
+ tftp_state.port = 0;
+ ip_addr_set_any(0, &tftp_state.addr);
+
+ if (tftp_state.last_data != NULL) {
+ pbuf_free(tftp_state.last_data);
+ tftp_state.last_data = NULL;
+ }
+
+ sys_untimeout(tftp_tmr, NULL);
+
+ if (tftp_state.handle) {
+ tftp_state.ctx->close(tftp_state.handle);
+ tftp_state.handle = NULL;
+ LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: closing\n"));
+ }
+}
+
+static void
+send_error(const ip_addr_t *addr, u16_t port, enum tftp_error code, const char *str)
+{
+ int str_length = strlen(str);
+ struct pbuf *p;
+ u16_t *payload;
+
+ p = pbuf_alloc(PBUF_TRANSPORT, (u16_t)(TFTP_HEADER_LENGTH + str_length + 1), PBUF_RAM);
+ if (p == NULL) {
+ return;
+ }
+
+ payload = (u16_t *) p->payload;
+ payload[0] = PP_HTONS(TFTP_ERROR);
+ payload[1] = lwip_htons(code);
+ MEMCPY(&payload[2], str, str_length + 1);
+
+ udp_sendto(tftp_state.upcb, p, addr, port);
+ pbuf_free(p);
+}
+
+static void
+send_ack(u16_t blknum)
+{
+ struct pbuf *p;
+ u16_t *payload;
+
+ p = pbuf_alloc(PBUF_TRANSPORT, TFTP_HEADER_LENGTH, PBUF_RAM);
+ if (p == NULL) {
+ return;
+ }
+ payload = (u16_t *) p->payload;
+
+ payload[0] = PP_HTONS(TFTP_ACK);
+ payload[1] = lwip_htons(blknum);
+ udp_sendto(tftp_state.upcb, p, &tftp_state.addr, tftp_state.port);
+ pbuf_free(p);
+}
+
+static void
+resend_data(void)
+{
+ struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, tftp_state.last_data->len, PBUF_RAM);
+ if (p == NULL) {
+ return;
+ }
+
+ if (pbuf_copy(p, tftp_state.last_data) != ERR_OK) {
+ pbuf_free(p);
+ return;
+ }
+
+ udp_sendto(tftp_state.upcb, p, &tftp_state.addr, tftp_state.port);
+ pbuf_free(p);
+}
+
+static void
+send_data(void)
+{
+ u16_t *payload;
+ int ret;
+
+ if (tftp_state.last_data != NULL) {
+ pbuf_free(tftp_state.last_data);
+ }
+
+ tftp_state.last_data = pbuf_alloc(PBUF_TRANSPORT, TFTP_HEADER_LENGTH + TFTP_MAX_PAYLOAD_SIZE, PBUF_RAM);
+ if (tftp_state.last_data == NULL) {
+ return;
+ }
+
+ payload = (u16_t *) tftp_state.last_data->payload;
+ payload[0] = PP_HTONS(TFTP_DATA);
+ payload[1] = lwip_htons(tftp_state.blknum);
+
+ ret = tftp_state.ctx->read(tftp_state.handle, &payload[2], TFTP_MAX_PAYLOAD_SIZE);
+ if (ret < 0) {
+ send_error(&tftp_state.addr, tftp_state.port, TFTP_ERROR_ACCESS_VIOLATION, "Error occured while reading the file.");
+ close_handle();
+ return;
+ }
+
+ pbuf_realloc(tftp_state.last_data, (u16_t)(TFTP_HEADER_LENGTH + ret));
+ resend_data();
+}
+
+static void
+recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
+{
+ u16_t *sbuf = (u16_t *) p->payload;
+ int opcode;
+
+ LWIP_UNUSED_ARG(arg);
+ LWIP_UNUSED_ARG(upcb);
+
+ if (((tftp_state.port != 0) && (port != tftp_state.port)) ||
+ (!ip_addr_isany_val(tftp_state.addr) && !ip_addr_cmp(&tftp_state.addr, addr))) {
+ send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Only one connection at a time is supported");
+ pbuf_free(p);
+ return;
+ }
+
+ opcode = sbuf[0];
+
+ tftp_state.last_pkt = tftp_state.timer;
+ tftp_state.retries = 0;
+
+ switch (opcode) {
+ case PP_HTONS(TFTP_RRQ): /* fall through */
+ case PP_HTONS(TFTP_WRQ): {
+ const char tftp_null = 0;
+ char filename[TFTP_MAX_FILENAME_LEN + 1];
+ char mode[TFTP_MAX_MODE_LEN + 1];
+ u16_t filename_end_offset;
+ u16_t mode_end_offset;
+
+ if (tftp_state.handle != NULL) {
+ send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Only one connection at a time is supported");
+ break;
+ }
+
+ sys_timeout(TFTP_TIMER_MSECS, tftp_tmr, NULL);
+
+ /* find \0 in pbuf -> end of filename string */
+ filename_end_offset = pbuf_memfind(p, &tftp_null, sizeof(tftp_null), 2);
+ if ((u16_t)(filename_end_offset - 1) > sizeof(filename)) {
+ send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Filename too long/not NULL terminated");
+ break;
+ }
+ pbuf_copy_partial(p, filename, filename_end_offset - 1, 2);
+
+ /* find \0 in pbuf -> end of mode string */
+ mode_end_offset = pbuf_memfind(p, &tftp_null, sizeof(tftp_null), filename_end_offset + 1);
+ if ((u16_t)(mode_end_offset - filename_end_offset) > sizeof(mode)) {
+ send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Mode too long/not NULL terminated");
+ break;
+ }
+ pbuf_copy_partial(p, mode, mode_end_offset - filename_end_offset, filename_end_offset + 1);
+
+ tftp_state.handle = tftp_state.ctx->open(filename, mode, opcode == PP_HTONS(TFTP_WRQ));
+ tftp_state.blknum = 1;
+
+ if (!tftp_state.handle) {
+ send_error(addr, port, TFTP_ERROR_FILE_NOT_FOUND, "Unable to open requested file.");
+ break;
+ }
+
+ LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: %s request from ", (opcode == PP_HTONS(TFTP_WRQ)) ? "write" : "read"));
+ ip_addr_debug_print(TFTP_DEBUG | LWIP_DBG_STATE, addr);
+ LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, (" for '%s' mode '%s'\n", filename, mode));
+
+ ip_addr_copy(tftp_state.addr, *addr);
+ tftp_state.port = port;
+
+ if (opcode == PP_HTONS(TFTP_WRQ)) {
+ tftp_state.mode_write = 1;
+ send_ack(0);
+ } else {
+ tftp_state.mode_write = 0;
+ send_data();
+ }
+
+ break;
+ }
+
+ case PP_HTONS(TFTP_DATA): {
+ int ret;
+ u16_t blknum;
+
+ if (tftp_state.handle == NULL) {
+ send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "No connection");
+ break;
+ }
+
+ if (tftp_state.mode_write != 1) {
+ send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Not a write connection");
+ break;
+ }
+
+ blknum = lwip_ntohs(sbuf[1]);
+ if (blknum == tftp_state.blknum) {
+ pbuf_remove_header(p, TFTP_HEADER_LENGTH);
+
+ ret = tftp_state.ctx->write(tftp_state.handle, p);
+ if (ret < 0) {
+ send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "error writing file");
+ close_handle();
+ } else {
+ send_ack(blknum);
+ }
+
+ if (p->tot_len < TFTP_MAX_PAYLOAD_SIZE) {
+ close_handle();
+ } else {
+ tftp_state.blknum++;
+ }
+ } else if ((u16_t)(blknum + 1) == tftp_state.blknum) {
+ /* retransmit of previous block, ack again (casting to u16_t to care for overflow) */
+ send_ack(blknum);
+ } else {
+ send_error(addr, port, TFTP_ERROR_UNKNOWN_TRFR_ID, "Wrong block number");
+ }
+ break;
+ }
+
+ case PP_HTONS(TFTP_ACK): {
+ u16_t blknum;
+ int lastpkt;
+
+ if (tftp_state.handle == NULL) {
+ send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "No connection");
+ break;
+ }
+
+ if (tftp_state.mode_write != 0) {
+ send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Not a read connection");
+ break;
+ }
+
+ blknum = lwip_ntohs(sbuf[1]);
+ if (blknum != tftp_state.blknum) {
+ send_error(addr, port, TFTP_ERROR_UNKNOWN_TRFR_ID, "Wrong block number");
+ break;
+ }
+
+ lastpkt = 0;
+
+ if (tftp_state.last_data != NULL) {
+ lastpkt = tftp_state.last_data->tot_len != (TFTP_MAX_PAYLOAD_SIZE + TFTP_HEADER_LENGTH);
+ }
+
+ if (!lastpkt) {
+ tftp_state.blknum++;
+ send_data();
+ } else {
+ close_handle();
+ }
+
+ break;
+ }
+
+ default:
+ send_error(addr, port, TFTP_ERROR_ILLEGAL_OPERATION, "Unknown operation");
+ break;
+ }
+
+ pbuf_free(p);
+}
+
+static void
+tftp_tmr(void *arg)
+{
+ LWIP_UNUSED_ARG(arg);
+
+ tftp_state.timer++;
+
+ if (tftp_state.handle == NULL) {
+ return;
+ }
+
+ sys_timeout(TFTP_TIMER_MSECS, tftp_tmr, NULL);
+
+ if ((tftp_state.timer - tftp_state.last_pkt) > (TFTP_TIMEOUT_MSECS / TFTP_TIMER_MSECS)) {
+ if ((tftp_state.last_data != NULL) && (tftp_state.retries < TFTP_MAX_RETRIES)) {
+ LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: timeout, retrying\n"));
+ resend_data();
+ tftp_state.retries++;
+ } else {
+ LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: timeout\n"));
+ close_handle();
+ }
+ }
+}
+
+/** @ingroup tftp
+ * Initialize TFTP server.
+ * @param ctx TFTP callback struct
+ */
+err_t
+tftp_init(const struct tftp_context *ctx)
+{
+ err_t ret;
+
+ /* LWIP_ASSERT_CORE_LOCKED(); is checked by udp_new() */
+ struct udp_pcb *pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
+ if (pcb == NULL) {
+ return ERR_MEM;
+ }
+
+ ret = udp_bind(pcb, IP_ANY_TYPE, TFTP_PORT);
+ if (ret != ERR_OK) {
+ udp_remove(pcb);
+ return ret;
+ }
+
+ tftp_state.handle = NULL;
+ tftp_state.port = 0;
+ tftp_state.ctx = ctx;
+ tftp_state.timer = 0;
+ tftp_state.last_data = NULL;
+ tftp_state.upcb = pcb;
+
+ udp_recv(pcb, recv, NULL);
+
+ return ERR_OK;
+}
+
+/** @ingroup tftp
+ * Deinitialize ("turn off") TFTP server.
+ */
+void tftp_cleanup(void)
+{
+ LWIP_ASSERT("Cleanup called on non-initialized TFTP", tftp_state.upcb != NULL);
+ udp_remove(tftp_state.upcb);
+ close_handle();
+ memset(&tftp_state, 0, sizeof(tftp_state));
+}
+
+#endif /* LWIP_UDP */
diff --git a/resources/ethernet/LwIP/arch/Makefile b/resources/ethernet/LwIP/arch/Makefile
new file mode 100644
index 00000000..1bff7aae
--- /dev/null
+++ b/resources/ethernet/LwIP/arch/Makefile
@@ -0,0 +1,5 @@
+SRC_FILES += sys_arch.c \
+ tcp_echo_socket_demo.c \
+ udp_echo.c
+
+include $(KERNEL_ROOT)/compiler.mk
diff --git a/resources/ethernet/LwIP/arch/cc.h b/resources/ethernet/LwIP/arch/cc.h
new file mode 100644
index 00000000..1a241967
--- /dev/null
+++ b/resources/ethernet/LwIP/arch/cc.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ */
+#ifndef __CC_H__
+#define __CC_H__
+
+#include "stdio.h"
+
+#include "main.h"
+
+//typedef unsigned char u8_t;
+//typedef signed char s8_t;
+//typedef unsigned short u16_t;
+//typedef signed short s16_t;
+//typedef unsigned long u32_t;
+//typedef signed long s32_t;
+//typedef u32_t mem_ptr_t;
+//typedef int sys_prot_t;
+
+
+#define U16_F "hu"
+#define S16_F "d"
+#define X16_F "hx"
+#define U32_F "u"
+#define S32_F "d"
+#define X32_F "x"
+#define SZT_F "uz"
+
+
+
+
+
+/* define compiler specific symbols */
+#if defined (__ICCARM__)
+
+#define PACK_STRUCT_BEGIN
+#define PACK_STRUCT_STRUCT
+#define PACK_STRUCT_END
+#define PACK_STRUCT_FIELD(x) x
+#define PACK_STRUCT_USE_INCLUDES
+
+#elif defined (__CC_ARM)
+
+#define PACK_STRUCT_BEGIN __packed
+#define PACK_STRUCT_STRUCT
+#define PACK_STRUCT_END
+#define PACK_STRUCT_FIELD(x) x
+
+#elif defined (__GNUC__)
+
+#define PACK_STRUCT_BEGIN
+#define PACK_STRUCT_STRUCT __attribute__ ((__packed__))
+#define PACK_STRUCT_END
+#define PACK_STRUCT_FIELD(x) x
+
+#elif defined (__TASKING__)
+
+#define PACK_STRUCT_BEGIN
+#define PACK_STRUCT_STRUCT
+#define PACK_STRUCT_END
+#define PACK_STRUCT_FIELD(x) x
+
+#endif
+
+
+#define LWIP_PLATFORM_ASSERT(x) do {printf(x);}while(0)
+
+#endif /* __CC_H__ */
diff --git a/resources/ethernet/LwIP/arch/ethernetif.h b/resources/ethernet/LwIP/arch/ethernetif.h
new file mode 100644
index 00000000..e4733e3a
--- /dev/null
+++ b/resources/ethernet/LwIP/arch/ethernetif.h
@@ -0,0 +1,86 @@
+/**
+ ******************************************************************************
+ * File Name : ethernetif.h
+ * Description : This file provides initialization code for LWIP
+ * middleWare.
+ ******************************************************************************
+ * This notice applies to any and all portions of this file
+ * that are not between comment pairs USER CODE BEGIN and
+ * USER CODE END. Other portions of this file, whether
+ * inserted by the user or by software development tools
+ * are owned by their respective copyright owners.
+ *
+ * Copyright (c) 2018 STMicroelectronics International N.V.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted, provided that the following conditions are met:
+ *
+ * 1. Redistribution of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of STMicroelectronics nor the names of other
+ * contributors to this software may be used to endorse or promote products
+ * derived from this software without specific written permission.
+ * 4. This software, including modifications and/or derivative works of this
+ * software, must execute solely and exclusively on microcontroller or
+ * microprocessor devices manufactured by or for STMicroelectronics.
+ * 5. Redistribution and use of this software other than as permitted under
+ * this license is void and will automatically terminate your rights under
+ * this license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
+ * RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
+ * SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ ******************************************************************************
+ */
+
+
+#ifndef __ETHERNETIF_H__
+#define __ETHERNETIF_H__
+
+#include "lwip/err.h"
+#include "lwip/netif.h"
+
+/* Within 'USER CODE' section, code will be kept by default at each generation */
+/* USER CODE BEGIN 0 */
+#define NETIF_MTU ( 1500 )
+
+#define NETIF_IN_TASK_STACK_SIZE ( 1024 )
+#define NETIF_IN_TASK_PRIORITY ( 3 )
+
+#define NETIF_OUT_TASK_STACK_SIZE ( 1024 )
+#define NETIF_OUT_TASK_PRIORITY ( 3 )
+
+/* USER CODE END 0 */
+
+
+/* Exported functions ------------------------------------------------------- */
+err_t ethernetif_init(struct netif *netif);
+
+void ethernetif_input( void *argument );
+void ethernetif_output( void *argument );
+void ethernetif_update_config(struct netif *netif);
+void ethernetif_notify_conn_changed(struct netif *netif);
+
+u32_t sys_jiffies(void);
+u32_t sys_now(void);
+
+/* USER CODE BEGIN 1 */
+
+/* USER CODE END 1 */
+#endif
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/resources/ethernet/LwIP/arch/lwipopts.h b/resources/ethernet/LwIP/arch/lwipopts.h
new file mode 100644
index 00000000..936446c3
--- /dev/null
+++ b/resources/ethernet/LwIP/arch/lwipopts.h
@@ -0,0 +1,499 @@
+/**
+ ******************************************************************************
+ * @file lwipopts.h
+ * @author MCD Application Team
+ * @version V1.1.0
+ * @date 31-July-2013
+ * @brief lwIP Options Configuration.
+ * This file is based on Utilities\lwip_v1.4.1\src\include\lwip\opt.h
+ * and contains the lwIP configuration for the STM32F4x7 demonstration.
+ ******************************************************************************
+ * @attention
+ *
+ * © COPYRIGHT 2013 STMicroelectronics
+ *
+ * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.st.com/software_license_agreement_liberty_v2
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************
+ */
+
+#ifndef __LWIPOPTS_H__
+#define __LWIPOPTS_H__
+
+/**
+ * SYS_LIGHTWEIGHT_PROT==1: if you want inter-task protection for certain
+ * critical regions during buffer allocation, deallocation and memory
+ * allocation and deallocation.
+ */
+#define SYS_LIGHTWEIGHT_PROT 1
+
+/**
+ * NO_SYS==1: Provides VERY minimal functionality. Otherwise,
+ * use lwIP facilities.
+ */
+#define NO_SYS 0
+
+/**
+ * NO_SYS_NO_TIMERS==1: Drop support for sys_timeout when NO_SYS==1
+ * Mainly for compatibility to old versions.
+ */
+#define NO_SYS_NO_TIMERS 0
+
+/* ---------- Memory options ---------- */
+/* MEM_ALIGNMENT: should be set to the alignment of the CPU for which
+ lwIP is compiled. 4 byte alignment -> define MEM_ALIGNMENT to 4, 2
+ byte alignment -> define MEM_ALIGNMENT to 2. */
+#define MEM_ALIGNMENT 4
+
+/* MEM_SIZE: the size of the heap memory. If the application will send
+a lot of data that needs to be copied, this should be set high. */
+#define MEM_SIZE (25*1024)
+
+/* MEMP_NUM_PBUF: the number of memp struct pbufs. If the application
+ sends a lot of data out of ROM (or other static memory), this
+ should be set high. */
+#define MEMP_NUM_PBUF 15
+/* MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One
+ per active UDP "connection". */
+#define MEMP_NUM_UDP_PCB 4
+/* MEMP_NUM_TCP_PCB: the number of simulatenously active TCP
+ connections. */
+#define MEMP_NUM_TCP_PCB 4
+/* MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP
+ connections. */
+#define MEMP_NUM_TCP_PCB_LISTEN 2
+/* MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP
+ segments. */
+#define MEMP_NUM_TCP_SEG 120
+/* MEMP_NUM_SYS_TIMEOUT: the number of simulateously active
+ timeouts. */
+#define MEMP_NUM_SYS_TIMEOUT 6
+
+
+/* ---------- Pbuf options ---------- */
+/* PBUF_POOL_SIZE: the number of buffers in the pbuf pool. */
+#define PBUF_POOL_SIZE 20
+/* PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. */
+#define PBUF_POOL_BUFSIZE LWIP_MEM_ALIGN_SIZE(TCP_MSS+40+PBUF_LINK_ENCAPSULATION_HLEN+PBUF_LINK_HLEN)
+
+
+/* ---------- TCP options ---------- */
+#define LWIP_TCP 1
+#define TCP_TTL 255
+
+/* Controls if TCP should queue segments that arrive out of
+ order. Define to 0 if your device is low on memory. */
+#define TCP_QUEUE_OOSEQ 0
+
+/* TCP Maximum segment size. */
+#define TCP_MSS (1500 - 40) /* TCP_MSS = (Ethernet MTU - IP header size - TCP header size) */
+
+/* TCP sender buffer space (bytes). */
+#define TCP_SND_BUF (11*TCP_MSS)
+
+/* TCP_SND_QUEUELEN: TCP sender buffer space (pbufs). This must be at least
+ as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. */
+
+#define TCP_SND_QUEUELEN (8* TCP_SND_BUF/TCP_MSS)
+
+/* TCP receive window. */
+#define TCP_WND 16000//(12*TCP_MSS)
+
+
+/* ---------- ICMP options ---------- */
+#define LWIP_ICMP 1
+
+
+/* ---------- DHCP options ---------- */
+/* Define LWIP_DHCP to 1 if you want DHCP configuration of
+ interfaces. DHCP is not implemented in lwIP 0.5.1, however, so
+ turning this on does currently not work. */
+#define LWIP_DHCP 0
+
+
+/* ---------- UDP options ---------- */
+#define LWIP_UDP 1
+#define UDP_TTL 255
+
+
+/* ---------- Statistics options ---------- */
+#define LWIP_STATS 0
+#define LWIP_PROVIDE_ERRNO 1
+
+/* ---------- link callback options ---------- */
+/* LWIP_NETIF_LINK_CALLBACK==1: Support a callback function from an interface
+ * whenever the link changes (i.e., link down)
+ */
+#define LWIP_NETIF_LINK_CALLBACK 0
+/*
+ --------------------------------------
+ ---------- Checksum options ----------
+ --------------------------------------
+*/
+
+/*
+The STM32F4x7 allows computing and verifying the IP, UDP, TCP and ICMP checksums by hardware:
+ - To use this feature let the following define uncommented.
+ - To disable it and process by CPU comment the the checksum.
+*/
+#define CHECKSUM_BY_HARDWARE
+
+
+#ifdef CHECKSUM_BY_HARDWARE
+ /* CHECKSUM_GEN_IP==0: Generate checksums by hardware for outgoing IP packets.*/
+ #define CHECKSUM_GEN_IP 0
+ /* CHECKSUM_GEN_UDP==0: Generate checksums by hardware for outgoing UDP packets.*/
+ #define CHECKSUM_GEN_UDP 0
+ /* CHECKSUM_GEN_TCP==0: Generate checksums by hardware for outgoing TCP packets.*/
+ #define CHECKSUM_GEN_TCP 0
+ /* CHECKSUM_CHECK_IP==0: Check checksums by hardware for incoming IP packets.*/
+ #define CHECKSUM_CHECK_IP 0
+ /* CHECKSUM_CHECK_UDP==0: Check checksums by hardware for incoming UDP packets.*/
+ #define CHECKSUM_CHECK_UDP 0
+ /* CHECKSUM_CHECK_TCP==0: Check checksums by hardware for incoming TCP packets.*/
+ #define CHECKSUM_CHECK_TCP 0
+ /* CHECKSUM_CHECK_ICMP==0: Check checksums by hardware for incoming ICMP packets.*/
+ #define CHECKSUM_GEN_ICMP 0
+#else
+ /* CHECKSUM_GEN_IP==1: Generate checksums in software for outgoing IP packets.*/
+ #define CHECKSUM_GEN_IP 1
+ /* CHECKSUM_GEN_UDP==1: Generate checksums in software for outgoing UDP packets.*/
+ #define CHECKSUM_GEN_UDP 1
+ /* CHECKSUM_GEN_TCP==1: Generate checksums in software for outgoing TCP packets.*/
+ #define CHECKSUM_GEN_TCP 1
+ /* CHECKSUM_CHECK_IP==1: Check checksums in software for incoming IP packets.*/
+ #define CHECKSUM_CHECK_IP 1
+ /* CHECKSUM_CHECK_UDP==1: Check checksums in software for incoming UDP packets.*/
+ #define CHECKSUM_CHECK_UDP 1
+ /* CHECKSUM_CHECK_TCP==1: Check checksums in software for incoming TCP packets.*/
+ #define CHECKSUM_CHECK_TCP 1
+ /* CHECKSUM_CHECK_ICMP==1: Check checksums by hardware for incoming ICMP packets.*/
+ #define CHECKSUM_GEN_ICMP 1
+#endif
+
+
+/*
+ ----------------------------------------------
+ ---------- Sequential layer options ----------
+ ----------------------------------------------
+*/
+/**
+ * LWIP_NETCONN==1: Enable Netconn API (require to use api_lib.c)
+ */
+#define LWIP_NETCONN 1
+
+/*
+ ------------------------------------
+ ---------- Socket options ----------
+ ------------------------------------
+*/
+/**
+ * LWIP_SOCKET==1: Enable Socket API (require to use sockets.c)
+ */
+#define LWIP_SOCKET 1
+
+/*
+ ---------------------------------
+ ---------- OS options ----------
+ ---------------------------------
+*/
+
+
+#define DEFAULT_UDP_RECVMBOX_SIZE 10
+#define DEFAULT_TCP_RECVMBOX_SIZE 10
+#define DEFAULT_ACCEPTMBOX_SIZE 10
+#define DEFAULT_THREAD_STACKSIZE 1024
+
+
+#define TCPIP_THREAD_NAME "lwip"
+#define TCPIP_THREAD_STACKSIZE 2048
+#define TCPIP_MBOX_SIZE 8
+#define TCPIP_THREAD_PRIO 3
+
+//#define IPERF_SERVER_THREAD_NAME "iperf_server"
+//#define IPERF_SERVER_THREAD_STACKSIZE 1024
+//#define IPERF_SERVER_THREAD_PRIO 0
+
+//#define BLOCK_TIME 250
+//#define BLOCK_TIME_WAITING_FOR_INPUT ( ( portTickType ) 100 )
+
+/*
+ ----------------------------------------
+ ---------- Lwip Debug options ----------
+ ----------------------------------------
+*/
+//#define LWIP_DEBUG 1
+
+#endif /* __LWIPOPTS_H__ */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
+
+
+
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ */
+//#ifndef __LWIPOPTS_H__
+//#define __LWIPOPTS_H__
+
+//#define TCPIP_MBOX_SIZE 6
+
+//#define TCPIP_THREAD_NAME "tcp/ip"
+//#define TCPIP_THREAD_STACKSIZE 1500
+//#define TCPIP_THREAD_PRIO 3
+
+//#define DEFAULT_THREAD_STACKSIZE 1500
+//#define DEFAULT_THREAD_PRIO 3
+
+//#define ETH_PAD_SIZE 2
+//#define LWIP_TCPIP_CORE_LOCKING 0 ///TODO Initialize mutex
+
+////#define LWIP_DEBUG
+////#define DBG_TYPES_ON 0xff
+
+//#define ETHARP_DEBUG LWIP_DBG_OFF
+//#define NETIF_DEBUG LWIP_DBG_ON
+//#define PBUF_DEBUG LWIP_DBG_OFF
+//#define API_LIB_DEBUG LWIP_DBG_OFF
+//#define API_MSG_DEBUG LWIP_DBG_OFF
+//#define SOCKETS_DEBUG LWIP_DBG_OFF
+//#define ICMP_DEBUG LWIP_DBG_OFF
+//#define IGMP_DEBUG LWIP_DBG_OFF
+//#define INET_DEBUG LWIP_DBG_OFF
+//#define IP_DEBUG LWIP_DBG_OFF
+//#define IP_REASS_DEBUG LWIP_DBG_OFF
+//#define RAW_DEBUG LWIP_DBG_OFF
+//#define MEM_DEBUG LWIP_DBG_OFF
+//#define MEMP_DEBUG LWIP_DBG_OFF
+//#define SYS_DEBUG LWIP_DBG_OFF
+//#define TCP_DEBUG LWIP_DBG_OFF
+//#define TCP_INPUT_DEBUG LWIP_DBG_OFF
+//#define TCP_FR_DEBUG LWIP_DBG_OFF
+//#define TCP_RTO_DEBUG LWIP_DBG_OFF
+//#define TCP_CWND_DEBUG LWIP_DBG_OFF
+//#define TCP_WND_DEBUG LWIP_DBG_OFF
+//#define TCP_OUTPUT_DEBUG LWIP_DBG_OFF
+//#define TCP_RST_DEBUG LWIP_DBG_OFF
+//#define TCP_QLEN_DEBUG LWIP_DBG_OFF
+//#define UDP_DEBUG LWIP_DBG_OFF
+//#define TCPIP_DEBUG LWIP_DBG_OFF
+//#define PPP_DEBUG LWIP_DBG_OFF
+//#define SLIP_DEBUG LWIP_DBG_OFF
+//#define DHCP_DEBUG LWIP_DBG_OFF
+//#define AUTOIP_DEBUG LWIP_DBG_OFF
+//#define SNMP_MSG_DEBUG LWIP_DBG_OFF
+//#define SNMP_MIB_DEBUG LWIP_DBG_OFF
+//#define DNS_DEBUG LWIP_DBG_OFF
+//#define LWIP_NOASSERT 0
+
+///**
+// * SYS_LIGHTWEIGHT_PROT==1: if you want inter-task protection for certain
+// * critical regions during buffer allocation, deallocation and memory
+// * allocation and deallocation.
+// */
+//#define SYS_LIGHTWEIGHT_PROT 1
+
+///*
+// ------------------------------------
+// ---------- Memory options ----------
+// ------------------------------------
+//*/
+
+///**
+// * MEM_ALIGNMENT: should be set to the alignment of the CPU
+// * 4 byte alignment -> #define MEM_ALIGNMENT 4
+// * 2 byte alignment -> #define MEM_ALIGNMENT 2
+// */
+//#define MEM_ALIGNMENT 4
+
+///** STF (9*1024)
+// * MEM_SIZE: the size of the heap memory. If the application will send
+// * a lot of data that needs to be copied, this should be set high.
+// */
+//#define MEM_SIZE (8*1024)
+
+///*
+// ------------------------------------------------
+// ---------- Internal Memory Pool Sizes ----------
+// ------------------------------------------------
+//*/
+///** STF 40
+// * MEMP_NUM_PBUF: the number of memp struct pbufs (used for PBUF_ROM and PBUF_REF).
+// * If the application sends a lot of data out of ROM (or other static memory),
+// * this should be set high.
+// */
+//#define MEMP_NUM_PBUF 20
+
+///** STF 8
+// * MEMP_NUM_TCP_PCB: the number of simulatenously active TCP connections.
+// * (requires the LWIP_TCP option)
+// */
+//#define MEMP_NUM_TCP_PCB 10
+
+///**
+// * MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP segments.
+// * (requires the LWIP_TCP option)
+// */
+//#define MEMP_NUM_TCP_SEG 8
+
+///** STF 3
+// * MEMP_NUM_SYS_TIMEOUT: the number of simulateously active timeouts.
+// * (requires NO_SYS==0)
+// */
+//#define MEMP_NUM_SYS_TIMEOUT 5
+
+///**
+// * MEMP_NUM_NETBUF: the number of struct netbufs.
+// * (only needed if you use the sequential API, like api_lib.c)
+// */
+//#define MEMP_NUM_NETBUF 4
+
+///** STF 8
+// * PBUF_POOL_SIZE: the number of buffers in the pbuf pool.
+// */
+//#define PBUF_POOL_SIZE 4
+
+
+///*
+// ----------------------------------
+// ---------- Pbuf options ----------
+// ----------------------------------
+//*/
+
+///**
+// * PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. The default is
+// * designed to accomodate single full size TCP frame in one pbuf, including
+// * TCP_MSS, IP header, and link header.
+// */
+//#define PBUF_POOL_BUFSIZE 1500
+
+///*
+// ---------------------------------
+// ---------- TCP options ----------
+// ---------------------------------
+//*/
+///**
+// * LWIP_TCP==1: Turn on TCP.
+// */
+//#define LWIP_TCP 1
+
+///* TCP Maximum segment size. */
+//#define TCP_MSS 1500
+
+///* TCP sender buffer space (bytes). */
+//#define TCP_SND_BUF 2*TCP_MSS
+
+///**
+// * TCP_WND: The size of a TCP window.
+// */
+//#define TCP_WND 2*TCP_MSS //1500
+
+///**
+// * TCP_SYNMAXRTX: Maximum number of retransmissions of SYN segments.
+// */
+//#define TCP_SYNMAXRTX 4
+
+///*
+// ---------------------------------
+// ---------- RAW options ----------
+// ---------------------------------
+//*/
+///**
+// * LWIP_RAW==1: Enable application layer to hook into the IP layer itself.
+// */
+//#define LWIP_RAW 0
+
+///*
+// ----------------------------------------------
+// ---------- Sequential layer options ----------
+// ----------------------------------------------
+//*/
+///**
+// * LWIP_NETCONN==1: Enable Netconn API (require to use api_lib.c)
+// */
+//#define LWIP_NETCONN 1
+///*
+// ------------------------------------
+// ---------- Socket options ----------
+// ------------------------------------
+//*/
+///**
+// * LWIP_SOCKET==1: Enable Socket API (require to use sockets.c)
+// */
+//#define LWIP_SOCKET 1
+
+
+///*
+// ----------------------------------------
+// ---------- Statistics options ----------
+// ----------------------------------------
+//*/
+///**
+// * LWIP_STATS==1: Enable statistics collection in lwip_stats.
+// */
+//#define LWIP_STATS 0
+
+///*
+// ----------------------------------
+// ---------- DHCP options ----------
+// ----------------------------------
+//*/
+///**
+// * LWIP_DHCP==1: Enable DHCP module.
+// */
+//#define LWIP_DHCP 0
+
+///*
+// ----------------------------------
+// ---------- IGMP options ----------
+// ----------------------------------
+//*/
+///**
+// * LWIP_IGMP==1: Turn on IGMP module.
+// */
+//#define LWIP_IGMP 0
+
+
+//#define LWIP_PROVIDE_ERRNO 1
+
+////#define LWIP_SNMP 1
+
+//#endif /* __LWIPOPTS_H__ */
diff --git a/resources/ethernet/LwIP/arch/main.h b/resources/ethernet/LwIP/arch/main.h
new file mode 100644
index 00000000..1618279d
--- /dev/null
+++ b/resources/ethernet/LwIP/arch/main.h
@@ -0,0 +1,50 @@
+/**
+ ******************************************************************************
+ * @file GPIO/GPIO_EXTI/Inc/main.h
+ * @author MCD Application Team
+ * @brief Header for main.c module
+ ******************************************************************************
+ * @attention
+ *
+ * © COPYRIGHT(c) 2017 STMicroelectronics
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of STMicroelectronics nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ ******************************************************************************
+ */
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __MAIN_H
+#define __MAIN_H
+
+/* Includes ------------------------------------------------------------------*/
+
+// void BSP_Init(void);
+/* Exported types ------------------------------------------------------------*/
+/* Exported constants --------------------------------------------------------*/
+/* Exported macro ------------------------------------------------------------*/
+/* Exported functions ------------------------------------------------------- */
+
+#endif /* __MAIN_H */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/resources/ethernet/LwIP/arch/perf.h b/resources/ethernet/LwIP/arch/perf.h
new file mode 100644
index 00000000..68afdb56
--- /dev/null
+++ b/resources/ethernet/LwIP/arch/perf.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ */
+#ifndef __PERF_H__
+#define __PERF_H__
+
+#define PERF_START /* null definition */
+#define PERF_STOP(x) /* null definition */
+
+#endif /* __PERF_H__ */
diff --git a/resources/ethernet/LwIP/arch/sys_arch.c b/resources/ethernet/LwIP/arch/sys_arch.c
new file mode 100644
index 00000000..f7221daf
--- /dev/null
+++ b/resources/ethernet/LwIP/arch/sys_arch.c
@@ -0,0 +1,450 @@
+/*
+ * Copyright (c) 2017 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ *
+ */
+/*
+* Copyright (c) 2020 AIIT XUOS Lab
+* XiUOS is licensed under Mulan PSL v2.
+* You can use this software according to the terms and conditions of the Mulan PSL v2.
+* You may obtain a copy of Mulan PSL v2 at:
+* http://license.coscl.org.cn/MulanPSL2
+* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
+* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
+* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
+* See the Mulan PSL v2 for more details.
+*/
+
+/**
+* @file sys_arch.c
+* @brief In order to adapt to XiUOS, some changes have been made to implement the LwIP interface.
+* @version 1.0
+* @author AIIT XUOS Lab
+* @date 2021-05-29
+*/
+#include "debug.h"
+
+#include
+#include
+
+#include "tcpip.h"
+#include "lwip/init.h"
+#include "lwip/netif.h"
+#include "lwip/sio.h"
+#include "ethernetif.h"
+
+#if !NO_SYS
+#include "sys_arch.h"
+#endif
+#include
+#include
+#include
+#include "lwip/dhcp.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+int errno;
+
+
+x_ticks_t lwip_sys_now;
+
+struct sys_timeouts {
+ struct sys_timeo *next;
+};
+
+struct timeoutlist
+{
+ struct sys_timeouts timeouts;
+ int32 pid;
+};
+
+#define SYS_THREAD_MAX 4
+
+static struct timeoutlist s_timeoutlist[SYS_THREAD_MAX];
+
+static u16_t s_nextthread = 0;
+
+u32_t
+sys_jiffies(void)
+{
+ lwip_sys_now = CurrentTicksGain();
+ return lwip_sys_now;
+}
+
+u32_t
+sys_now(void)
+{
+ lwip_sys_now = CurrentTicksGain();
+ return lwip_sys_now;
+}
+
+void
+sys_init(void)
+{
+ int i;
+ for(i = 0; i < SYS_THREAD_MAX; i++)
+ {
+ s_timeoutlist[i].pid = 0;
+ s_timeoutlist[i].timeouts.next = NULL;
+ }
+ s_nextthread = 0;
+}
+
+struct sys_timeouts *sys_arch_timeouts(void)
+{
+ int i;
+ int32 pid;
+ struct timeoutlist *tl;
+ pid = (int32)GetKTaskDescriptor()->id.id;
+ for(i = 0; i < s_nextthread; i++)
+ {
+ tl = &(s_timeoutlist[i]);
+ if(tl->pid == pid)
+ {
+ return &(tl->timeouts);
+ }
+ }
+ return NULL;
+}
+
+sys_prot_t sys_arch_protect(void)
+{
+ return CriticalAreaLock();
+}
+
+void sys_arch_unprotect(sys_prot_t pval)
+{
+ CriticalAreaUnLock(pval);
+}
+
+#if !NO_SYS
+
+err_t
+sys_sem_new(sys_sem_t *sem, u8_t count)
+{
+ *sem = KSemaphoreCreate((uint16)count);
+
+#if SYS_STATS
+ ++lwip_stats.sys.sem.used;
+ if (lwip_stats.sys.sem.max < lwip_stats.sys.sem.used) {
+ lwip_stats.sys.sem.max = lwip_stats.sys.sem.used;
+ }
+#endif /* SYS_STATS */
+
+ if(*sem >= 0)
+ return ERR_OK;
+ else
+ {
+#if SYS_STATS
+ ++lwip_stats.sys.sem.err;
+#endif /* SYS_STATS */
+ KPrintf("[sys_arch]:new sem fail!\n");
+ return ERR_MEM;
+ }
+}
+
+void
+sys_sem_free(sys_sem_t *sem)
+{
+#if SYS_STATS
+ --lwip_stats.sys.sem.used;
+#endif /* SYS_STATS */
+ KSemaphoreDelete(*sem);
+ *sem = SYS_SEM_NULL;
+}
+
+int sys_sem_valid(sys_sem_t *sem)
+{
+ return (*sem >= SYS_SEM_NULL);
+}
+
+void
+sys_sem_set_invalid(sys_sem_t *sem)
+{
+ *sem = SYS_SEM_NULL;
+}
+
+u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout)
+{
+ x_ticks_t start_tick = 0 ;
+ int32 wait_time = 0;
+
+
+ if(*sem == SYS_SEM_NULL)
+ return SYS_ARCH_TIMEOUT;
+
+
+ start_tick = CurrentTicksGain();
+
+ if (0 == timeout)
+ wait_time = WAITING_FOREVER;
+ else
+ wait_time = timeout;
+
+
+ if(KSemaphoreObtain(*sem, wait_time) == EOK)
+ return ((CurrentTicksGain()-start_tick)*MS_PER_SYSTICK_F407);
+ else
+ return SYS_ARCH_TIMEOUT;
+}
+
+void sys_sem_signal(sys_sem_t *sem)
+{
+ if(KSemaphoreAbandon( *sem ) != EOK)
+ KPrintf("[sys_arch]:sem signal fail!\n");
+}
+
+err_t sys_mutex_new(sys_mutex_t *mutex)
+{
+ *mutex = KMutexCreate();
+ if(*mutex >= 0)
+ return ERR_OK;
+ else
+ {
+ KPrintf("[sys_arch]:new mutex fail!\n");
+ return ERR_MEM;
+ }
+}
+
+void sys_mutex_free(sys_mutex_t *mutex)
+{
+ KMutexDelete(*mutex);
+}
+
+void sys_mutex_set_invalid(sys_mutex_t *mutex)
+{
+ *mutex = SYS_MRTEX_NULL;
+}
+
+void sys_mutex_lock(sys_mutex_t *mutex)
+{
+ KMutexObtain(*mutex,
+ WAITING_FOREVER);
+}
+
+void sys_mutex_unlock(sys_mutex_t *mutex)
+{
+ KMutexAbandon( *mutex );
+}
+
+
+sys_thread_t sys_thread_new(const char *name, lwip_thread_fn function, void *arg, int stacksize, int prio)
+{
+ sys_thread_t handle = -1;
+ handle = KTaskCreate(name,
+ function,
+ arg,
+ (uint32)stacksize,
+ (uint8)prio);
+
+ if (handle >= 0)
+ {
+ StartupKTask(handle);
+ return handle;
+ }
+ return -ERROR;
+}
+
+err_t sys_mbox_new(sys_mbox_t *mbox, int size)
+{
+ *mbox = KCreateMsgQueue(sizeof(void *), size);
+
+#if SYS_STATS
+ ++lwip_stats.sys.mbox.used;
+ if (lwip_stats.sys.mbox.max < lwip_stats.sys.mbox.used) {
+ lwip_stats.sys.mbox.max = lwip_stats.sys.mbox.used;
+ }
+#endif /* SYS_STATS */
+ if(*mbox < 0)
+ return ERR_MEM;
+
+ return ERR_OK;
+}
+
+void sys_mbox_free(sys_mbox_t *mbox)
+{
+ KDeleteMsgQueue(*mbox);
+}
+
+int sys_mbox_valid(sys_mbox_t *mbox)
+{
+ if (*mbox < SYS_MBOX_NULL)
+ return 0;
+ else
+ return 1;
+}
+
+void sys_mbox_set_invalid(sys_mbox_t *mbox)
+{
+ *mbox = SYS_MBOX_NULL;
+}
+
+void sys_mbox_post(sys_mbox_t *q, void *msg)
+{
+ while(KMsgQueueSendwait( *q, &msg, sizeof(void *), WAITING_FOREVER) != EOK);
+}
+
+err_t sys_mbox_trypost(sys_mbox_t *q, void *msg)
+{
+ if(KMsgQueueSend(*q, &msg, sizeof(void *)) == EOK)
+ return ERR_OK;
+ else
+ return ERR_MEM;
+}
+
+err_t sys_mbox_trypost_fromisr(sys_mbox_t *q, void *msg)
+{
+ return sys_mbox_trypost(q, msg);
+}
+
+u32_t sys_arch_mbox_fetch(sys_mbox_t *q, void **msg, u32_t timeout)
+{
+ x_ticks_t start_tick = 0 ;
+ int32 wait_time = 0;
+
+ start_tick = CurrentTicksGain();
+
+ if (0 == timeout)
+ wait_time = WAITING_FOREVER;
+ else
+ wait_time = timeout;
+
+ if(KMsgQueueRecv(*q, &(*msg), sizeof(void *), wait_time) == EOK)
+ return ((CurrentTicksGain()-start_tick)*MS_PER_SYSTICK_F407);
+ else{
+ *msg = NULL;
+ return SYS_ARCH_TIMEOUT;
+ }
+}
+
+u32_t sys_arch_mbox_tryfetch(sys_mbox_t *q, void **msg)
+{
+ if(KMsgQueueRecv(*q, &(*msg), sizeof(void *), 0) == EOK)
+ return ERR_OK;
+ else
+ return SYS_MBOX_EMPTY;
+}
+
+#if LWIP_NETCONN_SEM_PER_THREAD
+#error LWIP_NETCONN_SEM_PER_THREAD==1 not supported
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+#endif /* !NO_SYS */
+
+/* Variables Initialization */
+struct netif gnetif;
+ip4_addr_t ipaddr;
+ip4_addr_t netmask;
+ip4_addr_t gw;
+uint8_t IP_ADDRESS[4];
+uint8_t NETMASK_ADDRESS[4];
+uint8_t GATEWAY_ADDRESS[4];
+
+void TcpIpInit(void)
+{
+ tcpip_init(NULL, NULL);
+
+ /* IP addresses initialization */
+ /* USER CODE BEGIN 0 */
+#if LWIP_DHCP
+ ip_addr_set_zero_ip4(&ipaddr);
+ ip_addr_set_zero_ip4(&netmask);
+ ip_addr_set_zero_ip4(&gw);
+#else
+ #ifdef SET_AS_SERVER
+ IP4_ADDR(&ipaddr,IP_ADDR0_SERVER,IP_ADDR1_SERVER,IP_ADDR2_SERVER,IP_ADDR3_SERVER);
+ #else
+ IP4_ADDR(&ipaddr,IP_ADDR0_ClIENT,IP_ADDR1_ClIENT,IP_ADDR2_ClIENT,IP_ADDR3_ClIENT);
+ #endif
+
+ IP4_ADDR(&netmask,NETMASK_ADDR0,NETMASK_ADDR1,NETMASK_ADDR2,NETMASK_ADDR3);
+ IP4_ADDR(&gw,GW_ADDR0,GW_ADDR1,GW_ADDR2,GW_ADDR3);
+#endif /* USE_DHCP */
+ /* USER CODE END 0 */
+ /* Initilialize the LwIP stack without RTOS */
+ /* add the network interface (IPv4/IPv6) without RTOS */
+ netif_add(&gnetif, &ipaddr, &netmask, &gw, NULL, ðernetif_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 */
+ KPrintf("TcpIpInit : netif_set_up\n");
+ netif_set_up(&gnetif);
+ }
+ else
+ {
+ /* When the netif link is down this function must be called */
+ KPrintf("TcpIpInit : netif_set_down\n");
+ netif_set_down(&gnetif);
+ }
+
+#if LWIP_DHCP
+ int err;
+ /* Creates a new DHCP client for this interface on the first call.
+ Note: you must call dhcp_fine_tmr() and dhcp_coarse_tmr() at
+ the predefined regular intervals after starting the client.
+ You can peek in the netif->dhcp struct for the actual DHCP status.*/
+
+
+ err = dhcp_start(&gnetif);
+ if(err == ERR_OK)
+ KPrintf("lwip dhcp init success...\n\n");
+ else
+ KPrintf("lwip dhcp init fail...\n\n");
+ while(ip_addr_cmp(&(gnetif.ip_addr),&ipaddr))
+ {
+ vTaskDelay(1);
+ }
+#endif
+ KPrintf("\n\nIP:%d.%d.%d.%d\n\n", \
+ ((gnetif.ip_addr.addr)&0x000000ff), \
+ (((gnetif.ip_addr.addr)&0x0000ff00)>>8), \
+ (((gnetif.ip_addr.addr)&0x00ff0000)>>16), \
+ ((gnetif.ip_addr.addr)&0xff000000)>>24);
+}
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/ethernet/LwIP/arch/sys_arch.h b/resources/ethernet/LwIP/arch/sys_arch.h
new file mode 100644
index 00000000..acbf8b9e
--- /dev/null
+++ b/resources/ethernet/LwIP/arch/sys_arch.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2017 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ *
+ */
+/*
+* Copyright (c) 2020 AIIT XUOS Lab
+* XiUOS is licensed under Mulan PSL v2.
+* You can use this software according to the terms and conditions of the Mulan PSL v2.
+* You may obtain a copy of Mulan PSL v2 at:
+* http://license.coscl.org.cn/MulanPSL2
+* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
+* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
+* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
+* See the Mulan PSL v2 for more details.
+*/
+
+/**
+* @file sys_arch.h
+* @brief In order to adapt to XiUOS, some changes have been made to implement the LwIP interface.
+* @version 1.0
+* @author AIIT XUOS Lab
+* @date 2021-05-29
+*/
+
+#include
+#include
+#include "tcpip.h"
+
+#include
+
+/* USER CODE BEGIN 0 */
+#define SET_AS_SERVER 1 /* define this terminal is udp server or not*/
+
+#define LOCAL_PORT_SERVER 5001
+#define TARGET_PORT_CLIENT LOCAL_PORT_SERVER
+
+/*Static IP ADDRESS: IP_ADDR0.IP_ADDR1.IP_ADDR2.IP_ADDR3 */
+#define IP_ADDR0_SERVER 192
+#define IP_ADDR1_SERVER 168
+#define IP_ADDR2_SERVER 0
+#define IP_ADDR3_SERVER 166
+
+#define IP_ADDR0_ClIENT 192
+#define IP_ADDR1_ClIENT 168
+#define IP_ADDR2_ClIENT 0
+#define IP_ADDR3_ClIENT 177
+
+/*NETMASK*/
+#define NETMASK_ADDR0 255
+#define NETMASK_ADDR1 255
+#define NETMASK_ADDR2 255
+#define NETMASK_ADDR3 0
+
+/*Gateway Address*/
+#define GW_ADDR0 192
+#define GW_ADDR1 168
+#define GW_ADDR2 0
+#define GW_ADDR3 1
+/* USER CODE END 0 */
+
+#define SYS_MBOX_NULL 0
+#define SYS_SEM_NULL 0
+#define SYS_MRTEX_NULL SYS_SEM_NULL
+
+typedef int32 sys_sem_t;
+typedef int32 sys_mutex_t;
+typedef int32 sys_mbox_t;
+typedef int32 sys_thread_t;
+
+typedef x_base sys_prot_t;
+
+#define MS_PER_SYSTICK_F407 1000/TICK_PER_SECOND
+
+void TcpIpInit(void);
+
diff --git a/resources/ethernet/LwIP/arch/tcp_echo_socket_demo.c b/resources/ethernet/LwIP/arch/tcp_echo_socket_demo.c
new file mode 100644
index 00000000..2e7f03da
--- /dev/null
+++ b/resources/ethernet/LwIP/arch/tcp_echo_socket_demo.c
@@ -0,0 +1,68 @@
+/*
+* Copyright (c) 2020 AIIT XUOS Lab
+* XiUOS is licensed under Mulan PSL v2.
+* You can use this software according to the terms and conditions of the Mulan PSL v2.
+* You may obtain a copy of Mulan PSL v2 at:
+* http://license.coscl.org.cn/MulanPSL2
+* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
+* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
+* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
+* See the Mulan PSL v2 for more details.
+*/
+
+/**
+* @file tcp_echo_socket_demo.c
+* @brief One UDP demo based on LwIP
+* @version 1.0
+* @author AIIT XUOS Lab
+* @date 2021-05-29
+*/
+
+#include
+#include "udp_echo.h"
+#include
+
+static void NetStackTaskCreate(void* param);
+
+extern void TcpIpInit(void);
+
+extern char* send_msg;
+
+int UdpEchoSocketDemo(int argc, char *argv[])
+{
+ if (argc == 2)
+ {
+ send_msg = argv[1];
+ }
+
+ ETH_BSP_Config();
+
+ int32 thr_id = KTaskCreate(
+ (const char* )"NetStackTaskCreate",
+ NetStackTaskCreate,
+ (void* )NULL,
+ (uint16_t )512,
+ 15);
+
+ if(thr_id >= 0)
+ StartupKTask(thr_id);
+ else{
+ KPrintf("NetStackTaskCreate create failed !\n");
+ return -1;
+ }
+}
+#ifndef SEPARATE_COMPILE
+SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN),
+UdpEchoSocketDemo, UdpEchoSocketDemo, tcp_echo_socket function );
+#endif
+
+static void NetStackTaskCreate(void* param)
+{
+ TcpIpInit();
+
+ UdpEchoInit();
+}
+
+
+
+
diff --git a/resources/ethernet/LwIP/arch/udp_echo.c b/resources/ethernet/LwIP/arch/udp_echo.c
new file mode 100644
index 00000000..9eefd849
--- /dev/null
+++ b/resources/ethernet/LwIP/arch/udp_echo.c
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ */
+
+/*
+* Copyright (c) 2020 AIIT XUOS Lab
+* XiUOS is licensed under Mulan PSL v2.
+* You can use this software according to the terms and conditions of the Mulan PSL v2.
+* You may obtain a copy of Mulan PSL v2 at:
+* http://license.coscl.org.cn/MulanPSL2
+* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
+* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
+* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
+* See the Mulan PSL v2 for more details.
+*/
+
+/**
+* @file tcpecho.c
+* @brief Add UDP client function
+* @version 1.0
+* @author AIIT XUOS Lab
+* @date 2021-05-29
+*/
+
+#include "udp_echo.h"
+
+#include "lwip/opt.h"
+
+#if LWIP_SOCKET
+#include
+
+#include "lwip/sys.h"
+#include "lwip/api.h"
+#include
+
+#define RECV_DATA (1024)
+
+char* send_msg = "\n\nThis one is UDP pkg. Congratulations on you.\n\n";
+
+static void UdpEchoThreadServer(void *arg)
+{
+ KPrintf("UdpEchoThreadServer start.\n");
+
+ int sock = -1;
+ char *recv_data;
+ struct sockaddr_in udp_addr,seraddr;
+ int recv_data_len;
+ socklen_t addrlen;
+
+ while(1)
+ {
+ recv_data = (char *)malloc(RECV_DATA);
+ if (recv_data == NULL)
+ {
+ KPrintf("No memory\n");
+ goto __exit;
+ }
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0)
+ {
+ KPrintf("Socket error\n");
+ goto __exit;
+ }
+
+ udp_addr.sin_family = AF_INET;
+ udp_addr.sin_addr.s_addr = INADDR_ANY;
+ udp_addr.sin_port = htons(LOCAL_PORT_SERVER);
+ memset(&(udp_addr.sin_zero), 0, sizeof(udp_addr.sin_zero));
+
+ if (bind(sock, (struct sockaddr *)&udp_addr, sizeof(struct sockaddr)) == -1)
+ {
+ KPrintf("Unable to bind\n");
+ goto __exit;
+ }
+
+ KPrintf("UDP bind sucess, start to receive.\n");
+ KPrintf("\n\nLocal Port:%d\n\n", LOCAL_PORT_SERVER);
+
+ while(1)
+ {
+ recv_data_len=recvfrom(sock,recv_data,
+ RECV_DATA,0,
+ (struct sockaddr*)&seraddr,
+ &addrlen);
+
+ KPrintf("Receive from : %s\n",inet_ntoa(seraddr.sin_addr));
+
+ KPrintf("Recevce data : %s\n\n",recv_data);
+
+ sendto(sock,recv_data,
+ recv_data_len,0,
+ (struct sockaddr*)&seraddr,
+ addrlen);
+ }
+
+__exit:
+ if (sock >= 0) closesocket(sock);
+ if (recv_data) free(recv_data);
+ }
+}
+
+static void UdpEchoThreadClient(void *arg)
+{
+ KPrintf("UdpEchoThreadClient start.\n");
+ MdelayKTask(5000);
+ int sock_udp_send_once = -1;
+ sock_udp_send_once = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock_udp_send_once < 0)
+ {
+ KPrintf("Socket error\n");
+ goto __exit;
+ }
+
+ struct sockaddr_in udp_sock;
+ udp_sock.sin_family = AF_INET;
+ udp_sock.sin_port = htons(TARGET_PORT_CLIENT);
+ udp_sock.sin_addr.s_addr = PP_HTONL(LWIP_MAKEU32(IP_ADDR0_SERVER,IP_ADDR1_SERVER,IP_ADDR2_SERVER,IP_ADDR3_SERVER));
+ memset(&(udp_sock.sin_zero), 0, sizeof(udp_sock.sin_zero));
+
+ if (connect(sock_udp_send_once, (struct sockaddr *)&udp_sock, sizeof(struct sockaddr)))
+ {
+ KPrintf("Unable to connect\n");
+ goto __exit;
+ }
+
+ KPrintf("UDP connect sucess, start to send.\n");
+ KPrintf("\n\nTarget Port:%d\n\n", udp_sock.sin_port);
+
+ sendto(sock_udp_send_once,send_msg,
+ strlen(send_msg),0,
+ (struct sockaddr*)&udp_sock,
+ sizeof(struct sockaddr));
+
+ KPrintf("Send UDP msg: %s ", send_msg);
+
+ while (1)
+ {
+ KPrintf("Lwip client is running.\n");
+ MdelayKTask(3000);
+ }
+
+__exit:
+ if (sock_udp_send_once >= 0) closesocket(sock_udp_send_once);
+
+ return;
+}
+
+void
+UdpEchoInit(void)
+{
+ #ifdef SET_AS_SERVER
+ sys_thread_new("UdpEchoThreadServer", UdpEchoThreadServer, NULL, 2048, 4);
+ #else
+ sys_thread_new("UdpEchoThreadClient", UdpEchoThreadClient, NULL, 2048, 4);
+ #endif
+}
+
+#endif /* LWIP_NETCONN */
diff --git a/resources/ethernet/LwIP/arch/udp_echo.h b/resources/ethernet/LwIP/arch/udp_echo.h
new file mode 100644
index 00000000..ddf419e0
--- /dev/null
+++ b/resources/ethernet/LwIP/arch/udp_echo.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ */
+
+
+#ifndef LWIP_TCPECHO_H
+#define LWIP_TCPECHO_H
+
+void UdpEchoInit(void);
+
+
+#endif /* LWIP_TCPECHO_H */
diff --git a/resources/ethernet/LwIP/core/Makefile b/resources/ethernet/LwIP/core/Makefile
new file mode 100644
index 00000000..14836150
--- /dev/null
+++ b/resources/ethernet/LwIP/core/Makefile
@@ -0,0 +1,24 @@
+SRC_FILES += altcp_alloc.c \
+ altcp_tcp.c \
+ altcp.c \
+ def.c \
+ dns.c \
+ inet_chksum.c \
+ init.c \
+ ip.c \
+ mem.c \
+ memp.c \
+ netif.c \
+ pbuf.c \
+ raw.c \
+ stats.c \
+ sys.c \
+ tcp_in.c \
+ tcp_out.c \
+ tcp.c \
+ timeouts.c \
+ udp.c
+
+SRC_DIR += ipv4
+
+include $(KERNEL_ROOT)/compiler.mk
diff --git a/resources/ethernet/LwIP/core/altcp.c b/resources/ethernet/LwIP/core/altcp.c
new file mode 100644
index 00000000..d42b727c
--- /dev/null
+++ b/resources/ethernet/LwIP/core/altcp.c
@@ -0,0 +1,681 @@
+/**
+ * @file
+ * @defgroup altcp Application layered TCP Functions
+ * @ingroup altcp_api
+ *
+ * This file contains the common functions for altcp to work.
+ * For more details see @ref altcp_api.
+ */
+
+/**
+ * @defgroup altcp_api Application layered TCP Introduction
+ * @ingroup callbackstyle_api
+ *
+ * Overview
+ * --------
+ * altcp (application layered TCP connection API; to be used from TCPIP thread)
+ * is an abstraction layer that prevents applications linking hard against the
+ * @ref tcp.h functions while providing the same functionality. It is used to
+ * e.g. add SSL/TLS (see LWIP_ALTCP_TLS) or proxy-connect support to an application
+ * written for the tcp callback API without that application knowing the
+ * protocol details.
+ *
+ * * This interface mimics the tcp callback API to the application while preventing
+ * direct linking (much like virtual functions).
+ * * This way, an application can make use of other application layer protocols
+ * on top of TCP without knowing the details (e.g. TLS, proxy connection).
+ * * This is achieved by simply including "lwip/altcp.h" instead of "lwip/tcp.h",
+ * replacing "struct tcp_pcb" with "struct altcp_pcb" and prefixing all functions
+ * with "altcp_" instead of "tcp_".
+ *
+ * With altcp support disabled (LWIP_ALTCP==0), applications written against the
+ * altcp API can still be compiled but are directly linked against the tcp.h
+ * callback API and then cannot use layered protocols. To minimize code changes
+ * in this case, the use of altcp_allocators is strongly suggested.
+ *
+ * Usage
+ * -----
+ * To make use of this API from an existing tcp raw API application:
+ * * Include "lwip/altcp.h" instead of "lwip/tcp.h"
+ * * Replace "struct tcp_pcb" with "struct altcp_pcb"
+ * * Prefix all called tcp API functions with "altcp_" instead of "tcp_" to link
+ * against the altcp functions
+ * * @ref altcp_new (and @ref altcp_new_ip_type/@ref altcp_new_ip6) take
+ * an @ref altcp_allocator_t as an argument, whereas the original tcp API
+ * functions take no arguments.
+ * * An @ref altcp_allocator_t allocator is an object that holds a pointer to an
+ * allocator object and a corresponding state (e.g. for TLS, the corresponding
+ * state may hold certificates or keys). This way, the application does not
+ * even need to know if it uses TLS or pure TCP, this is handled at runtime
+ * by passing a specific allocator.
+ * * An application can alternatively bind hard to the altcp_tls API by calling
+ * @ref altcp_tls_new or @ref altcp_tls_wrap.
+ * * The TLS layer is not directly implemented by lwIP, but a port to mbedTLS is
+ * provided.
+ * * Another altcp layer is proxy-connect to use TLS behind a HTTP proxy (see
+ * @ref altcp_proxyconnect.h)
+ *
+ * altcp_allocator_t
+ * -----------------
+ * An altcp allocator is created by the application by combining an allocator
+ * callback function and a corresponding state, e.g.:\code{.c}
+ * static const unsigned char cert[] = {0x2D, ... (see mbedTLS doc for how to create this)};
+ * struct altcp_tls_config * conf = altcp_tls_create_config_client(cert, sizeof(cert));
+ * altcp_allocator_t tls_allocator = {
+ * altcp_tls_alloc, conf
+ * };
+ * \endcode
+ *
+ *
+ * struct altcp_tls_config
+ * -----------------------
+ * The struct altcp_tls_config holds state that is needed to create new TLS client
+ * or server connections (e.g. certificates and private keys).
+ *
+ * It is not defined by lwIP itself but by the TLS port (e.g. altcp_tls to mbedTLS
+ * adaption). However, the parameters used to create it are defined in @ref
+ * altcp_tls.h (see @ref altcp_tls_create_config_server_privkey_cert for servers
+ * and @ref altcp_tls_create_config_client/@ref altcp_tls_create_config_client_2wayauth
+ * for clients).
+ *
+ * For mbedTLS, ensure that certificates can be parsed by 'mbedtls_x509_crt_parse()' and
+ * private keys can be parsed by 'mbedtls_pk_parse_key()'.
+ */
+
+/*
+ * Copyright (c) 2017 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/altcp.h"
+#include "lwip/priv/altcp_priv.h"
+#include "lwip/altcp_tcp.h"
+#include "lwip/tcp.h"
+#include "lwip/mem.h"
+
+#include
+
+extern const struct altcp_functions altcp_tcp_functions;
+
+/**
+ * For altcp layer implementations only: allocate a new struct altcp_pcb from the pool
+ * and zero the memory
+ */
+struct altcp_pcb *
+altcp_alloc(void)
+{
+ struct altcp_pcb *ret = (struct altcp_pcb *)memp_malloc(MEMP_ALTCP_PCB);
+ if (ret != NULL) {
+ memset(ret, 0, sizeof(struct altcp_pcb));
+ }
+ return ret;
+}
+
+/**
+ * For altcp layer implementations only: return a struct altcp_pcb to the pool
+ */
+void
+altcp_free(struct altcp_pcb *conn)
+{
+ if (conn) {
+ if (conn->fns && conn->fns->dealloc) {
+ conn->fns->dealloc(conn);
+ }
+ memp_free(MEMP_ALTCP_PCB, conn);
+ }
+}
+
+/**
+ * @ingroup altcp
+ * altcp_new_ip6: @ref altcp_new for IPv6
+ */
+struct altcp_pcb *
+altcp_new_ip6(altcp_allocator_t *allocator)
+{
+ return altcp_new_ip_type(allocator, IPADDR_TYPE_V6);
+}
+
+/**
+ * @ingroup altcp
+ * altcp_new: @ref altcp_new for IPv4
+ */
+struct altcp_pcb *
+altcp_new(altcp_allocator_t *allocator)
+{
+ return altcp_new_ip_type(allocator, IPADDR_TYPE_V4);
+}
+
+/**
+ * @ingroup altcp
+ * altcp_new_ip_type: called by applications to allocate a new pcb with the help of an
+ * allocator function.
+ *
+ * @param allocator allocator function and argument
+ * @param ip_type IP version of the pcb (@ref lwip_ip_addr_type)
+ * @return a new altcp_pcb or NULL on error
+ */
+struct altcp_pcb *
+altcp_new_ip_type(altcp_allocator_t *allocator, u8_t ip_type)
+{
+ struct altcp_pcb *conn;
+ if (allocator == NULL) {
+ /* no allocator given, create a simple TCP connection */
+ return altcp_tcp_new_ip_type(ip_type);
+ }
+ if (allocator->alloc == NULL) {
+ /* illegal allocator */
+ return NULL;
+ }
+ conn = allocator->alloc(allocator->arg, ip_type);
+ if (conn == NULL) {
+ /* allocation failed */
+ return NULL;
+ }
+ return conn;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_arg()
+ */
+void
+altcp_arg(struct altcp_pcb *conn, void *arg)
+{
+ if (conn) {
+ conn->arg = arg;
+ }
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_accept()
+ */
+void
+altcp_accept(struct altcp_pcb *conn, altcp_accept_fn accept)
+{
+ if (conn != NULL) {
+ conn->accept = accept;
+ }
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_recv()
+ */
+void
+altcp_recv(struct altcp_pcb *conn, altcp_recv_fn recv)
+{
+ if (conn) {
+ conn->recv = recv;
+ }
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_sent()
+ */
+void
+altcp_sent(struct altcp_pcb *conn, altcp_sent_fn sent)
+{
+ if (conn) {
+ conn->sent = sent;
+ }
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_poll()
+ */
+void
+altcp_poll(struct altcp_pcb *conn, altcp_poll_fn poll, u8_t interval)
+{
+ if (conn) {
+ conn->poll = poll;
+ conn->pollinterval = interval;
+ if (conn->fns && conn->fns->set_poll) {
+ conn->fns->set_poll(conn, interval);
+ }
+ }
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_err()
+ */
+void
+altcp_err(struct altcp_pcb *conn, altcp_err_fn err)
+{
+ if (conn) {
+ conn->err = err;
+ }
+}
+
+/* Generic functions calling the "virtual" ones */
+
+/**
+ * @ingroup altcp
+ * @see tcp_recved()
+ */
+void
+altcp_recved(struct altcp_pcb *conn, u16_t len)
+{
+ if (conn && conn->fns && conn->fns->recved) {
+ conn->fns->recved(conn, len);
+ }
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_bind()
+ */
+err_t
+altcp_bind(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port)
+{
+ if (conn && conn->fns && conn->fns->bind) {
+ return conn->fns->bind(conn, ipaddr, port);
+ }
+ return ERR_VAL;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_connect()
+ */
+err_t
+altcp_connect(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port, altcp_connected_fn connected)
+{
+ if (conn && conn->fns && conn->fns->connect) {
+ return conn->fns->connect(conn, ipaddr, port, connected);
+ }
+ return ERR_VAL;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_listen_with_backlog_and_err()
+ */
+struct altcp_pcb *
+altcp_listen_with_backlog_and_err(struct altcp_pcb *conn, u8_t backlog, err_t *err)
+{
+ if (conn && conn->fns && conn->fns->listen) {
+ return conn->fns->listen(conn, backlog, err);
+ }
+ return NULL;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_abort()
+ */
+void
+altcp_abort(struct altcp_pcb *conn)
+{
+ if (conn && conn->fns && conn->fns->abort) {
+ conn->fns->abort(conn);
+ }
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_close()
+ */
+err_t
+altcp_close(struct altcp_pcb *conn)
+{
+ if (conn && conn->fns && conn->fns->close) {
+ return conn->fns->close(conn);
+ }
+ return ERR_VAL;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_shutdown()
+ */
+err_t
+altcp_shutdown(struct altcp_pcb *conn, int shut_rx, int shut_tx)
+{
+ if (conn && conn->fns && conn->fns->shutdown) {
+ return conn->fns->shutdown(conn, shut_rx, shut_tx);
+ }
+ return ERR_VAL;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_write()
+ */
+err_t
+altcp_write(struct altcp_pcb *conn, const void *dataptr, u16_t len, u8_t apiflags)
+{
+ if (conn && conn->fns && conn->fns->write) {
+ return conn->fns->write(conn, dataptr, len, apiflags);
+ }
+ return ERR_VAL;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_output()
+ */
+err_t
+altcp_output(struct altcp_pcb *conn)
+{
+ if (conn && conn->fns && conn->fns->output) {
+ return conn->fns->output(conn);
+ }
+ return ERR_VAL;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_mss()
+ */
+u16_t
+altcp_mss(struct altcp_pcb *conn)
+{
+ if (conn && conn->fns && conn->fns->mss) {
+ return conn->fns->mss(conn);
+ }
+ return 0;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_sndbuf()
+ */
+u16_t
+altcp_sndbuf(struct altcp_pcb *conn)
+{
+ if (conn && conn->fns && conn->fns->sndbuf) {
+ return conn->fns->sndbuf(conn);
+ }
+ return 0;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_sndqueuelen()
+ */
+u16_t
+altcp_sndqueuelen(struct altcp_pcb *conn)
+{
+ if (conn && conn->fns && conn->fns->sndqueuelen) {
+ return conn->fns->sndqueuelen(conn);
+ }
+ return 0;
+}
+
+void
+altcp_nagle_disable(struct altcp_pcb *conn)
+{
+ if (conn && conn->fns && conn->fns->nagle_disable) {
+ conn->fns->nagle_disable(conn);
+ }
+}
+
+void
+altcp_nagle_enable(struct altcp_pcb *conn)
+{
+ if (conn && conn->fns && conn->fns->nagle_enable) {
+ conn->fns->nagle_enable(conn);
+ }
+}
+
+int
+altcp_nagle_disabled(struct altcp_pcb *conn)
+{
+ if (conn && conn->fns && conn->fns->nagle_disabled) {
+ return conn->fns->nagle_disabled(conn);
+ }
+ return 0;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_setprio()
+ */
+void
+altcp_setprio(struct altcp_pcb *conn, u8_t prio)
+{
+ if (conn && conn->fns && conn->fns->setprio) {
+ conn->fns->setprio(conn, prio);
+ }
+}
+
+err_t
+altcp_get_tcp_addrinfo(struct altcp_pcb *conn, int local, ip_addr_t *addr, u16_t *port)
+{
+ if (conn && conn->fns && conn->fns->addrinfo) {
+ return conn->fns->addrinfo(conn, local, addr, port);
+ }
+ return ERR_VAL;
+}
+
+ip_addr_t *
+altcp_get_ip(struct altcp_pcb *conn, int local)
+{
+ if (conn && conn->fns && conn->fns->getip) {
+ return conn->fns->getip(conn, local);
+ }
+ return NULL;
+}
+
+u16_t
+altcp_get_port(struct altcp_pcb *conn, int local)
+{
+ if (conn && conn->fns && conn->fns->getport) {
+ return conn->fns->getport(conn, local);
+ }
+ return 0;
+}
+
+#ifdef LWIP_DEBUG
+enum tcp_state
+altcp_dbg_get_tcp_state(struct altcp_pcb *conn)
+{
+ if (conn && conn->fns && conn->fns->dbg_get_tcp_state) {
+ return conn->fns->dbg_get_tcp_state(conn);
+ }
+ return CLOSED;
+}
+#endif
+
+/* Default implementations for the "virtual" functions */
+
+void
+altcp_default_set_poll(struct altcp_pcb *conn, u8_t interval)
+{
+ if (conn && conn->inner_conn) {
+ altcp_poll(conn->inner_conn, conn->poll, interval);
+ }
+}
+
+void
+altcp_default_recved(struct altcp_pcb *conn, u16_t len)
+{
+ if (conn && conn->inner_conn) {
+ altcp_recved(conn->inner_conn, len);
+ }
+}
+
+err_t
+altcp_default_bind(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port)
+{
+ if (conn && conn->inner_conn) {
+ return altcp_bind(conn->inner_conn, ipaddr, port);
+ }
+ return ERR_VAL;
+}
+
+err_t
+altcp_default_shutdown(struct altcp_pcb *conn, int shut_rx, int shut_tx)
+{
+ if (conn) {
+ if (shut_rx && shut_tx && conn->fns && conn->fns->close) {
+ /* default shutdown for both sides is close */
+ return conn->fns->close(conn);
+ }
+ if (conn->inner_conn) {
+ return altcp_shutdown(conn->inner_conn, shut_rx, shut_tx);
+ }
+ }
+ return ERR_VAL;
+}
+
+err_t
+altcp_default_write(struct altcp_pcb *conn, const void *dataptr, u16_t len, u8_t apiflags)
+{
+ if (conn && conn->inner_conn) {
+ return altcp_write(conn->inner_conn, dataptr, len, apiflags);
+ }
+ return ERR_VAL;
+}
+
+err_t
+altcp_default_output(struct altcp_pcb *conn)
+{
+ if (conn && conn->inner_conn) {
+ return altcp_output(conn->inner_conn);
+ }
+ return ERR_VAL;
+}
+
+u16_t
+altcp_default_mss(struct altcp_pcb *conn)
+{
+ if (conn && conn->inner_conn) {
+ return altcp_mss(conn->inner_conn);
+ }
+ return 0;
+}
+
+u16_t
+altcp_default_sndbuf(struct altcp_pcb *conn)
+{
+ if (conn && conn->inner_conn) {
+ return altcp_sndbuf(conn->inner_conn);
+ }
+ return 0;
+}
+
+u16_t
+altcp_default_sndqueuelen(struct altcp_pcb *conn)
+{
+ if (conn && conn->inner_conn) {
+ return altcp_sndqueuelen(conn->inner_conn);
+ }
+ return 0;
+}
+
+void
+altcp_default_nagle_disable(struct altcp_pcb *conn)
+{
+ if (conn && conn->inner_conn) {
+ altcp_nagle_disable(conn->inner_conn);
+ }
+}
+
+void
+altcp_default_nagle_enable(struct altcp_pcb *conn)
+{
+ if (conn && conn->inner_conn) {
+ altcp_nagle_enable(conn->inner_conn);
+ }
+}
+
+int
+altcp_default_nagle_disabled(struct altcp_pcb *conn)
+{
+ if (conn && conn->inner_conn) {
+ return altcp_nagle_disabled(conn->inner_conn);
+ }
+ return 0;
+}
+
+void
+altcp_default_setprio(struct altcp_pcb *conn, u8_t prio)
+{
+ if (conn && conn->inner_conn) {
+ altcp_setprio(conn->inner_conn, prio);
+ }
+}
+
+void
+altcp_default_dealloc(struct altcp_pcb *conn)
+{
+ LWIP_UNUSED_ARG(conn);
+ /* nothing to do */
+}
+
+err_t
+altcp_default_get_tcp_addrinfo(struct altcp_pcb *conn, int local, ip_addr_t *addr, u16_t *port)
+{
+ if (conn && conn->inner_conn) {
+ return altcp_get_tcp_addrinfo(conn->inner_conn, local, addr, port);
+ }
+ return ERR_VAL;
+}
+
+ip_addr_t *
+altcp_default_get_ip(struct altcp_pcb *conn, int local)
+{
+ if (conn && conn->inner_conn) {
+ return altcp_get_ip(conn->inner_conn, local);
+ }
+ return NULL;
+}
+
+u16_t
+altcp_default_get_port(struct altcp_pcb *conn, int local)
+{
+ if (conn && conn->inner_conn) {
+ return altcp_get_port(conn->inner_conn, local);
+ }
+ return 0;
+}
+
+#ifdef LWIP_DEBUG
+enum tcp_state
+altcp_default_dbg_get_tcp_state(struct altcp_pcb *conn)
+{
+ if (conn && conn->inner_conn) {
+ return altcp_dbg_get_tcp_state(conn->inner_conn);
+ }
+ return CLOSED;
+}
+#endif
+
+
+#endif /* LWIP_ALTCP */
diff --git a/resources/ethernet/LwIP/core/altcp_alloc.c b/resources/ethernet/LwIP/core/altcp_alloc.c
new file mode 100644
index 00000000..8c2390ee
--- /dev/null
+++ b/resources/ethernet/LwIP/core/altcp_alloc.c
@@ -0,0 +1,87 @@
+/**
+ * @file
+ * Application layered TCP connection API (to be used from TCPIP thread)\n
+ * This interface mimics the tcp callback API to the application while preventing
+ * direct linking (much like virtual functions).
+ * This way, an application can make use of other application layer protocols
+ * on top of TCP without knowing the details (e.g. TLS, proxy connection).
+ *
+ * This file contains allocation implementation that combine several layers.
+ */
+
+/*
+ * Copyright (c) 2017 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/altcp.h"
+#include "lwip/altcp_tcp.h"
+#include "lwip/altcp_tls.h"
+#include "lwip/priv/altcp_priv.h"
+#include "lwip/mem.h"
+
+#include
+
+#if LWIP_ALTCP_TLS
+
+/** This standard allocator function creates an altcp pcb for
+ * TLS over TCP */
+struct altcp_pcb *
+altcp_tls_new(struct altcp_tls_config *config, u8_t ip_type)
+{
+ struct altcp_pcb *inner_conn, *ret;
+ LWIP_UNUSED_ARG(ip_type);
+
+ inner_conn = altcp_tcp_new_ip_type(ip_type);
+ if (inner_conn == NULL) {
+ return NULL;
+ }
+ ret = altcp_tls_wrap(config, inner_conn);
+ if (ret == NULL) {
+ altcp_close(inner_conn);
+ }
+ return ret;
+}
+
+/** This standard allocator function creates an altcp pcb for
+ * TLS over TCP */
+struct altcp_pcb *
+altcp_tls_alloc(void *arg, u8_t ip_type)
+{
+ return altcp_tls_new((struct altcp_tls_config *)arg, ip_type);
+}
+
+#endif /* LWIP_ALTCP_TLS */
+
+#endif /* LWIP_ALTCP */
diff --git a/resources/ethernet/LwIP/core/altcp_tcp.c b/resources/ethernet/LwIP/core/altcp_tcp.c
new file mode 100644
index 00000000..f5b8e844
--- /dev/null
+++ b/resources/ethernet/LwIP/core/altcp_tcp.c
@@ -0,0 +1,543 @@
+/**
+ * @file
+ * Application layered TCP connection API (to be used from TCPIP thread)\n
+ * This interface mimics the tcp callback API to the application while preventing
+ * direct linking (much like virtual functions).
+ * This way, an application can make use of other application layer protocols
+ * on top of TCP without knowing the details (e.g. TLS, proxy connection).
+ *
+ * This file contains the base implementation calling into tcp.
+ */
+
+/*
+ * Copyright (c) 2017 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/altcp.h"
+#include "lwip/altcp_tcp.h"
+#include "lwip/priv/altcp_priv.h"
+#include "lwip/tcp.h"
+#include "lwip/mem.h"
+
+#include
+
+#define ALTCP_TCP_ASSERT_CONN(conn) do { \
+ LWIP_ASSERT("conn->inner_conn == NULL", (conn)->inner_conn == NULL); \
+ LWIP_UNUSED_ARG(conn); /* for LWIP_NOASSERT */ } while(0)
+#define ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb) do { \
+ LWIP_ASSERT("pcb mismatch", (conn)->state == tpcb); \
+ LWIP_UNUSED_ARG(tpcb); /* for LWIP_NOASSERT */ \
+ ALTCP_TCP_ASSERT_CONN(conn); } while(0)
+
+
+/* Variable prototype, the actual declaration is at the end of this file
+ since it contains pointers to static functions declared here */
+extern const struct altcp_functions altcp_tcp_functions;
+
+static void altcp_tcp_setup(struct altcp_pcb *conn, struct tcp_pcb *tpcb);
+
+/* callback functions for TCP */
+static err_t
+altcp_tcp_accept(void *arg, struct tcp_pcb *new_tpcb, err_t err)
+{
+ struct altcp_pcb *listen_conn = (struct altcp_pcb *)arg;
+ if (listen_conn && listen_conn->accept) {
+ /* create a new altcp_conn to pass to the next 'accept' callback */
+ struct altcp_pcb *new_conn = altcp_alloc();
+ if (new_conn == NULL) {
+ return ERR_MEM;
+ }
+ altcp_tcp_setup(new_conn, new_tpcb);
+ return listen_conn->accept(listen_conn->arg, new_conn, err);
+ }
+ return ERR_ARG;
+}
+
+static err_t
+altcp_tcp_connected(void *arg, struct tcp_pcb *tpcb, err_t err)
+{
+ struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+ if (conn) {
+ ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb);
+ if (conn->connected) {
+ return conn->connected(conn->arg, conn, err);
+ }
+ }
+ return ERR_OK;
+}
+
+static err_t
+altcp_tcp_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
+{
+ struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+ if (conn) {
+ ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb);
+ if (conn->recv) {
+ return conn->recv(conn->arg, conn, p, err);
+ }
+ }
+ if (p != NULL) {
+ /* prevent memory leaks */
+ pbuf_free(p);
+ }
+ return ERR_OK;
+}
+
+static err_t
+altcp_tcp_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
+{
+ struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+ if (conn) {
+ ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb);
+ if (conn->sent) {
+ return conn->sent(conn->arg, conn, len);
+ }
+ }
+ return ERR_OK;
+}
+
+static err_t
+altcp_tcp_poll(void *arg, struct tcp_pcb *tpcb)
+{
+ struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+ if (conn) {
+ ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb);
+ if (conn->poll) {
+ return conn->poll(conn->arg, conn);
+ }
+ }
+ return ERR_OK;
+}
+
+static void
+altcp_tcp_err(void *arg, err_t err)
+{
+ struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+ if (conn) {
+ conn->state = NULL; /* already freed */
+ if (conn->err) {
+ conn->err(conn->arg, err);
+ }
+ altcp_free(conn);
+ }
+}
+
+/* setup functions */
+
+static void
+altcp_tcp_remove_callbacks(struct tcp_pcb *tpcb)
+{
+ tcp_arg(tpcb, NULL);
+ tcp_recv(tpcb, NULL);
+ tcp_sent(tpcb, NULL);
+ tcp_err(tpcb, NULL);
+ tcp_poll(tpcb, NULL, tpcb->pollinterval);
+}
+
+static void
+altcp_tcp_setup_callbacks(struct altcp_pcb *conn, struct tcp_pcb *tpcb)
+{
+ tcp_arg(tpcb, conn);
+ tcp_recv(tpcb, altcp_tcp_recv);
+ tcp_sent(tpcb, altcp_tcp_sent);
+ tcp_err(tpcb, altcp_tcp_err);
+ /* tcp_poll is set when interval is set by application */
+ /* listen is set totally different :-) */
+}
+
+static void
+altcp_tcp_setup(struct altcp_pcb *conn, struct tcp_pcb *tpcb)
+{
+ altcp_tcp_setup_callbacks(conn, tpcb);
+ conn->state = tpcb;
+ conn->fns = &altcp_tcp_functions;
+}
+
+struct altcp_pcb *
+altcp_tcp_new_ip_type(u8_t ip_type)
+{
+ /* Allocate the tcp pcb first to invoke the priority handling code
+ if we're out of pcbs */
+ struct tcp_pcb *tpcb = tcp_new_ip_type(ip_type);
+ if (tpcb != NULL) {
+ struct altcp_pcb *ret = altcp_alloc();
+ if (ret != NULL) {
+ altcp_tcp_setup(ret, tpcb);
+ return ret;
+ } else {
+ /* altcp_pcb allocation failed -> free the tcp_pcb too */
+ tcp_close(tpcb);
+ }
+ }
+ return NULL;
+}
+
+/** altcp_tcp allocator function fitting to @ref altcp_allocator_t / @ref altcp_new.
+*
+* arg pointer is not used for TCP.
+*/
+struct altcp_pcb *
+altcp_tcp_alloc(void *arg, u8_t ip_type)
+{
+ LWIP_UNUSED_ARG(arg);
+ return altcp_tcp_new_ip_type(ip_type);
+}
+
+struct altcp_pcb *
+altcp_tcp_wrap(struct tcp_pcb *tpcb)
+{
+ if (tpcb != NULL) {
+ struct altcp_pcb *ret = altcp_alloc();
+ if (ret != NULL) {
+ altcp_tcp_setup(ret, tpcb);
+ return ret;
+ }
+ }
+ return NULL;
+}
+
+
+/* "virtual" functions calling into tcp */
+static void
+altcp_tcp_set_poll(struct altcp_pcb *conn, u8_t interval)
+{
+ if (conn != NULL) {
+ struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+ ALTCP_TCP_ASSERT_CONN(conn);
+ tcp_poll(pcb, altcp_tcp_poll, interval);
+ }
+}
+
+static void
+altcp_tcp_recved(struct altcp_pcb *conn, u16_t len)
+{
+ if (conn != NULL) {
+ struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+ ALTCP_TCP_ASSERT_CONN(conn);
+ tcp_recved(pcb, len);
+ }
+}
+
+static err_t
+altcp_tcp_bind(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port)
+{
+ struct tcp_pcb *pcb;
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+ ALTCP_TCP_ASSERT_CONN(conn);
+ pcb = (struct tcp_pcb *)conn->state;
+ return tcp_bind(pcb, ipaddr, port);
+}
+
+static err_t
+altcp_tcp_connect(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port, altcp_connected_fn connected)
+{
+ struct tcp_pcb *pcb;
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+ ALTCP_TCP_ASSERT_CONN(conn);
+ conn->connected = connected;
+ pcb = (struct tcp_pcb *)conn->state;
+ return tcp_connect(pcb, ipaddr, port, altcp_tcp_connected);
+}
+
+static struct altcp_pcb *
+altcp_tcp_listen(struct altcp_pcb *conn, u8_t backlog, err_t *err)
+{
+ struct tcp_pcb *pcb;
+ struct tcp_pcb *lpcb;
+ if (conn == NULL) {
+ return NULL;
+ }
+ ALTCP_TCP_ASSERT_CONN(conn);
+ pcb = (struct tcp_pcb *)conn->state;
+ lpcb = tcp_listen_with_backlog_and_err(pcb, backlog, err);
+ if (lpcb != NULL) {
+ conn->state = lpcb;
+ tcp_accept(lpcb, altcp_tcp_accept);
+ return conn;
+ }
+ return NULL;
+}
+
+static void
+altcp_tcp_abort(struct altcp_pcb *conn)
+{
+ if (conn != NULL) {
+ struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+ ALTCP_TCP_ASSERT_CONN(conn);
+ if (pcb) {
+ tcp_abort(pcb);
+ }
+ }
+}
+
+static err_t
+altcp_tcp_close(struct altcp_pcb *conn)
+{
+ struct tcp_pcb *pcb;
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+ ALTCP_TCP_ASSERT_CONN(conn);
+ pcb = (struct tcp_pcb *)conn->state;
+ if (pcb) {
+ err_t err;
+ tcp_poll_fn oldpoll = pcb->poll;
+ altcp_tcp_remove_callbacks(pcb);
+ err = tcp_close(pcb);
+ if (err != ERR_OK) {
+ /* not closed, set up all callbacks again */
+ altcp_tcp_setup_callbacks(conn, pcb);
+ /* poll callback is not included in the above */
+ tcp_poll(pcb, oldpoll, pcb->pollinterval);
+ return err;
+ }
+ conn->state = NULL; /* unsafe to reference pcb after tcp_close(). */
+ }
+ altcp_free(conn);
+ return ERR_OK;
+}
+
+static err_t
+altcp_tcp_shutdown(struct altcp_pcb *conn, int shut_rx, int shut_tx)
+{
+ struct tcp_pcb *pcb;
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+ ALTCP_TCP_ASSERT_CONN(conn);
+ pcb = (struct tcp_pcb *)conn->state;
+ return tcp_shutdown(pcb, shut_rx, shut_tx);
+}
+
+static err_t
+altcp_tcp_write(struct altcp_pcb *conn, const void *dataptr, u16_t len, u8_t apiflags)
+{
+ struct tcp_pcb *pcb;
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+ ALTCP_TCP_ASSERT_CONN(conn);
+ pcb = (struct tcp_pcb *)conn->state;
+ return tcp_write(pcb, dataptr, len, apiflags);
+}
+
+static err_t
+altcp_tcp_output(struct altcp_pcb *conn)
+{
+ struct tcp_pcb *pcb;
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+ ALTCP_TCP_ASSERT_CONN(conn);
+ pcb = (struct tcp_pcb *)conn->state;
+ return tcp_output(pcb);
+}
+
+static u16_t
+altcp_tcp_mss(struct altcp_pcb *conn)
+{
+ struct tcp_pcb *pcb;
+ if (conn == NULL) {
+ return 0;
+ }
+ ALTCP_TCP_ASSERT_CONN(conn);
+ pcb = (struct tcp_pcb *)conn->state;
+ return tcp_mss(pcb);
+}
+
+static u16_t
+altcp_tcp_sndbuf(struct altcp_pcb *conn)
+{
+ struct tcp_pcb *pcb;
+ if (conn == NULL) {
+ return 0;
+ }
+ ALTCP_TCP_ASSERT_CONN(conn);
+ pcb = (struct tcp_pcb *)conn->state;
+ return tcp_sndbuf(pcb);
+}
+
+static u16_t
+altcp_tcp_sndqueuelen(struct altcp_pcb *conn)
+{
+ struct tcp_pcb *pcb;
+ if (conn == NULL) {
+ return 0;
+ }
+ ALTCP_TCP_ASSERT_CONN(conn);
+ pcb = (struct tcp_pcb *)conn->state;
+ return tcp_sndqueuelen(pcb);
+}
+
+static void
+altcp_tcp_nagle_disable(struct altcp_pcb *conn)
+{
+ if (conn && conn->state) {
+ struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+ ALTCP_TCP_ASSERT_CONN(conn);
+ tcp_nagle_disable(pcb);
+ }
+}
+
+static void
+altcp_tcp_nagle_enable(struct altcp_pcb *conn)
+{
+ if (conn && conn->state) {
+ struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+ ALTCP_TCP_ASSERT_CONN(conn);
+ tcp_nagle_enable(pcb);
+ }
+}
+
+static int
+altcp_tcp_nagle_disabled(struct altcp_pcb *conn)
+{
+ if (conn && conn->state) {
+ struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+ ALTCP_TCP_ASSERT_CONN(conn);
+ return tcp_nagle_disabled(pcb);
+ }
+ return 0;
+}
+
+static void
+altcp_tcp_setprio(struct altcp_pcb *conn, u8_t prio)
+{
+ if (conn != NULL) {
+ struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+ ALTCP_TCP_ASSERT_CONN(conn);
+ tcp_setprio(pcb, prio);
+ }
+}
+
+static void
+altcp_tcp_dealloc(struct altcp_pcb *conn)
+{
+ LWIP_UNUSED_ARG(conn);
+ ALTCP_TCP_ASSERT_CONN(conn);
+ /* no private state to clean up */
+}
+
+static err_t
+altcp_tcp_get_tcp_addrinfo(struct altcp_pcb *conn, int local, ip_addr_t *addr, u16_t *port)
+{
+ if (conn) {
+ struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+ ALTCP_TCP_ASSERT_CONN(conn);
+ return tcp_tcp_get_tcp_addrinfo(pcb, local, addr, port);
+ }
+ return ERR_VAL;
+}
+
+static ip_addr_t *
+altcp_tcp_get_ip(struct altcp_pcb *conn, int local)
+{
+ if (conn) {
+ struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+ ALTCP_TCP_ASSERT_CONN(conn);
+ if (pcb) {
+ if (local) {
+ return &pcb->local_ip;
+ } else {
+ return &pcb->remote_ip;
+ }
+ }
+ }
+ return NULL;
+}
+
+static u16_t
+altcp_tcp_get_port(struct altcp_pcb *conn, int local)
+{
+ if (conn) {
+ struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+ ALTCP_TCP_ASSERT_CONN(conn);
+ if (pcb) {
+ if (local) {
+ return pcb->local_port;
+ } else {
+ return pcb->remote_port;
+ }
+ }
+ }
+ return 0;
+}
+
+#ifdef LWIP_DEBUG
+static enum tcp_state
+altcp_tcp_dbg_get_tcp_state(struct altcp_pcb *conn)
+{
+ if (conn) {
+ struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+ ALTCP_TCP_ASSERT_CONN(conn);
+ if (pcb) {
+ return pcb->state;
+ }
+ }
+ return CLOSED;
+}
+#endif
+const struct altcp_functions altcp_tcp_functions = {
+ altcp_tcp_set_poll,
+ altcp_tcp_recved,
+ altcp_tcp_bind,
+ altcp_tcp_connect,
+ altcp_tcp_listen,
+ altcp_tcp_abort,
+ altcp_tcp_close,
+ altcp_tcp_shutdown,
+ altcp_tcp_write,
+ altcp_tcp_output,
+ altcp_tcp_mss,
+ altcp_tcp_sndbuf,
+ altcp_tcp_sndqueuelen,
+ altcp_tcp_nagle_disable,
+ altcp_tcp_nagle_enable,
+ altcp_tcp_nagle_disabled,
+ altcp_tcp_setprio,
+ altcp_tcp_dealloc,
+ altcp_tcp_get_tcp_addrinfo,
+ altcp_tcp_get_ip,
+ altcp_tcp_get_port
+#ifdef LWIP_DEBUG
+ , altcp_tcp_dbg_get_tcp_state
+#endif
+};
+
+#endif /* LWIP_ALTCP */
diff --git a/resources/ethernet/LwIP/core/def.c b/resources/ethernet/LwIP/core/def.c
new file mode 100644
index 00000000..004a5288
--- /dev/null
+++ b/resources/ethernet/LwIP/core/def.c
@@ -0,0 +1,240 @@
+/**
+ * @file
+ * Common functions used throughout the stack.
+ *
+ * These are reference implementations of the byte swapping functions.
+ * Again with the aim of being simple, correct and fully portable.
+ * Byte swapping is the second thing you would want to optimize. You will
+ * need to port it to your architecture and in your cc.h:
+ *
+ * \#define lwip_htons(x) your_htons
+ * \#define lwip_htonl(x) your_htonl
+ *
+ * Note lwip_ntohs() and lwip_ntohl() are merely references to the htonx counterparts.
+ *
+ * If you \#define them to htons() and htonl(), you should
+ * \#define LWIP_DONT_PROVIDE_BYTEORDER_FUNCTIONS to prevent lwIP from
+ * defining htonx/ntohx compatibility macros.
+
+ * @defgroup sys_nonstandard Non-standard functions
+ * @ingroup sys_layer
+ * lwIP provides default implementations for non-standard functions.
+ * These can be mapped to OS functions to reduce code footprint if desired.
+ * All defines related to this section must not be placed in lwipopts.h,
+ * but in arch/cc.h!
+ * These options cannot be \#defined in lwipopts.h since they are not options
+ * of lwIP itself, but options of the lwIP port to your system.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ *
+ */
+
+#include "lwip/opt.h"
+#include "lwip/def.h"
+
+#include
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+
+#if !defined(lwip_htons)
+/**
+ * Convert an u16_t from host- to network byte order.
+ *
+ * @param n u16_t in host byte order
+ * @return n in network byte order
+ */
+u16_t
+lwip_htons(u16_t n)
+{
+ return PP_HTONS(n);
+}
+#endif /* lwip_htons */
+
+#if !defined(lwip_htonl)
+/**
+ * Convert an u32_t from host- to network byte order.
+ *
+ * @param n u32_t in host byte order
+ * @return n in network byte order
+ */
+u32_t
+lwip_htonl(u32_t n)
+{
+ return PP_HTONL(n);
+}
+#endif /* lwip_htonl */
+
+#endif /* BYTE_ORDER == LITTLE_ENDIAN */
+
+#ifndef lwip_strnstr
+/**
+ * @ingroup sys_nonstandard
+ * lwIP default implementation for strnstr() non-standard function.
+ * This can be \#defined to strnstr() depending on your platform port.
+ */
+char *
+lwip_strnstr(const char *buffer, const char *token, size_t n)
+{
+ const char *p;
+ size_t tokenlen = strlen(token);
+ if (tokenlen == 0) {
+ return LWIP_CONST_CAST(char *, buffer);
+ }
+ for (p = buffer; *p && (p + tokenlen <= buffer + n); p++) {
+ if ((*p == *token) && (strncmp(p, token, tokenlen) == 0)) {
+ return LWIP_CONST_CAST(char *, p);
+ }
+ }
+ return NULL;
+}
+#endif
+
+#ifndef lwip_stricmp
+/**
+ * @ingroup sys_nonstandard
+ * lwIP default implementation for stricmp() non-standard function.
+ * This can be \#defined to stricmp() depending on your platform port.
+ */
+int
+lwip_stricmp(const char *str1, const char *str2)
+{
+ char c1, c2;
+
+ do {
+ c1 = *str1++;
+ c2 = *str2++;
+ if (c1 != c2) {
+ char c1_upc = c1 | 0x20;
+ if ((c1_upc >= 'a') && (c1_upc <= 'z')) {
+ /* characters are not equal an one is in the alphabet range:
+ downcase both chars and check again */
+ char c2_upc = c2 | 0x20;
+ if (c1_upc != c2_upc) {
+ /* still not equal */
+ /* don't care for < or > */
+ return 1;
+ }
+ } else {
+ /* characters are not equal but none is in the alphabet range */
+ return 1;
+ }
+ }
+ } while (c1 != 0);
+ return 0;
+}
+#endif
+
+#ifndef lwip_strnicmp
+/**
+ * @ingroup sys_nonstandard
+ * lwIP default implementation for strnicmp() non-standard function.
+ * This can be \#defined to strnicmp() depending on your platform port.
+ */
+int
+lwip_strnicmp(const char *str1, const char *str2, size_t len)
+{
+ char c1, c2;
+
+ do {
+ c1 = *str1++;
+ c2 = *str2++;
+ if (c1 != c2) {
+ char c1_upc = c1 | 0x20;
+ if ((c1_upc >= 'a') && (c1_upc <= 'z')) {
+ /* characters are not equal an one is in the alphabet range:
+ downcase both chars and check again */
+ char c2_upc = c2 | 0x20;
+ if (c1_upc != c2_upc) {
+ /* still not equal */
+ /* don't care for < or > */
+ return 1;
+ }
+ } else {
+ /* characters are not equal but none is in the alphabet range */
+ return 1;
+ }
+ }
+ len--;
+ } while ((len != 0) && (c1 != 0));
+ return 0;
+}
+#endif
+
+#ifndef lwip_itoa
+/**
+ * @ingroup sys_nonstandard
+ * lwIP default implementation for itoa() non-standard function.
+ * This can be \#defined to itoa() or snprintf(result, bufsize, "%d", number) depending on your platform port.
+ */
+void
+lwip_itoa(char *result, size_t bufsize, int number)
+{
+ char *res = result;
+ char *tmp = result + bufsize - 1;
+ int n = (number >= 0) ? number : -number;
+
+ /* handle invalid bufsize */
+ if (bufsize < 2) {
+ if (bufsize == 1) {
+ *result = 0;
+ }
+ return;
+ }
+
+ /* First, add sign */
+ if (number < 0) {
+ *res++ = '-';
+ }
+ /* Then create the string from the end and stop if buffer full,
+ and ensure output string is zero terminated */
+ *tmp = 0;
+ while ((n != 0) && (tmp > res)) {
+ char val = (char)('0' + (n % 10));
+ tmp--;
+ *tmp = val;
+ n = n / 10;
+ }
+ if (n) {
+ /* buffer is too small */
+ *result = 0;
+ return;
+ }
+ if (*tmp == 0) {
+ /* Nothing added? */
+ *res++ = '0';
+ *res++ = 0;
+ return;
+ }
+ /* move from temporary buffer to output buffer (sign is not moved) */
+ memmove(res, tmp, (size_t)((result + bufsize) - tmp));
+}
+#endif
diff --git a/resources/ethernet/LwIP/core/dns.c b/resources/ethernet/LwIP/core/dns.c
new file mode 100644
index 00000000..737b3393
--- /dev/null
+++ b/resources/ethernet/LwIP/core/dns.c
@@ -0,0 +1,1631 @@
+/**
+ * @file
+ * DNS - host name to IP address resolver.
+ *
+ * @defgroup dns DNS
+ * @ingroup callbackstyle_api
+ *
+ * Implements a DNS host name to IP address resolver.
+ *
+ * The lwIP DNS resolver functions are used to lookup a host name and
+ * map it to a numerical IP address. It maintains a list of resolved
+ * hostnames that can be queried with the dns_lookup() function.
+ * New hostnames can be resolved using the dns_query() function.
+ *
+ * The lwIP version of the resolver also adds a non-blocking version of
+ * gethostbyname() that will work with a raw API application. This function
+ * checks for an IP address string first and converts it if it is valid.
+ * gethostbyname() then does a dns_lookup() to see if the name is
+ * already in the table. If so, the IP is returned. If not, a query is
+ * issued and the function returns with a ERR_INPROGRESS status. The app
+ * using the dns client must then go into a waiting state.
+ *
+ * Once a hostname has been resolved (or found to be non-existent),
+ * the resolver code calls a specified callback function (which
+ * must be implemented by the module that uses the resolver).
+ *
+ * Multicast DNS queries are supported for names ending on ".local".
+ * However, only "One-Shot Multicast DNS Queries" are supported (RFC 6762
+ * chapter 5.1), this is not a fully compliant implementation of continuous
+ * mDNS querying!
+ *
+ * All functions must be called from TCPIP thread.
+ *
+ * @see DNS_MAX_SERVERS
+ * @see LWIP_DHCP_MAX_DNS_SERVERS
+ * @see @ref netconn_common for thread-safe access.
+ */
+
+/*
+ * Port to lwIP from uIP
+ * by Jim Pettinato April 2007
+ *
+ * security fixes and more by Simon Goldschmidt
+ *
+ * uIP version Copyright (c) 2002-2003, Adam Dunkels.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-----------------------------------------------------------------------------
+ * RFC 1035 - Domain names - implementation and specification
+ * RFC 2181 - Clarifications to the DNS Specification
+ *----------------------------------------------------------------------------*/
+
+/** @todo: define good default values (rfc compliance) */
+/** @todo: improve answer parsing, more checkings... */
+/** @todo: check RFC1035 - 7.3. Processing responses */
+/** @todo: one-shot mDNS: dual-stack fallback to another IP version */
+
+/*-----------------------------------------------------------------------------
+ * Includes
+ *----------------------------------------------------------------------------*/
+
+#include "lwip/opt.h"
+
+#if LWIP_DNS /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/udp.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/dns.h"
+#include "lwip/prot/dns.h"
+
+#include
+
+/** Random generator function to create random TXIDs and source ports for queries */
+#ifndef DNS_RAND_TXID
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_XID) != 0)
+#define DNS_RAND_TXID LWIP_RAND
+#else
+static u16_t dns_txid;
+#define DNS_RAND_TXID() (++dns_txid)
+#endif
+#endif
+
+/** Limits the source port to be >= 1024 by default */
+#ifndef DNS_PORT_ALLOWED
+#define DNS_PORT_ALLOWED(port) ((port) >= 1024)
+#endif
+
+/** DNS resource record max. TTL (one week as default) */
+#ifndef DNS_MAX_TTL
+#define DNS_MAX_TTL 604800
+#elif DNS_MAX_TTL > 0x7FFFFFFF
+#error DNS_MAX_TTL must be a positive 32-bit value
+#endif
+
+#if DNS_TABLE_SIZE > 255
+#error DNS_TABLE_SIZE must fit into an u8_t
+#endif
+#if DNS_MAX_SERVERS > 255
+#error DNS_MAX_SERVERS must fit into an u8_t
+#endif
+
+/* The number of parallel requests (i.e. calls to dns_gethostbyname
+ * that cannot be answered from the DNS table.
+ * This is set to the table size by default.
+ */
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
+#ifndef DNS_MAX_REQUESTS
+#define DNS_MAX_REQUESTS DNS_TABLE_SIZE
+#else
+#if DNS_MAX_REQUESTS > 255
+#error DNS_MAX_REQUESTS must fit into an u8_t
+#endif
+#endif
+#else
+/* In this configuration, both arrays have to have the same size and are used
+ * like one entry (used/free) */
+#define DNS_MAX_REQUESTS DNS_TABLE_SIZE
+#endif
+
+/* The number of UDP source ports used in parallel */
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
+#ifndef DNS_MAX_SOURCE_PORTS
+#define DNS_MAX_SOURCE_PORTS DNS_MAX_REQUESTS
+#else
+#if DNS_MAX_SOURCE_PORTS > 255
+#error DNS_MAX_SOURCE_PORTS must fit into an u8_t
+#endif
+#endif
+#else
+#ifdef DNS_MAX_SOURCE_PORTS
+#undef DNS_MAX_SOURCE_PORTS
+#endif
+#define DNS_MAX_SOURCE_PORTS 1
+#endif
+
+#if LWIP_IPV4 && LWIP_IPV6
+#define LWIP_DNS_ADDRTYPE_IS_IPV6(t) (((t) == LWIP_DNS_ADDRTYPE_IPV6_IPV4) || ((t) == LWIP_DNS_ADDRTYPE_IPV6))
+#define LWIP_DNS_ADDRTYPE_MATCH_IP(t, ip) (IP_IS_V6_VAL(ip) ? LWIP_DNS_ADDRTYPE_IS_IPV6(t) : (!LWIP_DNS_ADDRTYPE_IS_IPV6(t)))
+#define LWIP_DNS_ADDRTYPE_ARG(x) , x
+#define LWIP_DNS_ADDRTYPE_ARG_OR_ZERO(x) x
+#define LWIP_DNS_SET_ADDRTYPE(x, y) do { x = y; } while(0)
+#else
+#if LWIP_IPV6
+#define LWIP_DNS_ADDRTYPE_IS_IPV6(t) 1
+#else
+#define LWIP_DNS_ADDRTYPE_IS_IPV6(t) 0
+#endif
+#define LWIP_DNS_ADDRTYPE_MATCH_IP(t, ip) 1
+#define LWIP_DNS_ADDRTYPE_ARG(x)
+#define LWIP_DNS_ADDRTYPE_ARG_OR_ZERO(x) 0
+#define LWIP_DNS_SET_ADDRTYPE(x, y)
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+#define LWIP_DNS_ISMDNS_ARG(x) , x
+#else
+#define LWIP_DNS_ISMDNS_ARG(x)
+#endif
+
+/** DNS query message structure.
+ No packing needed: only used locally on the stack. */
+struct dns_query {
+ /* DNS query record starts with either a domain name or a pointer
+ to a name already present somewhere in the packet. */
+ u16_t type;
+ u16_t cls;
+};
+#define SIZEOF_DNS_QUERY 4
+
+/** DNS answer message structure.
+ No packing needed: only used locally on the stack. */
+struct dns_answer {
+ /* DNS answer record starts with either a domain name or a pointer
+ to a name already present somewhere in the packet. */
+ u16_t type;
+ u16_t cls;
+ u32_t ttl;
+ u16_t len;
+};
+#define SIZEOF_DNS_ANSWER 10
+/* maximum allowed size for the struct due to non-packed */
+#define SIZEOF_DNS_ANSWER_ASSERT 12
+
+/* DNS table entry states */
+typedef enum {
+ DNS_STATE_UNUSED = 0,
+ DNS_STATE_NEW = 1,
+ DNS_STATE_ASKING = 2,
+ DNS_STATE_DONE = 3
+} dns_state_enum_t;
+
+/** DNS table entry */
+struct dns_table_entry {
+ u32_t ttl;
+ ip_addr_t ipaddr;
+ u16_t txid;
+ u8_t state;
+ u8_t server_idx;
+ u8_t tmr;
+ u8_t retries;
+ u8_t seqno;
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
+ u8_t pcb_idx;
+#endif
+ char name[DNS_MAX_NAME_LENGTH];
+#if LWIP_IPV4 && LWIP_IPV6
+ u8_t reqaddrtype;
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+ u8_t is_mdns;
+#endif
+};
+
+/** DNS request table entry: used when dns_gehostbyname cannot answer the
+ * request from the DNS table */
+struct dns_req_entry {
+ /* pointer to callback on DNS query done */
+ dns_found_callback found;
+ /* argument passed to the callback function */
+ void *arg;
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
+ u8_t dns_table_idx;
+#endif
+#if LWIP_IPV4 && LWIP_IPV6
+ u8_t reqaddrtype;
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+};
+
+#if DNS_LOCAL_HOSTLIST
+
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+/** Local host-list. For hostnames in this list, no
+ * external name resolution is performed */
+static struct local_hostlist_entry *local_hostlist_dynamic;
+#else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+
+/** Defining this allows the local_hostlist_static to be placed in a different
+ * linker section (e.g. FLASH) */
+#ifndef DNS_LOCAL_HOSTLIST_STORAGE_PRE
+#define DNS_LOCAL_HOSTLIST_STORAGE_PRE static
+#endif /* DNS_LOCAL_HOSTLIST_STORAGE_PRE */
+/** Defining this allows the local_hostlist_static to be placed in a different
+ * linker section (e.g. FLASH) */
+#ifndef DNS_LOCAL_HOSTLIST_STORAGE_POST
+#define DNS_LOCAL_HOSTLIST_STORAGE_POST
+#endif /* DNS_LOCAL_HOSTLIST_STORAGE_POST */
+DNS_LOCAL_HOSTLIST_STORAGE_PRE struct local_hostlist_entry local_hostlist_static[]
+ DNS_LOCAL_HOSTLIST_STORAGE_POST = DNS_LOCAL_HOSTLIST_INIT;
+
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+
+static void dns_init_local(void);
+static err_t dns_lookup_local(const char *hostname, ip_addr_t *addr LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype));
+#endif /* DNS_LOCAL_HOSTLIST */
+
+
+/* forward declarations */
+static void dns_recv(void *s, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port);
+static void dns_check_entries(void);
+static void dns_call_found(u8_t idx, ip_addr_t *addr);
+
+/*-----------------------------------------------------------------------------
+ * Globals
+ *----------------------------------------------------------------------------*/
+
+/* DNS variables */
+static struct udp_pcb *dns_pcbs[DNS_MAX_SOURCE_PORTS];
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
+static u8_t dns_last_pcb_idx;
+#endif
+static u8_t dns_seqno;
+static struct dns_table_entry dns_table[DNS_TABLE_SIZE];
+static struct dns_req_entry dns_requests[DNS_MAX_REQUESTS];
+static ip_addr_t dns_servers[DNS_MAX_SERVERS];
+
+#if LWIP_IPV4
+const ip_addr_t dns_mquery_v4group = DNS_MQUERY_IPV4_GROUP_INIT;
+#endif /* LWIP_IPV4 */
+#if LWIP_IPV6
+const ip_addr_t dns_mquery_v6group = DNS_MQUERY_IPV6_GROUP_INIT;
+#endif /* LWIP_IPV6 */
+
+/**
+ * Initialize the resolver: set up the UDP pcb and configure the default server
+ * (if DNS_SERVER_ADDRESS is set).
+ */
+void
+dns_init(void)
+{
+#ifdef DNS_SERVER_ADDRESS
+ /* initialize default DNS server address */
+ ip_addr_t dnsserver;
+ DNS_SERVER_ADDRESS(&dnsserver);
+ dns_setserver(0, &dnsserver);
+#endif /* DNS_SERVER_ADDRESS */
+
+ LWIP_ASSERT("sanity check SIZEOF_DNS_QUERY",
+ sizeof(struct dns_query) == SIZEOF_DNS_QUERY);
+ LWIP_ASSERT("sanity check SIZEOF_DNS_ANSWER",
+ sizeof(struct dns_answer) <= SIZEOF_DNS_ANSWER_ASSERT);
+
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_init: initializing\n"));
+
+ /* if dns client not yet initialized... */
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) == 0)
+ if (dns_pcbs[0] == NULL) {
+ dns_pcbs[0] = udp_new_ip_type(IPADDR_TYPE_ANY);
+ LWIP_ASSERT("dns_pcbs[0] != NULL", dns_pcbs[0] != NULL);
+
+ /* initialize DNS table not needed (initialized to zero since it is a
+ * global variable) */
+ LWIP_ASSERT("For implicit initialization to work, DNS_STATE_UNUSED needs to be 0",
+ DNS_STATE_UNUSED == 0);
+
+ /* initialize DNS client */
+ udp_bind(dns_pcbs[0], IP_ANY_TYPE, 0);
+ udp_recv(dns_pcbs[0], dns_recv, NULL);
+ }
+#endif
+
+#if DNS_LOCAL_HOSTLIST
+ dns_init_local();
+#endif
+}
+
+/**
+ * @ingroup dns
+ * Initialize one of the DNS servers.
+ *
+ * @param numdns the index of the DNS server to set must be < DNS_MAX_SERVERS
+ * @param dnsserver IP address of the DNS server to set
+ */
+void
+dns_setserver(u8_t numdns, const ip_addr_t *dnsserver)
+{
+ if (numdns < DNS_MAX_SERVERS) {
+ if (dnsserver != NULL) {
+ dns_servers[numdns] = (*dnsserver);
+ } else {
+ dns_servers[numdns] = *IP_ADDR_ANY;
+ }
+ }
+}
+
+/**
+ * @ingroup dns
+ * Obtain one of the currently configured DNS server.
+ *
+ * @param numdns the index of the DNS server
+ * @return IP address of the indexed DNS server or "ip_addr_any" if the DNS
+ * server has not been configured.
+ */
+const ip_addr_t *
+dns_getserver(u8_t numdns)
+{
+ if (numdns < DNS_MAX_SERVERS) {
+ return &dns_servers[numdns];
+ } else {
+ return IP_ADDR_ANY;
+ }
+}
+
+/**
+ * The DNS resolver client timer - handle retries and timeouts and should
+ * be called every DNS_TMR_INTERVAL milliseconds (every second by default).
+ */
+void
+dns_tmr(void)
+{
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_tmr: dns_check_entries\n"));
+ dns_check_entries();
+}
+
+#if DNS_LOCAL_HOSTLIST
+static void
+dns_init_local(void)
+{
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT)
+ size_t i;
+ struct local_hostlist_entry *entry;
+ /* Dynamic: copy entries from DNS_LOCAL_HOSTLIST_INIT to list */
+ struct local_hostlist_entry local_hostlist_init[] = DNS_LOCAL_HOSTLIST_INIT;
+ size_t namelen;
+ for (i = 0; i < LWIP_ARRAYSIZE(local_hostlist_init); i++) {
+ struct local_hostlist_entry *init_entry = &local_hostlist_init[i];
+ LWIP_ASSERT("invalid host name (NULL)", init_entry->name != NULL);
+ namelen = strlen(init_entry->name);
+ LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN);
+ entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST);
+ LWIP_ASSERT("mem-error in dns_init_local", entry != NULL);
+ if (entry != NULL) {
+ char *entry_name = (char *)entry + sizeof(struct local_hostlist_entry);
+ MEMCPY(entry_name, init_entry->name, namelen);
+ entry_name[namelen] = 0;
+ entry->name = entry_name;
+ entry->addr = init_entry->addr;
+ entry->next = local_hostlist_dynamic;
+ local_hostlist_dynamic = entry;
+ }
+ }
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT) */
+}
+
+/**
+ * @ingroup dns
+ * Iterate the local host-list for a hostname.
+ *
+ * @param iterator_fn a function that is called for every entry in the local host-list
+ * @param iterator_arg 3rd argument passed to iterator_fn
+ * @return the number of entries in the local host-list
+ */
+size_t
+dns_local_iterate(dns_found_callback iterator_fn, void *iterator_arg)
+{
+ size_t i;
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+ struct local_hostlist_entry *entry = local_hostlist_dynamic;
+ i = 0;
+ while (entry != NULL) {
+ if (iterator_fn != NULL) {
+ iterator_fn(entry->name, &entry->addr, iterator_arg);
+ }
+ i++;
+ entry = entry->next;
+ }
+#else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+ for (i = 0; i < LWIP_ARRAYSIZE(local_hostlist_static); i++) {
+ if (iterator_fn != NULL) {
+ iterator_fn(local_hostlist_static[i].name, &local_hostlist_static[i].addr, iterator_arg);
+ }
+ }
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+ return i;
+}
+
+/**
+ * @ingroup dns
+ * Scans the local host-list for a hostname.
+ *
+ * @param hostname Hostname to look for in the local host-list
+ * @param addr the first IP address for the hostname in the local host-list or
+ * IPADDR_NONE if not found.
+ * @param dns_addrtype - LWIP_DNS_ADDRTYPE_IPV4_IPV6: try to resolve IPv4 (ATTENTION: no fallback here!)
+ * - LWIP_DNS_ADDRTYPE_IPV6_IPV4: try to resolve IPv6 (ATTENTION: no fallback here!)
+ * - LWIP_DNS_ADDRTYPE_IPV4: try to resolve IPv4 only
+ * - LWIP_DNS_ADDRTYPE_IPV6: try to resolve IPv6 only
+ * @return ERR_OK if found, ERR_ARG if not found
+ */
+err_t
+dns_local_lookup(const char *hostname, ip_addr_t *addr, u8_t dns_addrtype)
+{
+ LWIP_UNUSED_ARG(dns_addrtype);
+ return dns_lookup_local(hostname, addr LWIP_DNS_ADDRTYPE_ARG(dns_addrtype));
+}
+
+/* Internal implementation for dns_local_lookup and dns_lookup */
+static err_t
+dns_lookup_local(const char *hostname, ip_addr_t *addr LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype))
+{
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+ struct local_hostlist_entry *entry = local_hostlist_dynamic;
+ while (entry != NULL) {
+ if ((lwip_stricmp(entry->name, hostname) == 0) &&
+ LWIP_DNS_ADDRTYPE_MATCH_IP(dns_addrtype, entry->addr)) {
+ if (addr) {
+ ip_addr_copy(*addr, entry->addr);
+ }
+ return ERR_OK;
+ }
+ entry = entry->next;
+ }
+#else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+ size_t i;
+ for (i = 0; i < LWIP_ARRAYSIZE(local_hostlist_static); i++) {
+ if ((lwip_stricmp(local_hostlist_static[i].name, hostname) == 0) &&
+ LWIP_DNS_ADDRTYPE_MATCH_IP(dns_addrtype, local_hostlist_static[i].addr)) {
+ if (addr) {
+ ip_addr_copy(*addr, local_hostlist_static[i].addr);
+ }
+ return ERR_OK;
+ }
+ }
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+ return ERR_ARG;
+}
+
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+/**
+ * @ingroup dns
+ * Remove all entries from the local host-list for a specific hostname
+ * and/or IP address
+ *
+ * @param hostname hostname for which entries shall be removed from the local
+ * host-list
+ * @param addr address for which entries shall be removed from the local host-list
+ * @return the number of removed entries
+ */
+int
+dns_local_removehost(const char *hostname, const ip_addr_t *addr)
+{
+ int removed = 0;
+ struct local_hostlist_entry *entry = local_hostlist_dynamic;
+ struct local_hostlist_entry *last_entry = NULL;
+ while (entry != NULL) {
+ if (((hostname == NULL) || !lwip_stricmp(entry->name, hostname)) &&
+ ((addr == NULL) || ip_addr_cmp(&entry->addr, addr))) {
+ struct local_hostlist_entry *free_entry;
+ if (last_entry != NULL) {
+ last_entry->next = entry->next;
+ } else {
+ local_hostlist_dynamic = entry->next;
+ }
+ free_entry = entry;
+ entry = entry->next;
+ memp_free(MEMP_LOCALHOSTLIST, free_entry);
+ removed++;
+ } else {
+ last_entry = entry;
+ entry = entry->next;
+ }
+ }
+ return removed;
+}
+
+/**
+ * @ingroup dns
+ * Add a hostname/IP address pair to the local host-list.
+ * Duplicates are not checked.
+ *
+ * @param hostname hostname of the new entry
+ * @param addr IP address of the new entry
+ * @return ERR_OK if succeeded or ERR_MEM on memory error
+ */
+err_t
+dns_local_addhost(const char *hostname, const ip_addr_t *addr)
+{
+ struct local_hostlist_entry *entry;
+ size_t namelen;
+ char *entry_name;
+ LWIP_ASSERT("invalid host name (NULL)", hostname != NULL);
+ namelen = strlen(hostname);
+ LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN);
+ entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST);
+ if (entry == NULL) {
+ return ERR_MEM;
+ }
+ entry_name = (char *)entry + sizeof(struct local_hostlist_entry);
+ MEMCPY(entry_name, hostname, namelen);
+ entry_name[namelen] = 0;
+ entry->name = entry_name;
+ ip_addr_copy(entry->addr, *addr);
+ entry->next = local_hostlist_dynamic;
+ local_hostlist_dynamic = entry;
+ return ERR_OK;
+}
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC*/
+#endif /* DNS_LOCAL_HOSTLIST */
+
+/**
+ * @ingroup dns
+ * Look up a hostname in the array of known hostnames.
+ *
+ * @note This function only looks in the internal array of known
+ * hostnames, it does not send out a query for the hostname if none
+ * was found. The function dns_enqueue() can be used to send a query
+ * for a hostname.
+ *
+ * @param name the hostname to look up
+ * @param addr the hostname's IP address, as u32_t (instead of ip_addr_t to
+ * better check for failure: != IPADDR_NONE) or IPADDR_NONE if the hostname
+ * was not found in the cached dns_table.
+ * @return ERR_OK if found, ERR_ARG if not found
+ */
+static err_t
+dns_lookup(const char *name, ip_addr_t *addr LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype))
+{
+ u8_t i;
+#if DNS_LOCAL_HOSTLIST
+ if (dns_lookup_local(name, addr LWIP_DNS_ADDRTYPE_ARG(dns_addrtype)) == ERR_OK) {
+ return ERR_OK;
+ }
+#endif /* DNS_LOCAL_HOSTLIST */
+#ifdef DNS_LOOKUP_LOCAL_EXTERN
+ if (DNS_LOOKUP_LOCAL_EXTERN(name, addr, LWIP_DNS_ADDRTYPE_ARG_OR_ZERO(dns_addrtype)) == ERR_OK) {
+ return ERR_OK;
+ }
+#endif /* DNS_LOOKUP_LOCAL_EXTERN */
+
+ /* Walk through name list, return entry if found. If not, return NULL. */
+ for (i = 0; i < DNS_TABLE_SIZE; ++i) {
+ if ((dns_table[i].state == DNS_STATE_DONE) &&
+ (lwip_strnicmp(name, dns_table[i].name, sizeof(dns_table[i].name)) == 0) &&
+ LWIP_DNS_ADDRTYPE_MATCH_IP(dns_addrtype, dns_table[i].ipaddr)) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_lookup: \"%s\": found = ", name));
+ ip_addr_debug_print_val(DNS_DEBUG, dns_table[i].ipaddr);
+ LWIP_DEBUGF(DNS_DEBUG, ("\n"));
+ if (addr) {
+ ip_addr_copy(*addr, dns_table[i].ipaddr);
+ }
+ return ERR_OK;
+ }
+ }
+
+ return ERR_ARG;
+}
+
+/**
+ * Compare the "dotted" name "query" with the encoded name "response"
+ * to make sure an answer from the DNS server matches the current dns_table
+ * entry (otherwise, answers might arrive late for hostname not on the list
+ * any more).
+ *
+ * For now, this function compares case-insensitive to cope with all kinds of
+ * servers. This also means that "dns 0x20 bit encoding" must be checked
+ * externally, if we want to implement it.
+ * Currently, the request is sent exactly as passed in by he user request.
+ *
+ * @param query hostname (not encoded) from the dns_table
+ * @param p pbuf containing the encoded hostname in the DNS response
+ * @param start_offset offset into p where the name starts
+ * @return 0xFFFF: names differ, other: names equal -> offset behind name
+ */
+static u16_t
+dns_compare_name(const char *query, struct pbuf *p, u16_t start_offset)
+{
+ int n;
+ u16_t response_offset = start_offset;
+
+ do {
+ n = pbuf_try_get_at(p, response_offset);
+ if ((n < 0) || (response_offset == 0xFFFF)) {
+ /* error or overflow */
+ return 0xFFFF;
+ }
+ response_offset++;
+ /** @see RFC 1035 - 4.1.4. Message compression */
+ if ((n & 0xc0) == 0xc0) {
+ /* Compressed name: cannot be equal since we don't send them */
+ return 0xFFFF;
+ } else {
+ /* Not compressed name */
+ while (n > 0) {
+ int c = pbuf_try_get_at(p, response_offset);
+ if (c < 0) {
+ return 0xFFFF;
+ }
+ if (lwip_tolower((*query)) != lwip_tolower((u8_t)c)) {
+ return 0xFFFF;
+ }
+ if (response_offset == 0xFFFF) {
+ /* would overflow */
+ return 0xFFFF;
+ }
+ response_offset++;
+ ++query;
+ --n;
+ }
+ ++query;
+ }
+ n = pbuf_try_get_at(p, response_offset);
+ if (n < 0) {
+ return 0xFFFF;
+ }
+ } while (n != 0);
+
+ if (response_offset == 0xFFFF) {
+ /* would overflow */
+ return 0xFFFF;
+ }
+ return (u16_t)(response_offset + 1);
+}
+
+/**
+ * Walk through a compact encoded DNS name and return the end of the name.
+ *
+ * @param p pbuf containing the name
+ * @param query_idx start index into p pointing to encoded DNS name in the DNS server response
+ * @return index to end of the name
+ */
+static u16_t
+dns_skip_name(struct pbuf *p, u16_t query_idx)
+{
+ int n;
+ u16_t offset = query_idx;
+
+ do {
+ n = pbuf_try_get_at(p, offset++);
+ if ((n < 0) || (offset == 0)) {
+ return 0xFFFF;
+ }
+ /** @see RFC 1035 - 4.1.4. Message compression */
+ if ((n & 0xc0) == 0xc0) {
+ /* Compressed name: since we only want to skip it (not check it), stop here */
+ break;
+ } else {
+ /* Not compressed name */
+ if (offset + n >= p->tot_len) {
+ return 0xFFFF;
+ }
+ offset = (u16_t)(offset + n);
+ }
+ n = pbuf_try_get_at(p, offset);
+ if (n < 0) {
+ return 0xFFFF;
+ }
+ } while (n != 0);
+
+ if (offset == 0xFFFF) {
+ return 0xFFFF;
+ }
+ return (u16_t)(offset + 1);
+}
+
+/**
+ * Send a DNS query packet.
+ *
+ * @param idx the DNS table entry index for which to send a request
+ * @return ERR_OK if packet is sent; an err_t indicating the problem otherwise
+ */
+static err_t
+dns_send(u8_t idx)
+{
+ err_t err;
+ struct dns_hdr hdr;
+ struct dns_query qry;
+ struct pbuf *p;
+ u16_t query_idx, copy_len;
+ const char *hostname, *hostname_part;
+ u8_t n;
+ u8_t pcb_idx;
+ struct dns_table_entry *entry = &dns_table[idx];
+
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_send: dns_servers[%"U16_F"] \"%s\": request\n",
+ (u16_t)(entry->server_idx), entry->name));
+ LWIP_ASSERT("dns server out of array", entry->server_idx < DNS_MAX_SERVERS);
+ if (ip_addr_isany_val(dns_servers[entry->server_idx])
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+ && !entry->is_mdns
+#endif
+ ) {
+ /* DNS server not valid anymore, e.g. PPP netif has been shut down */
+ /* call specified callback function if provided */
+ dns_call_found(idx, NULL);
+ /* flush this entry */
+ entry->state = DNS_STATE_UNUSED;
+ return ERR_OK;
+ }
+
+ /* if here, we have either a new query or a retry on a previous query to process */
+ p = pbuf_alloc(PBUF_TRANSPORT, (u16_t)(SIZEOF_DNS_HDR + strlen(entry->name) + 2 +
+ SIZEOF_DNS_QUERY), PBUF_RAM);
+ if (p != NULL) {
+ const ip_addr_t *dst;
+ u16_t dst_port;
+ /* fill dns header */
+ memset(&hdr, 0, SIZEOF_DNS_HDR);
+ hdr.id = lwip_htons(entry->txid);
+ hdr.flags1 = DNS_FLAG1_RD;
+ hdr.numquestions = PP_HTONS(1);
+ pbuf_take(p, &hdr, SIZEOF_DNS_HDR);
+ hostname = entry->name;
+ --hostname;
+
+ /* convert hostname into suitable query format. */
+ query_idx = SIZEOF_DNS_HDR;
+ do {
+ ++hostname;
+ hostname_part = hostname;
+ for (n = 0; *hostname != '.' && *hostname != 0; ++hostname) {
+ ++n;
+ }
+ copy_len = (u16_t)(hostname - hostname_part);
+ if (query_idx + n + 1 > 0xFFFF) {
+ /* u16_t overflow */
+ goto overflow_return;
+ }
+ pbuf_put_at(p, query_idx, n);
+ pbuf_take_at(p, hostname_part, copy_len, (u16_t)(query_idx + 1));
+ query_idx = (u16_t)(query_idx + n + 1);
+ } while (*hostname != 0);
+ pbuf_put_at(p, query_idx, 0);
+ query_idx++;
+
+ /* fill dns query */
+ if (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype)) {
+ qry.type = PP_HTONS(DNS_RRTYPE_AAAA);
+ } else {
+ qry.type = PP_HTONS(DNS_RRTYPE_A);
+ }
+ qry.cls = PP_HTONS(DNS_RRCLASS_IN);
+ pbuf_take_at(p, &qry, SIZEOF_DNS_QUERY, query_idx);
+
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
+ pcb_idx = entry->pcb_idx;
+#else
+ pcb_idx = 0;
+#endif
+ /* send dns packet */
+ LWIP_DEBUGF(DNS_DEBUG, ("sending DNS request ID %d for name \"%s\" to server %d\r\n",
+ entry->txid, entry->name, entry->server_idx));
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+ if (entry->is_mdns) {
+ dst_port = DNS_MQUERY_PORT;
+#if LWIP_IPV6
+ if (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype)) {
+ dst = &dns_mquery_v6group;
+ }
+#endif
+#if LWIP_IPV4 && LWIP_IPV6
+ else
+#endif
+#if LWIP_IPV4
+ {
+ dst = &dns_mquery_v4group;
+ }
+#endif
+ } else
+#endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */
+ {
+ dst_port = DNS_SERVER_PORT;
+ dst = &dns_servers[entry->server_idx];
+ }
+ err = udp_sendto(dns_pcbs[pcb_idx], p, dst, dst_port);
+
+ /* free pbuf */
+ pbuf_free(p);
+ } else {
+ err = ERR_MEM;
+ }
+
+ return err;
+overflow_return:
+ pbuf_free(p);
+ return ERR_VAL;
+}
+
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
+static struct udp_pcb *
+dns_alloc_random_port(void)
+{
+ err_t err;
+ struct udp_pcb *pcb;
+
+ pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
+ if (pcb == NULL) {
+ /* out of memory, have to reuse an existing pcb */
+ return NULL;
+ }
+ do {
+ u16_t port = (u16_t)DNS_RAND_TXID();
+ if (DNS_PORT_ALLOWED(port)) {
+ err = udp_bind(pcb, IP_ANY_TYPE, port);
+ } else {
+ /* this port is not allowed, try again */
+ err = ERR_USE;
+ }
+ } while (err == ERR_USE);
+ if (err != ERR_OK) {
+ udp_remove(pcb);
+ return NULL;
+ }
+ udp_recv(pcb, dns_recv, NULL);
+ return pcb;
+}
+
+/**
+ * dns_alloc_pcb() - allocates a new pcb (or reuses an existing one) to be used
+ * for sending a request
+ *
+ * @return an index into dns_pcbs
+ */
+static u8_t
+dns_alloc_pcb(void)
+{
+ u8_t i;
+ u8_t idx;
+
+ for (i = 0; i < DNS_MAX_SOURCE_PORTS; i++) {
+ if (dns_pcbs[i] == NULL) {
+ break;
+ }
+ }
+ if (i < DNS_MAX_SOURCE_PORTS) {
+ dns_pcbs[i] = dns_alloc_random_port();
+ if (dns_pcbs[i] != NULL) {
+ /* succeeded */
+ dns_last_pcb_idx = i;
+ return i;
+ }
+ }
+ /* if we come here, creating a new UDP pcb failed, so we have to use
+ an already existing one (so overflow is no issue) */
+ for (i = 0, idx = (u8_t)(dns_last_pcb_idx + 1); i < DNS_MAX_SOURCE_PORTS; i++, idx++) {
+ if (idx >= DNS_MAX_SOURCE_PORTS) {
+ idx = 0;
+ }
+ if (dns_pcbs[idx] != NULL) {
+ dns_last_pcb_idx = idx;
+ return idx;
+ }
+ }
+ return DNS_MAX_SOURCE_PORTS;
+}
+#endif /* ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0) */
+
+/**
+ * dns_call_found() - call the found callback and check if there are duplicate
+ * entries for the given hostname. If there are any, their found callback will
+ * be called and they will be removed.
+ *
+ * @param idx dns table index of the entry that is resolved or removed
+ * @param addr IP address for the hostname (or NULL on error or memory shortage)
+ */
+static void
+dns_call_found(u8_t idx, ip_addr_t *addr)
+{
+#if ((LWIP_DNS_SECURE & (LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING | LWIP_DNS_SECURE_RAND_SRC_PORT)) != 0)
+ u8_t i;
+#endif
+
+#if LWIP_IPV4 && LWIP_IPV6
+ if (addr != NULL) {
+ /* check that address type matches the request and adapt the table entry */
+ if (IP_IS_V6_VAL(*addr)) {
+ LWIP_ASSERT("invalid response", LWIP_DNS_ADDRTYPE_IS_IPV6(dns_table[idx].reqaddrtype));
+ dns_table[idx].reqaddrtype = LWIP_DNS_ADDRTYPE_IPV6;
+ } else {
+ LWIP_ASSERT("invalid response", !LWIP_DNS_ADDRTYPE_IS_IPV6(dns_table[idx].reqaddrtype));
+ dns_table[idx].reqaddrtype = LWIP_DNS_ADDRTYPE_IPV4;
+ }
+ }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
+ for (i = 0; i < DNS_MAX_REQUESTS; i++) {
+ if (dns_requests[i].found && (dns_requests[i].dns_table_idx == idx)) {
+ (*dns_requests[i].found)(dns_table[idx].name, addr, dns_requests[i].arg);
+ /* flush this entry */
+ dns_requests[i].found = NULL;
+ }
+ }
+#else
+ if (dns_requests[idx].found) {
+ (*dns_requests[idx].found)(dns_table[idx].name, addr, dns_requests[idx].arg);
+ }
+ dns_requests[idx].found = NULL;
+#endif
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
+ /* close the pcb used unless other request are using it */
+ for (i = 0; i < DNS_MAX_REQUESTS; i++) {
+ if (i == idx) {
+ continue; /* only check other requests */
+ }
+ if (dns_table[i].state == DNS_STATE_ASKING) {
+ if (dns_table[i].pcb_idx == dns_table[idx].pcb_idx) {
+ /* another request is still using the same pcb */
+ dns_table[idx].pcb_idx = DNS_MAX_SOURCE_PORTS;
+ break;
+ }
+ }
+ }
+ if (dns_table[idx].pcb_idx < DNS_MAX_SOURCE_PORTS) {
+ /* if we come here, the pcb is not used any more and can be removed */
+ udp_remove(dns_pcbs[dns_table[idx].pcb_idx]);
+ dns_pcbs[dns_table[idx].pcb_idx] = NULL;
+ dns_table[idx].pcb_idx = DNS_MAX_SOURCE_PORTS;
+ }
+#endif
+}
+
+/* Create a query transmission ID that is unique for all outstanding queries */
+static u16_t
+dns_create_txid(void)
+{
+ u16_t txid;
+ u8_t i;
+
+again:
+ txid = (u16_t)DNS_RAND_TXID();
+
+ /* check whether the ID is unique */
+ for (i = 0; i < DNS_TABLE_SIZE; i++) {
+ if ((dns_table[i].state == DNS_STATE_ASKING) &&
+ (dns_table[i].txid == txid)) {
+ /* ID already used by another pending query */
+ goto again;
+ }
+ }
+
+ return txid;
+}
+
+/**
+ * Check whether there are other backup DNS servers available to try
+ */
+static u8_t
+dns_backupserver_available(struct dns_table_entry *pentry)
+{
+ u8_t ret = 0;
+
+ if (pentry) {
+ if ((pentry->server_idx + 1 < DNS_MAX_SERVERS) && !ip_addr_isany_val(dns_servers[pentry->server_idx + 1])) {
+ ret = 1;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * dns_check_entry() - see if entry has not yet been queried and, if so, sends out a query.
+ * Check an entry in the dns_table:
+ * - send out query for new entries
+ * - retry old pending entries on timeout (also with different servers)
+ * - remove completed entries from the table if their TTL has expired
+ *
+ * @param i index of the dns_table entry to check
+ */
+static void
+dns_check_entry(u8_t i)
+{
+ err_t err;
+ struct dns_table_entry *entry = &dns_table[i];
+
+ LWIP_ASSERT("array index out of bounds", i < DNS_TABLE_SIZE);
+
+ switch (entry->state) {
+ case DNS_STATE_NEW:
+ /* initialize new entry */
+ entry->txid = dns_create_txid();
+ entry->state = DNS_STATE_ASKING;
+ entry->server_idx = 0;
+ entry->tmr = 1;
+ entry->retries = 0;
+
+ /* send DNS packet for this entry */
+ err = dns_send(i);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING,
+ ("dns_send returned error: %s\n", lwip_strerr(err)));
+ }
+ break;
+ case DNS_STATE_ASKING:
+ if (--entry->tmr == 0) {
+ if (++entry->retries == DNS_MAX_RETRIES) {
+ if (dns_backupserver_available(entry)
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+ && !entry->is_mdns
+#endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */
+ ) {
+ /* change of server */
+ entry->server_idx++;
+ entry->tmr = 1;
+ entry->retries = 0;
+ } else {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": timeout\n", entry->name));
+ /* call specified callback function if provided */
+ dns_call_found(i, NULL);
+ /* flush this entry */
+ entry->state = DNS_STATE_UNUSED;
+ break;
+ }
+ } else {
+ /* wait longer for the next retry */
+ entry->tmr = entry->retries;
+ }
+
+ /* send DNS packet for this entry */
+ err = dns_send(i);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING,
+ ("dns_send returned error: %s\n", lwip_strerr(err)));
+ }
+ }
+ break;
+ case DNS_STATE_DONE:
+ /* if the time to live is nul */
+ if ((entry->ttl == 0) || (--entry->ttl == 0)) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": flush\n", entry->name));
+ /* flush this entry, there cannot be any related pending entries in this state */
+ entry->state = DNS_STATE_UNUSED;
+ }
+ break;
+ case DNS_STATE_UNUSED:
+ /* nothing to do */
+ break;
+ default:
+ LWIP_ASSERT("unknown dns_table entry state:", 0);
+ break;
+ }
+}
+
+/**
+ * Call dns_check_entry for each entry in dns_table - check all entries.
+ */
+static void
+dns_check_entries(void)
+{
+ u8_t i;
+
+ for (i = 0; i < DNS_TABLE_SIZE; ++i) {
+ dns_check_entry(i);
+ }
+}
+
+/**
+ * Save TTL and call dns_call_found for correct response.
+ */
+static void
+dns_correct_response(u8_t idx, u32_t ttl)
+{
+ struct dns_table_entry *entry = &dns_table[idx];
+
+ entry->state = DNS_STATE_DONE;
+
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response = ", entry->name));
+ ip_addr_debug_print_val(DNS_DEBUG, entry->ipaddr);
+ LWIP_DEBUGF(DNS_DEBUG, ("\n"));
+
+ /* read the answer resource record's TTL, and maximize it if needed */
+ entry->ttl = ttl;
+ if (entry->ttl > DNS_MAX_TTL) {
+ entry->ttl = DNS_MAX_TTL;
+ }
+ dns_call_found(idx, &entry->ipaddr);
+
+ if (entry->ttl == 0) {
+ /* RFC 883, page 29: "Zero values are
+ interpreted to mean that the RR can only be used for the
+ transaction in progress, and should not be cached."
+ -> flush this entry now */
+ /* entry reused during callback? */
+ if (entry->state == DNS_STATE_DONE) {
+ entry->state = DNS_STATE_UNUSED;
+ }
+ }
+}
+
+/**
+ * Receive input function for DNS response packets arriving for the dns UDP pcb.
+ */
+static void
+dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
+{
+ u8_t i;
+ u16_t txid;
+ u16_t res_idx;
+ struct dns_hdr hdr;
+ struct dns_answer ans;
+ struct dns_query qry;
+ u16_t nquestions, nanswers;
+
+ LWIP_UNUSED_ARG(arg);
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_UNUSED_ARG(port);
+
+ /* is the dns message big enough ? */
+ if (p->tot_len < (SIZEOF_DNS_HDR + SIZEOF_DNS_QUERY)) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too small\n"));
+ /* free pbuf and return */
+ goto ignore_packet;
+ }
+
+ /* copy dns payload inside static buffer for processing */
+ if (pbuf_copy_partial(p, &hdr, SIZEOF_DNS_HDR, 0) == SIZEOF_DNS_HDR) {
+ /* Match the ID in the DNS header with the name table. */
+ txid = lwip_htons(hdr.id);
+ for (i = 0; i < DNS_TABLE_SIZE; i++) {
+ struct dns_table_entry *entry = &dns_table[i];
+ if ((entry->state == DNS_STATE_ASKING) &&
+ (entry->txid == txid)) {
+
+ /* We only care about the question(s) and the answers. The authrr
+ and the extrarr are simply discarded. */
+ nquestions = lwip_htons(hdr.numquestions);
+ nanswers = lwip_htons(hdr.numanswers);
+
+ /* Check for correct response. */
+ if ((hdr.flags1 & DNS_FLAG1_RESPONSE) == 0) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": not a response\n", entry->name));
+ goto ignore_packet; /* ignore this packet */
+ }
+ if (nquestions != 1) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", entry->name));
+ goto ignore_packet; /* ignore this packet */
+ }
+
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+ if (!entry->is_mdns)
+#endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */
+ {
+ /* Check whether response comes from the same network address to which the
+ question was sent. (RFC 5452) */
+ if (!ip_addr_cmp(addr, &dns_servers[entry->server_idx])) {
+ goto ignore_packet; /* ignore this packet */
+ }
+ }
+
+ /* Check if the name in the "question" part match with the name in the entry and
+ skip it if equal. */
+ res_idx = dns_compare_name(entry->name, p, SIZEOF_DNS_HDR);
+ if (res_idx == 0xFFFF) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", entry->name));
+ goto ignore_packet; /* ignore this packet */
+ }
+
+ /* check if "question" part matches the request */
+ if (pbuf_copy_partial(p, &qry, SIZEOF_DNS_QUERY, res_idx) != SIZEOF_DNS_QUERY) {
+ goto ignore_packet; /* ignore this packet */
+ }
+ if ((qry.cls != PP_HTONS(DNS_RRCLASS_IN)) ||
+ (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype) && (qry.type != PP_HTONS(DNS_RRTYPE_AAAA))) ||
+ (!LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype) && (qry.type != PP_HTONS(DNS_RRTYPE_A)))) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", entry->name));
+ goto ignore_packet; /* ignore this packet */
+ }
+ /* skip the rest of the "question" part */
+ if (res_idx + SIZEOF_DNS_QUERY > 0xFFFF) {
+ goto ignore_packet;
+ }
+ res_idx = (u16_t)(res_idx + SIZEOF_DNS_QUERY);
+
+ /* Check for error. If so, call callback to inform. */
+ if (hdr.flags2 & DNS_FLAG2_ERR_MASK) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in flags\n", entry->name));
+
+ /* if there is another backup DNS server to try
+ * then don't stop the DNS request
+ */
+ if (dns_backupserver_available(entry)) {
+ /* avoid retrying the same server */
+ entry->retries = DNS_MAX_RETRIES-1;
+ entry->tmr = 1;
+
+ /* contact next available server for this entry */
+ dns_check_entry(i);
+
+ goto ignore_packet;
+ }
+ } else {
+ while ((nanswers > 0) && (res_idx < p->tot_len)) {
+ /* skip answer resource record's host name */
+ res_idx = dns_skip_name(p, res_idx);
+ if (res_idx == 0xFFFF) {
+ goto ignore_packet; /* ignore this packet */
+ }
+
+ /* Check for IP address type and Internet class. Others are discarded. */
+ if (pbuf_copy_partial(p, &ans, SIZEOF_DNS_ANSWER, res_idx) != SIZEOF_DNS_ANSWER) {
+ goto ignore_packet; /* ignore this packet */
+ }
+ if (res_idx + SIZEOF_DNS_ANSWER > 0xFFFF) {
+ goto ignore_packet;
+ }
+ res_idx = (u16_t)(res_idx + SIZEOF_DNS_ANSWER);
+
+ if (ans.cls == PP_HTONS(DNS_RRCLASS_IN)) {
+#if LWIP_IPV4
+ if ((ans.type == PP_HTONS(DNS_RRTYPE_A)) && (ans.len == PP_HTONS(sizeof(ip4_addr_t)))) {
+#if LWIP_IPV4 && LWIP_IPV6
+ if (!LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype))
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+ {
+ ip4_addr_t ip4addr;
+ /* read the IP address after answer resource record's header */
+ if (pbuf_copy_partial(p, &ip4addr, sizeof(ip4_addr_t), res_idx) != sizeof(ip4_addr_t)) {
+ goto ignore_packet; /* ignore this packet */
+ }
+ ip_addr_copy_from_ip4(dns_table[i].ipaddr, ip4addr);
+ pbuf_free(p);
+ /* handle correct response */
+ dns_correct_response(i, lwip_ntohl(ans.ttl));
+ return;
+ }
+ }
+#endif /* LWIP_IPV4 */
+#if LWIP_IPV6
+ if ((ans.type == PP_HTONS(DNS_RRTYPE_AAAA)) && (ans.len == PP_HTONS(sizeof(ip6_addr_p_t)))) {
+#if LWIP_IPV4 && LWIP_IPV6
+ if (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype))
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+ {
+ ip6_addr_p_t ip6addr;
+ /* read the IP address after answer resource record's header */
+ if (pbuf_copy_partial(p, &ip6addr, sizeof(ip6_addr_p_t), res_idx) != sizeof(ip6_addr_p_t)) {
+ goto ignore_packet; /* ignore this packet */
+ }
+ /* @todo: scope ip6addr? Might be required for link-local addresses at least? */
+ ip_addr_copy_from_ip6_packed(dns_table[i].ipaddr, ip6addr);
+ pbuf_free(p);
+ /* handle correct response */
+ dns_correct_response(i, lwip_ntohl(ans.ttl));
+ return;
+ }
+ }
+#endif /* LWIP_IPV6 */
+ }
+ /* skip this answer */
+ if ((int)(res_idx + lwip_htons(ans.len)) > 0xFFFF) {
+ goto ignore_packet; /* ignore this packet */
+ }
+ res_idx = (u16_t)(res_idx + lwip_htons(ans.len));
+ --nanswers;
+ }
+#if LWIP_IPV4 && LWIP_IPV6
+ if ((entry->reqaddrtype == LWIP_DNS_ADDRTYPE_IPV4_IPV6) ||
+ (entry->reqaddrtype == LWIP_DNS_ADDRTYPE_IPV6_IPV4)) {
+ if (entry->reqaddrtype == LWIP_DNS_ADDRTYPE_IPV4_IPV6) {
+ /* IPv4 failed, try IPv6 */
+ dns_table[i].reqaddrtype = LWIP_DNS_ADDRTYPE_IPV6;
+ } else {
+ /* IPv6 failed, try IPv4 */
+ dns_table[i].reqaddrtype = LWIP_DNS_ADDRTYPE_IPV4;
+ }
+ pbuf_free(p);
+ dns_table[i].state = DNS_STATE_NEW;
+ dns_check_entry(i);
+ return;
+ }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in response\n", entry->name));
+ }
+ /* call callback to indicate error, clean up memory and return */
+ pbuf_free(p);
+ dns_call_found(i, NULL);
+ dns_table[i].state = DNS_STATE_UNUSED;
+ return;
+ }
+ }
+ }
+
+ignore_packet:
+ /* deallocate memory and return */
+ pbuf_free(p);
+ return;
+}
+
+/**
+ * Queues a new hostname to resolve and sends out a DNS query for that hostname
+ *
+ * @param name the hostname that is to be queried
+ * @param hostnamelen length of the hostname
+ * @param found a callback function to be called on success, failure or timeout
+ * @param callback_arg argument to pass to the callback function
+ * @return err_t return code.
+ */
+static err_t
+dns_enqueue(const char *name, size_t hostnamelen, dns_found_callback found,
+ void *callback_arg LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype) LWIP_DNS_ISMDNS_ARG(u8_t is_mdns))
+{
+ u8_t i;
+ u8_t lseq, lseqi;
+ struct dns_table_entry *entry = NULL;
+ size_t namelen;
+ struct dns_req_entry *req;
+
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
+ u8_t r;
+ /* check for duplicate entries */
+ for (i = 0; i < DNS_TABLE_SIZE; i++) {
+ if ((dns_table[i].state == DNS_STATE_ASKING) &&
+ (lwip_strnicmp(name, dns_table[i].name, sizeof(dns_table[i].name)) == 0)) {
+#if LWIP_IPV4 && LWIP_IPV6
+ if (dns_table[i].reqaddrtype != dns_addrtype) {
+ /* requested address types don't match
+ this can lead to 2 concurrent requests, but mixing the address types
+ for the same host should not be that common */
+ continue;
+ }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+ /* this is a duplicate entry, find a free request entry */
+ for (r = 0; r < DNS_MAX_REQUESTS; r++) {
+ if (dns_requests[r].found == 0) {
+ dns_requests[r].found = found;
+ dns_requests[r].arg = callback_arg;
+ dns_requests[r].dns_table_idx = i;
+ LWIP_DNS_SET_ADDRTYPE(dns_requests[r].reqaddrtype, dns_addrtype);
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": duplicate request\n", name));
+ return ERR_INPROGRESS;
+ }
+ }
+ }
+ }
+ /* no duplicate entries found */
+#endif
+
+ /* search an unused entry, or the oldest one */
+ lseq = 0;
+ lseqi = DNS_TABLE_SIZE;
+ for (i = 0; i < DNS_TABLE_SIZE; ++i) {
+ entry = &dns_table[i];
+ /* is it an unused entry ? */
+ if (entry->state == DNS_STATE_UNUSED) {
+ break;
+ }
+ /* check if this is the oldest completed entry */
+ if (entry->state == DNS_STATE_DONE) {
+ u8_t age = (u8_t)(dns_seqno - entry->seqno);
+ if (age > lseq) {
+ lseq = age;
+ lseqi = i;
+ }
+ }
+ }
+
+ /* if we don't have found an unused entry, use the oldest completed one */
+ if (i == DNS_TABLE_SIZE) {
+ if ((lseqi >= DNS_TABLE_SIZE) || (dns_table[lseqi].state != DNS_STATE_DONE)) {
+ /* no entry can be used now, table is full */
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS entries table is full\n", name));
+ return ERR_MEM;
+ } else {
+ /* use the oldest completed one */
+ i = lseqi;
+ entry = &dns_table[i];
+ }
+ }
+
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
+ /* find a free request entry */
+ req = NULL;
+ for (r = 0; r < DNS_MAX_REQUESTS; r++) {
+ if (dns_requests[r].found == NULL) {
+ req = &dns_requests[r];
+ break;
+ }
+ }
+ if (req == NULL) {
+ /* no request entry can be used now, table is full */
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS request entries table is full\n", name));
+ return ERR_MEM;
+ }
+ req->dns_table_idx = i;
+#else
+ /* in this configuration, the entry index is the same as the request index */
+ req = &dns_requests[i];
+#endif
+
+ /* use this entry */
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS entry %"U16_F"\n", name, (u16_t)(i)));
+
+ /* fill the entry */
+ entry->state = DNS_STATE_NEW;
+ entry->seqno = dns_seqno;
+ LWIP_DNS_SET_ADDRTYPE(entry->reqaddrtype, dns_addrtype);
+ LWIP_DNS_SET_ADDRTYPE(req->reqaddrtype, dns_addrtype);
+ req->found = found;
+ req->arg = callback_arg;
+ namelen = LWIP_MIN(hostnamelen, DNS_MAX_NAME_LENGTH - 1);
+ MEMCPY(entry->name, name, namelen);
+ entry->name[namelen] = 0;
+
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
+ entry->pcb_idx = dns_alloc_pcb();
+ if (entry->pcb_idx >= DNS_MAX_SOURCE_PORTS) {
+ /* failed to get a UDP pcb */
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": failed to allocate a pcb\n", name));
+ entry->state = DNS_STATE_UNUSED;
+ req->found = NULL;
+ return ERR_MEM;
+ }
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS pcb %"U16_F"\n", name, (u16_t)(entry->pcb_idx)));
+#endif
+
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+ entry->is_mdns = is_mdns;
+#endif
+
+ dns_seqno++;
+
+ /* force to send query without waiting timer */
+ dns_check_entry(i);
+
+ /* dns query is enqueued */
+ return ERR_INPROGRESS;
+}
+
+/**
+ * @ingroup dns
+ * Resolve a hostname (string) into an IP address.
+ * NON-BLOCKING callback version for use with raw API!!!
+ *
+ * Returns immediately with one of err_t return codes:
+ * - ERR_OK if hostname is a valid IP address string or the host
+ * name is already in the local names table.
+ * - ERR_INPROGRESS enqueue a request to be sent to the DNS server
+ * for resolution if no errors are present.
+ * - ERR_ARG: dns client not initialized or invalid hostname
+ *
+ * @param hostname the hostname that is to be queried
+ * @param addr pointer to a ip_addr_t where to store the address if it is already
+ * cached in the dns_table (only valid if ERR_OK is returned!)
+ * @param found a callback function to be called on success, failure or timeout (only if
+ * ERR_INPROGRESS is returned!)
+ * @param callback_arg argument to pass to the callback function
+ * @return a err_t return code.
+ */
+err_t
+dns_gethostbyname(const char *hostname, ip_addr_t *addr, dns_found_callback found,
+ void *callback_arg)
+{
+ return dns_gethostbyname_addrtype(hostname, addr, found, callback_arg, LWIP_DNS_ADDRTYPE_DEFAULT);
+}
+
+/**
+ * @ingroup dns
+ * Like dns_gethostbyname, but returned address type can be controlled:
+ * @param hostname the hostname that is to be queried
+ * @param addr pointer to a ip_addr_t where to store the address if it is already
+ * cached in the dns_table (only valid if ERR_OK is returned!)
+ * @param found a callback function to be called on success, failure or timeout (only if
+ * ERR_INPROGRESS is returned!)
+ * @param callback_arg argument to pass to the callback function
+ * @param dns_addrtype - LWIP_DNS_ADDRTYPE_IPV4_IPV6: try to resolve IPv4 first, try IPv6 if IPv4 fails only
+ * - LWIP_DNS_ADDRTYPE_IPV6_IPV4: try to resolve IPv6 first, try IPv4 if IPv6 fails only
+ * - LWIP_DNS_ADDRTYPE_IPV4: try to resolve IPv4 only
+ * - LWIP_DNS_ADDRTYPE_IPV6: try to resolve IPv6 only
+ */
+err_t
+dns_gethostbyname_addrtype(const char *hostname, ip_addr_t *addr, dns_found_callback found,
+ void *callback_arg, u8_t dns_addrtype)
+{
+ size_t hostnamelen;
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+ u8_t is_mdns;
+#endif
+ /* not initialized or no valid server yet, or invalid addr pointer
+ * or invalid hostname or invalid hostname length */
+ if ((addr == NULL) ||
+ (!hostname) || (!hostname[0])) {
+ return ERR_ARG;
+ }
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) == 0)
+ if (dns_pcbs[0] == NULL) {
+ return ERR_ARG;
+ }
+#endif
+ hostnamelen = strlen(hostname);
+ if (hostnamelen >= DNS_MAX_NAME_LENGTH) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_gethostbyname: name too long to resolve"));
+ return ERR_ARG;
+ }
+
+
+#if LWIP_HAVE_LOOPIF
+ if (strcmp(hostname, "localhost") == 0) {
+ ip_addr_set_loopback(LWIP_DNS_ADDRTYPE_IS_IPV6(dns_addrtype), addr);
+ return ERR_OK;
+ }
+#endif /* LWIP_HAVE_LOOPIF */
+
+ /* host name already in octet notation? set ip addr and return ERR_OK */
+ if (ipaddr_aton(hostname, addr)) {
+#if LWIP_IPV4 && LWIP_IPV6
+ if ((IP_IS_V6(addr) && (dns_addrtype != LWIP_DNS_ADDRTYPE_IPV4)) ||
+ (IP_IS_V4(addr) && (dns_addrtype != LWIP_DNS_ADDRTYPE_IPV6)))
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+ {
+ return ERR_OK;
+ }
+ }
+ /* already have this address cached? */
+ if (dns_lookup(hostname, addr LWIP_DNS_ADDRTYPE_ARG(dns_addrtype)) == ERR_OK) {
+ return ERR_OK;
+ }
+#if LWIP_IPV4 && LWIP_IPV6
+ if ((dns_addrtype == LWIP_DNS_ADDRTYPE_IPV4_IPV6) || (dns_addrtype == LWIP_DNS_ADDRTYPE_IPV6_IPV4)) {
+ /* fallback to 2nd IP type and try again to lookup */
+ u8_t fallback;
+ if (dns_addrtype == LWIP_DNS_ADDRTYPE_IPV4_IPV6) {
+ fallback = LWIP_DNS_ADDRTYPE_IPV6;
+ } else {
+ fallback = LWIP_DNS_ADDRTYPE_IPV4;
+ }
+ if (dns_lookup(hostname, addr LWIP_DNS_ADDRTYPE_ARG(fallback)) == ERR_OK) {
+ return ERR_OK;
+ }
+ }
+#else /* LWIP_IPV4 && LWIP_IPV6 */
+ LWIP_UNUSED_ARG(dns_addrtype);
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+ if (strstr(hostname, ".local") == &hostname[hostnamelen] - 6) {
+ is_mdns = 1;
+ } else {
+ is_mdns = 0;
+ }
+
+ if (!is_mdns)
+#endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */
+ {
+ /* prevent calling found callback if no server is set, return error instead */
+ if (ip_addr_isany_val(dns_servers[0])) {
+ return ERR_VAL;
+ }
+ }
+
+ /* queue query with specified callback */
+ return dns_enqueue(hostname, hostnamelen, found, callback_arg LWIP_DNS_ADDRTYPE_ARG(dns_addrtype)
+ LWIP_DNS_ISMDNS_ARG(is_mdns));
+}
+
+#endif /* LWIP_DNS */
diff --git a/resources/ethernet/LwIP/core/inet_chksum.c b/resources/ethernet/LwIP/core/inet_chksum.c
new file mode 100644
index 00000000..25255b90
--- /dev/null
+++ b/resources/ethernet/LwIP/core/inet_chksum.c
@@ -0,0 +1,608 @@
+/**
+ * @file
+ * Internet checksum functions.\n
+ *
+ * These are some reference implementations of the checksum algorithm, with the
+ * aim of being simple, correct and fully portable. Checksumming is the
+ * first thing you would want to optimize for your platform. If you create
+ * your own version, link it in and in your cc.h put:
+ *
+ * \#define LWIP_CHKSUM your_checksum_routine
+ *
+ * Or you can select from the implementations below by defining
+ * LWIP_CHKSUM_ALGORITHM to 1, 2 or 3.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/inet_chksum.h"
+#include "lwip/def.h"
+#include "lwip/ip_addr.h"
+
+#include
+
+#ifndef LWIP_CHKSUM
+# define LWIP_CHKSUM lwip_standard_chksum
+# ifndef LWIP_CHKSUM_ALGORITHM
+# define LWIP_CHKSUM_ALGORITHM 2
+# endif
+u16_t lwip_standard_chksum(const void *dataptr, int len);
+#endif
+/* If none set: */
+#ifndef LWIP_CHKSUM_ALGORITHM
+# define LWIP_CHKSUM_ALGORITHM 0
+#endif
+
+#if (LWIP_CHKSUM_ALGORITHM == 1) /* Version #1 */
+/**
+ * lwip checksum
+ *
+ * @param dataptr points to start of data to be summed at any boundary
+ * @param len length of data to be summed
+ * @return host order (!) lwip checksum (non-inverted Internet sum)
+ *
+ * @note accumulator size limits summable length to 64k
+ * @note host endianess is irrelevant (p3 RFC1071)
+ */
+u16_t
+lwip_standard_chksum(const void *dataptr, int len)
+{
+ u32_t acc;
+ u16_t src;
+ const u8_t *octetptr;
+
+ acc = 0;
+ /* dataptr may be at odd or even addresses */
+ octetptr = (const u8_t *)dataptr;
+ while (len > 1) {
+ /* declare first octet as most significant
+ thus assume network order, ignoring host order */
+ src = (*octetptr) << 8;
+ octetptr++;
+ /* declare second octet as least significant */
+ src |= (*octetptr);
+ octetptr++;
+ acc += src;
+ len -= 2;
+ }
+ if (len > 0) {
+ /* accumulate remaining octet */
+ src = (*octetptr) << 8;
+ acc += src;
+ }
+ /* add deferred carry bits */
+ acc = (acc >> 16) + (acc & 0x0000ffffUL);
+ if ((acc & 0xffff0000UL) != 0) {
+ acc = (acc >> 16) + (acc & 0x0000ffffUL);
+ }
+ /* This maybe a little confusing: reorder sum using lwip_htons()
+ instead of lwip_ntohs() since it has a little less call overhead.
+ The caller must invert bits for Internet sum ! */
+ return lwip_htons((u16_t)acc);
+}
+#endif
+
+#if (LWIP_CHKSUM_ALGORITHM == 2) /* Alternative version #2 */
+/*
+ * Curt McDowell
+ * Broadcom Corp.
+ * csm@broadcom.com
+ *
+ * IP checksum two bytes at a time with support for
+ * unaligned buffer.
+ * Works for len up to and including 0x20000.
+ * by Curt McDowell, Broadcom Corp. 12/08/2005
+ *
+ * @param dataptr points to start of data to be summed at any boundary
+ * @param len length of data to be summed
+ * @return host order (!) lwip checksum (non-inverted Internet sum)
+ */
+u16_t
+lwip_standard_chksum(const void *dataptr, int len)
+{
+ const u8_t *pb = (const u8_t *)dataptr;
+ const u16_t *ps;
+ u16_t t = 0;
+ u32_t sum = 0;
+ int odd = ((mem_ptr_t)pb & 1);
+
+ /* Get aligned to u16_t */
+ if (odd && len > 0) {
+ ((u8_t *)&t)[1] = *pb++;
+ len--;
+ }
+
+ /* Add the bulk of the data */
+ ps = (const u16_t *)(const void *)pb;
+ while (len > 1) {
+ sum += *ps++;
+ len -= 2;
+ }
+
+ /* Consume left-over byte, if any */
+ if (len > 0) {
+ ((u8_t *)&t)[0] = *(const u8_t *)ps;
+ }
+
+ /* Add end bytes */
+ sum += t;
+
+ /* Fold 32-bit sum to 16 bits
+ calling this twice is probably faster than if statements... */
+ sum = FOLD_U32T(sum);
+ sum = FOLD_U32T(sum);
+
+ /* Swap if alignment was odd */
+ if (odd) {
+ sum = SWAP_BYTES_IN_WORD(sum);
+ }
+
+ return (u16_t)sum;
+}
+#endif
+
+#if (LWIP_CHKSUM_ALGORITHM == 3) /* Alternative version #3 */
+/**
+ * An optimized checksum routine. Basically, it uses loop-unrolling on
+ * the checksum loop, treating the head and tail bytes specially, whereas
+ * the inner loop acts on 8 bytes at a time.
+ *
+ * @arg start of buffer to be checksummed. May be an odd byte address.
+ * @len number of bytes in the buffer to be checksummed.
+ * @return host order (!) lwip checksum (non-inverted Internet sum)
+ *
+ * by Curt McDowell, Broadcom Corp. December 8th, 2005
+ */
+u16_t
+lwip_standard_chksum(const void *dataptr, int len)
+{
+ const u8_t *pb = (const u8_t *)dataptr;
+ const u16_t *ps;
+ u16_t t = 0;
+ const u32_t *pl;
+ u32_t sum = 0, tmp;
+ /* starts at odd byte address? */
+ int odd = ((mem_ptr_t)pb & 1);
+
+ if (odd && len > 0) {
+ ((u8_t *)&t)[1] = *pb++;
+ len--;
+ }
+
+ ps = (const u16_t *)(const void *)pb;
+
+ if (((mem_ptr_t)ps & 3) && len > 1) {
+ sum += *ps++;
+ len -= 2;
+ }
+
+ pl = (const u32_t *)(const void *)ps;
+
+ while (len > 7) {
+ tmp = sum + *pl++; /* ping */
+ if (tmp < sum) {
+ tmp++; /* add back carry */
+ }
+
+ sum = tmp + *pl++; /* pong */
+ if (sum < tmp) {
+ sum++; /* add back carry */
+ }
+
+ len -= 8;
+ }
+
+ /* make room in upper bits */
+ sum = FOLD_U32T(sum);
+
+ ps = (const u16_t *)pl;
+
+ /* 16-bit aligned word remaining? */
+ while (len > 1) {
+ sum += *ps++;
+ len -= 2;
+ }
+
+ /* dangling tail byte remaining? */
+ if (len > 0) { /* include odd byte */
+ ((u8_t *)&t)[0] = *(const u8_t *)ps;
+ }
+
+ sum += t; /* add end bytes */
+
+ /* Fold 32-bit sum to 16 bits
+ calling this twice is probably faster than if statements... */
+ sum = FOLD_U32T(sum);
+ sum = FOLD_U32T(sum);
+
+ if (odd) {
+ sum = SWAP_BYTES_IN_WORD(sum);
+ }
+
+ return (u16_t)sum;
+}
+#endif
+
+/** Parts of the pseudo checksum which are common to IPv4 and IPv6 */
+static u16_t
+inet_cksum_pseudo_base(struct pbuf *p, u8_t proto, u16_t proto_len, u32_t acc)
+{
+ struct pbuf *q;
+ int swapped = 0;
+
+ /* iterate through all pbuf in chain */
+ for (q = p; q != NULL; q = q->next) {
+ LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n",
+ (void *)q, (void *)q->next));
+ acc += LWIP_CHKSUM(q->payload, q->len);
+ /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/
+ /* just executing this next line is probably faster that the if statement needed
+ to check whether we really need to execute it, and does no harm */
+ acc = FOLD_U32T(acc);
+ if (q->len % 2 != 0) {
+ swapped = !swapped;
+ acc = SWAP_BYTES_IN_WORD(acc);
+ }
+ /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/
+ }
+
+ if (swapped) {
+ acc = SWAP_BYTES_IN_WORD(acc);
+ }
+
+ acc += (u32_t)lwip_htons((u16_t)proto);
+ acc += (u32_t)lwip_htons(proto_len);
+
+ /* Fold 32-bit sum to 16 bits
+ calling this twice is probably faster than if statements... */
+ acc = FOLD_U32T(acc);
+ acc = FOLD_U32T(acc);
+ LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc));
+ return (u16_t)~(acc & 0xffffUL);
+}
+
+#if LWIP_IPV4
+/* inet_chksum_pseudo:
+ *
+ * Calculates the IPv4 pseudo Internet checksum used by TCP and UDP for a pbuf chain.
+ * IP addresses are expected to be in network byte order.
+ *
+ * @param p chain of pbufs over that a checksum should be calculated (ip data part)
+ * @param src source ip address (used for checksum of pseudo header)
+ * @param dst destination ip address (used for checksum of pseudo header)
+ * @param proto ip protocol (used for checksum of pseudo header)
+ * @param proto_len length of the ip data part (used for checksum of pseudo header)
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+inet_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
+ const ip4_addr_t *src, const ip4_addr_t *dest)
+{
+ u32_t acc;
+ u32_t addr;
+
+ addr = ip4_addr_get_u32(src);
+ acc = (addr & 0xffffUL);
+ acc = (u32_t)(acc + ((addr >> 16) & 0xffffUL));
+ addr = ip4_addr_get_u32(dest);
+ acc = (u32_t)(acc + (addr & 0xffffUL));
+ acc = (u32_t)(acc + ((addr >> 16) & 0xffffUL));
+ /* fold down to 16 bits */
+ acc = FOLD_U32T(acc);
+ acc = FOLD_U32T(acc);
+
+ return inet_cksum_pseudo_base(p, proto, proto_len, acc);
+}
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV6
+/**
+ * Calculates the checksum with IPv6 pseudo header used by TCP and UDP for a pbuf chain.
+ * IPv6 addresses are expected to be in network byte order.
+ *
+ * @param p chain of pbufs over that a checksum should be calculated (ip data part)
+ * @param proto ipv6 protocol/next header (used for checksum of pseudo header)
+ * @param proto_len length of the ipv6 payload (used for checksum of pseudo header)
+ * @param src source ipv6 address (used for checksum of pseudo header)
+ * @param dest destination ipv6 address (used for checksum of pseudo header)
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+ip6_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
+ const ip6_addr_t *src, const ip6_addr_t *dest)
+{
+ u32_t acc = 0;
+ u32_t addr;
+ u8_t addr_part;
+
+ for (addr_part = 0; addr_part < 4; addr_part++) {
+ addr = src->addr[addr_part];
+ acc = (u32_t)(acc + (addr & 0xffffUL));
+ acc = (u32_t)(acc + ((addr >> 16) & 0xffffUL));
+ addr = dest->addr[addr_part];
+ acc = (u32_t)(acc + (addr & 0xffffUL));
+ acc = (u32_t)(acc + ((addr >> 16) & 0xffffUL));
+ }
+ /* fold down to 16 bits */
+ acc = FOLD_U32T(acc);
+ acc = FOLD_U32T(acc);
+
+ return inet_cksum_pseudo_base(p, proto, proto_len, acc);
+}
+#endif /* LWIP_IPV6 */
+
+/* ip_chksum_pseudo:
+ *
+ * Calculates the IPv4 or IPv6 pseudo Internet checksum used by TCP and UDP for a pbuf chain.
+ * IP addresses are expected to be in network byte order.
+ *
+ * @param p chain of pbufs over that a checksum should be calculated (ip data part)
+ * @param src source ip address (used for checksum of pseudo header)
+ * @param dst destination ip address (used for checksum of pseudo header)
+ * @param proto ip protocol (used for checksum of pseudo header)
+ * @param proto_len length of the ip data part (used for checksum of pseudo header)
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+ip_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
+ const ip_addr_t *src, const ip_addr_t *dest)
+{
+#if LWIP_IPV6
+ if (IP_IS_V6(dest)) {
+ return ip6_chksum_pseudo(p, proto, proto_len, ip_2_ip6(src), ip_2_ip6(dest));
+ }
+#endif /* LWIP_IPV6 */
+#if LWIP_IPV4 && LWIP_IPV6
+ else
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+#if LWIP_IPV4
+ {
+ return inet_chksum_pseudo(p, proto, proto_len, ip_2_ip4(src), ip_2_ip4(dest));
+ }
+#endif /* LWIP_IPV4 */
+}
+
+/** Parts of the pseudo checksum which are common to IPv4 and IPv6 */
+static u16_t
+inet_cksum_pseudo_partial_base(struct pbuf *p, u8_t proto, u16_t proto_len,
+ u16_t chksum_len, u32_t acc)
+{
+ struct pbuf *q;
+ int swapped = 0;
+ u16_t chklen;
+
+ /* iterate through all pbuf in chain */
+ for (q = p; (q != NULL) && (chksum_len > 0); q = q->next) {
+ LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n",
+ (void *)q, (void *)q->next));
+ chklen = q->len;
+ if (chklen > chksum_len) {
+ chklen = chksum_len;
+ }
+ acc += LWIP_CHKSUM(q->payload, chklen);
+ chksum_len = (u16_t)(chksum_len - chklen);
+ LWIP_ASSERT("delete me", chksum_len < 0x7fff);
+ /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/
+ /* fold the upper bit down */
+ acc = FOLD_U32T(acc);
+ if (q->len % 2 != 0) {
+ swapped = !swapped;
+ acc = SWAP_BYTES_IN_WORD(acc);
+ }
+ /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/
+ }
+
+ if (swapped) {
+ acc = SWAP_BYTES_IN_WORD(acc);
+ }
+
+ acc += (u32_t)lwip_htons((u16_t)proto);
+ acc += (u32_t)lwip_htons(proto_len);
+
+ /* Fold 32-bit sum to 16 bits
+ calling this twice is probably faster than if statements... */
+ acc = FOLD_U32T(acc);
+ acc = FOLD_U32T(acc);
+ LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc));
+ return (u16_t)~(acc & 0xffffUL);
+}
+
+#if LWIP_IPV4
+/* inet_chksum_pseudo_partial:
+ *
+ * Calculates the IPv4 pseudo Internet checksum used by TCP and UDP for a pbuf chain.
+ * IP addresses are expected to be in network byte order.
+ *
+ * @param p chain of pbufs over that a checksum should be calculated (ip data part)
+ * @param src source ip address (used for checksum of pseudo header)
+ * @param dst destination ip address (used for checksum of pseudo header)
+ * @param proto ip protocol (used for checksum of pseudo header)
+ * @param proto_len length of the ip data part (used for checksum of pseudo header)
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+inet_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len,
+ u16_t chksum_len, const ip4_addr_t *src, const ip4_addr_t *dest)
+{
+ u32_t acc;
+ u32_t addr;
+
+ addr = ip4_addr_get_u32(src);
+ acc = (addr & 0xffffUL);
+ acc = (u32_t)(acc + ((addr >> 16) & 0xffffUL));
+ addr = ip4_addr_get_u32(dest);
+ acc = (u32_t)(acc + (addr & 0xffffUL));
+ acc = (u32_t)(acc + ((addr >> 16) & 0xffffUL));
+ /* fold down to 16 bits */
+ acc = FOLD_U32T(acc);
+ acc = FOLD_U32T(acc);
+
+ return inet_cksum_pseudo_partial_base(p, proto, proto_len, chksum_len, acc);
+}
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV6
+/**
+ * Calculates the checksum with IPv6 pseudo header used by TCP and UDP for a pbuf chain.
+ * IPv6 addresses are expected to be in network byte order. Will only compute for a
+ * portion of the payload.
+ *
+ * @param p chain of pbufs over that a checksum should be calculated (ip data part)
+ * @param proto ipv6 protocol/next header (used for checksum of pseudo header)
+ * @param proto_len length of the ipv6 payload (used for checksum of pseudo header)
+ * @param chksum_len number of payload bytes used to compute chksum
+ * @param src source ipv6 address (used for checksum of pseudo header)
+ * @param dest destination ipv6 address (used for checksum of pseudo header)
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+ip6_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len,
+ u16_t chksum_len, const ip6_addr_t *src, const ip6_addr_t *dest)
+{
+ u32_t acc = 0;
+ u32_t addr;
+ u8_t addr_part;
+
+ for (addr_part = 0; addr_part < 4; addr_part++) {
+ addr = src->addr[addr_part];
+ acc = (u32_t)(acc + (addr & 0xffffUL));
+ acc = (u32_t)(acc + ((addr >> 16) & 0xffffUL));
+ addr = dest->addr[addr_part];
+ acc = (u32_t)(acc + (addr & 0xffffUL));
+ acc = (u32_t)(acc + ((addr >> 16) & 0xffffUL));
+ }
+ /* fold down to 16 bits */
+ acc = FOLD_U32T(acc);
+ acc = FOLD_U32T(acc);
+
+ return inet_cksum_pseudo_partial_base(p, proto, proto_len, chksum_len, acc);
+}
+#endif /* LWIP_IPV6 */
+
+/* ip_chksum_pseudo_partial:
+ *
+ * Calculates the IPv4 or IPv6 pseudo Internet checksum used by TCP and UDP for a pbuf chain.
+ *
+ * @param p chain of pbufs over that a checksum should be calculated (ip data part)
+ * @param src source ip address (used for checksum of pseudo header)
+ * @param dst destination ip address (used for checksum of pseudo header)
+ * @param proto ip protocol (used for checksum of pseudo header)
+ * @param proto_len length of the ip data part (used for checksum of pseudo header)
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+ip_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len,
+ u16_t chksum_len, const ip_addr_t *src, const ip_addr_t *dest)
+{
+#if LWIP_IPV6
+ if (IP_IS_V6(dest)) {
+ return ip6_chksum_pseudo_partial(p, proto, proto_len, chksum_len, ip_2_ip6(src), ip_2_ip6(dest));
+ }
+#endif /* LWIP_IPV6 */
+#if LWIP_IPV4 && LWIP_IPV6
+ else
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+#if LWIP_IPV4
+ {
+ return inet_chksum_pseudo_partial(p, proto, proto_len, chksum_len, ip_2_ip4(src), ip_2_ip4(dest));
+ }
+#endif /* LWIP_IPV4 */
+}
+
+/* inet_chksum:
+ *
+ * Calculates the Internet checksum over a portion of memory. Used primarily for IP
+ * and ICMP.
+ *
+ * @param dataptr start of the buffer to calculate the checksum (no alignment needed)
+ * @param len length of the buffer to calculate the checksum
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+
+u16_t
+inet_chksum(const void *dataptr, u16_t len)
+{
+ return (u16_t)~(unsigned int)LWIP_CHKSUM(dataptr, len);
+}
+
+/**
+ * Calculate a checksum over a chain of pbufs (without pseudo-header, much like
+ * inet_chksum only pbufs are used).
+ *
+ * @param p pbuf chain over that the checksum should be calculated
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+inet_chksum_pbuf(struct pbuf *p)
+{
+ u32_t acc;
+ struct pbuf *q;
+ int swapped = 0;
+
+ acc = 0;
+ for (q = p; q != NULL; q = q->next) {
+ acc += LWIP_CHKSUM(q->payload, q->len);
+ acc = FOLD_U32T(acc);
+ if (q->len % 2 != 0) {
+ swapped = !swapped;
+ acc = SWAP_BYTES_IN_WORD(acc);
+ }
+ }
+
+ if (swapped) {
+ acc = SWAP_BYTES_IN_WORD(acc);
+ }
+ return (u16_t)~(acc & 0xffffUL);
+}
+
+/* These are some implementations for LWIP_CHKSUM_COPY, which copies data
+ * like MEMCPY but generates a checksum at the same time. Since this is a
+ * performance-sensitive function, you might want to create your own version
+ * in assembly targeted at your hardware by defining it in lwipopts.h:
+ * #define LWIP_CHKSUM_COPY(dst, src, len) your_chksum_copy(dst, src, len)
+ */
+
+#if (LWIP_CHKSUM_COPY_ALGORITHM == 1) /* Version #1 */
+/** Safe but slow: first call MEMCPY, then call LWIP_CHKSUM.
+ * For architectures with big caches, data might still be in cache when
+ * generating the checksum after copying.
+ */
+u16_t
+lwip_chksum_copy(void *dst, const void *src, u16_t len)
+{
+ MEMCPY(dst, src, len);
+ return LWIP_CHKSUM(dst, len);
+}
+#endif /* (LWIP_CHKSUM_COPY_ALGORITHM == 1) */
diff --git a/resources/ethernet/LwIP/core/init.c b/resources/ethernet/LwIP/core/init.c
new file mode 100644
index 00000000..b6885364
--- /dev/null
+++ b/resources/ethernet/LwIP/core/init.c
@@ -0,0 +1,380 @@
+/**
+ * @file
+ * Modules initialization
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/init.h"
+#include "lwip/stats.h"
+#include "lwip/sys.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/pbuf.h"
+#include "lwip/netif.h"
+#include "lwip/sockets.h"
+#include "lwip/ip.h"
+#include "lwip/raw.h"
+#include "lwip/udp.h"
+#include "lwip/priv/tcp_priv.h"
+#include "lwip/igmp.h"
+#include "lwip/dns.h"
+#include "lwip/timeouts.h"
+#include "lwip/etharp.h"
+#include "lwip/ip6.h"
+#include "lwip/nd6.h"
+#include "lwip/mld6.h"
+#include "lwip/api.h"
+
+#include "netif/ppp/ppp_opts.h"
+#include "netif/ppp/ppp_impl.h"
+
+#ifndef LWIP_SKIP_PACKING_CHECK
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct packed_struct_test {
+ PACK_STRUCT_FLD_8(u8_t dummy1);
+ PACK_STRUCT_FIELD(u32_t dummy2);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+#define PACKED_STRUCT_TEST_EXPECTED_SIZE 5
+
+#endif
+
+/* Compile-time sanity checks for configuration errors.
+ * These can be done independently of LWIP_DEBUG, without penalty.
+ */
+#ifndef BYTE_ORDER
+#error "BYTE_ORDER is not defined, you have to define it in your cc.h"
+#endif
+#if (!IP_SOF_BROADCAST && IP_SOF_BROADCAST_RECV)
+#error "If you want to use broadcast filter per pcb on recv operations, you have to define IP_SOF_BROADCAST=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_UDPLITE)
+#error "If you want to use UDP Lite, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_DHCP)
+#error "If you want to use DHCP, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && !LWIP_RAW && LWIP_MULTICAST_TX_OPTIONS)
+#error "If you want to use LWIP_MULTICAST_TX_OPTIONS, you have to define LWIP_UDP=1 and/or LWIP_RAW=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_DNS)
+#error "If you want to use DNS, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if !MEMP_MEM_MALLOC /* MEMP_NUM_* checks are disabled when not using the pool allocator */
+#if (LWIP_ARP && ARP_QUEUEING && (MEMP_NUM_ARP_QUEUE<=0))
+#error "If you want to use ARP Queueing, you have to define MEMP_NUM_ARP_QUEUE>=1 in your lwipopts.h"
+#endif
+#if (LWIP_RAW && (MEMP_NUM_RAW_PCB<=0))
+#error "If you want to use RAW, you have to define MEMP_NUM_RAW_PCB>=1 in your lwipopts.h"
+#endif
+#if (LWIP_UDP && (MEMP_NUM_UDP_PCB<=0))
+#error "If you want to use UDP, you have to define MEMP_NUM_UDP_PCB>=1 in your lwipopts.h"
+#endif
+#if (LWIP_TCP && (MEMP_NUM_TCP_PCB<=0))
+#error "If you want to use TCP, you have to define MEMP_NUM_TCP_PCB>=1 in your lwipopts.h"
+#endif
+#if (LWIP_IGMP && (MEMP_NUM_IGMP_GROUP<=1))
+#error "If you want to use IGMP, you have to define MEMP_NUM_IGMP_GROUP>1 in your lwipopts.h"
+#endif
+#if (LWIP_IGMP && !LWIP_MULTICAST_TX_OPTIONS)
+#error "If you want to use IGMP, you have to define LWIP_MULTICAST_TX_OPTIONS==1 in your lwipopts.h"
+#endif
+#if (LWIP_IGMP && !LWIP_IPV4)
+#error "IGMP needs LWIP_IPV4 enabled in your lwipopts.h"
+#endif
+#if ((LWIP_NETCONN || LWIP_SOCKET) && (MEMP_NUM_TCPIP_MSG_API<=0))
+#error "If you want to use Sequential API, you have to define MEMP_NUM_TCPIP_MSG_API>=1 in your lwipopts.h"
+#endif
+/* There must be sufficient timeouts, taking into account requirements of the subsystems. */
+#if LWIP_TIMERS && (MEMP_NUM_SYS_TIMEOUT < LWIP_NUM_SYS_TIMEOUT_INTERNAL)
+#error "MEMP_NUM_SYS_TIMEOUT is too low to accomodate all required timeouts"
+#endif
+#if (IP_REASSEMBLY && (MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS))
+#error "MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS doesn't make sense since each struct ip_reassdata must hold 2 pbufs at least!"
+#endif
+#endif /* !MEMP_MEM_MALLOC */
+#if LWIP_WND_SCALE
+#if (LWIP_TCP && (TCP_WND > 0xffffffff))
+#error "If you want to use TCP, TCP_WND must fit in an u32_t, so, you have to reduce it in your lwipopts.h"
+#endif
+#if (LWIP_TCP && (TCP_RCV_SCALE > 14))
+#error "The maximum valid window scale value is 14!"
+#endif
+#if (LWIP_TCP && (TCP_WND > (0xFFFFU << TCP_RCV_SCALE)))
+#error "TCP_WND is bigger than the configured LWIP_WND_SCALE allows!"
+#endif
+#if (LWIP_TCP && ((TCP_WND >> TCP_RCV_SCALE) == 0))
+#error "TCP_WND is too small for the configured LWIP_WND_SCALE (results in zero window)!"
+#endif
+#else /* LWIP_WND_SCALE */
+#if (LWIP_TCP && (TCP_WND > 0xffff))
+#error "If you want to use TCP, TCP_WND must fit in an u16_t, so, you have to reduce it in your lwipopts.h (or enable window scaling)"
+#endif
+#endif /* LWIP_WND_SCALE */
+#if (LWIP_TCP && (TCP_SND_QUEUELEN > 0xffff))
+#error "If you want to use TCP, TCP_SND_QUEUELEN must fit in an u16_t, so, you have to reduce it in your lwipopts.h"
+#endif
+#if (LWIP_TCP && (TCP_SND_QUEUELEN < 2))
+#error "TCP_SND_QUEUELEN must be at least 2 for no-copy TCP writes to work"
+#endif
+#if (LWIP_TCP && ((TCP_MAXRTX > 12) || (TCP_SYNMAXRTX > 12)))
+#error "If you want to use TCP, TCP_MAXRTX and TCP_SYNMAXRTX must less or equal to 12 (due to tcp_backoff table), so, you have to reduce them in your lwipopts.h"
+#endif
+#if (LWIP_TCP && TCP_LISTEN_BACKLOG && ((TCP_DEFAULT_LISTEN_BACKLOG < 0) || (TCP_DEFAULT_LISTEN_BACKLOG > 0xff)))
+#error "If you want to use TCP backlog, TCP_DEFAULT_LISTEN_BACKLOG must fit into an u8_t"
+#endif
+#if (LWIP_TCP && LWIP_TCP_SACK_OUT && !TCP_QUEUE_OOSEQ)
+#error "To use LWIP_TCP_SACK_OUT, TCP_QUEUE_OOSEQ needs to be enabled"
+#endif
+#if (LWIP_TCP && LWIP_TCP_SACK_OUT && (LWIP_TCP_MAX_SACK_NUM < 1))
+#error "LWIP_TCP_MAX_SACK_NUM must be greater than 0"
+#endif
+#if (LWIP_NETIF_API && (NO_SYS==1))
+#error "If you want to use NETIF API, you have to define NO_SYS=0 in your lwipopts.h"
+#endif
+#if ((LWIP_SOCKET || LWIP_NETCONN) && (NO_SYS==1))
+#error "If you want to use Sequential API, you have to define NO_SYS=0 in your lwipopts.h"
+#endif
+#if (LWIP_PPP_API && (NO_SYS==1))
+#error "If you want to use PPP API, you have to define NO_SYS=0 in your lwipopts.h"
+#endif
+#if (LWIP_PPP_API && (PPP_SUPPORT==0))
+#error "If you want to use PPP API, you have to enable PPP_SUPPORT in your lwipopts.h"
+#endif
+#if (((!LWIP_DHCP) || (!LWIP_AUTOIP)) && LWIP_DHCP_AUTOIP_COOP)
+#error "If you want to use DHCP/AUTOIP cooperation mode, you have to define LWIP_DHCP=1 and LWIP_AUTOIP=1 in your lwipopts.h"
+#endif
+#if (((!LWIP_DHCP) || (!LWIP_ARP)) && DHCP_DOES_ARP_CHECK)
+#error "If you want to use DHCP ARP checking, you have to define LWIP_DHCP=1 and LWIP_ARP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_ARP && LWIP_AUTOIP)
+#error "If you want to use AUTOIP, you have to define LWIP_ARP=1 in your lwipopts.h"
+#endif
+#if (LWIP_TCP && ((LWIP_EVENT_API && LWIP_CALLBACK_API) || (!LWIP_EVENT_API && !LWIP_CALLBACK_API)))
+#error "One and exactly one of LWIP_EVENT_API and LWIP_CALLBACK_API has to be enabled in your lwipopts.h"
+#endif
+#if (LWIP_ALTCP && LWIP_EVENT_API)
+#error "The application layered tcp API does not work with LWIP_EVENT_API"
+#endif
+#if (MEM_LIBC_MALLOC && MEM_USE_POOLS)
+#error "MEM_LIBC_MALLOC and MEM_USE_POOLS may not both be simultaneously enabled in your lwipopts.h"
+#endif
+#if (MEM_USE_POOLS && !MEMP_USE_CUSTOM_POOLS)
+#error "MEM_USE_POOLS requires custom pools (MEMP_USE_CUSTOM_POOLS) to be enabled in your lwipopts.h"
+#endif
+#if (PBUF_POOL_BUFSIZE <= MEM_ALIGNMENT)
+#error "PBUF_POOL_BUFSIZE must be greater than MEM_ALIGNMENT or the offset may take the full first pbuf"
+#endif
+#if (DNS_LOCAL_HOSTLIST && !DNS_LOCAL_HOSTLIST_IS_DYNAMIC && !(defined(DNS_LOCAL_HOSTLIST_INIT)))
+#error "you have to define define DNS_LOCAL_HOSTLIST_INIT {{'host1', 0x123}, {'host2', 0x234}} to initialize DNS_LOCAL_HOSTLIST"
+#endif
+#if PPP_SUPPORT && !PPPOS_SUPPORT && !PPPOE_SUPPORT && !PPPOL2TP_SUPPORT
+#error "PPP_SUPPORT needs at least one of PPPOS_SUPPORT, PPPOE_SUPPORT or PPPOL2TP_SUPPORT turned on"
+#endif
+#if PPP_SUPPORT && !PPP_IPV4_SUPPORT && !PPP_IPV6_SUPPORT
+#error "PPP_SUPPORT needs PPP_IPV4_SUPPORT and/or PPP_IPV6_SUPPORT turned on"
+#endif
+#if PPP_SUPPORT && PPP_IPV4_SUPPORT && !LWIP_IPV4
+#error "PPP_IPV4_SUPPORT needs LWIP_IPV4 turned on"
+#endif
+#if PPP_SUPPORT && PPP_IPV6_SUPPORT && !LWIP_IPV6
+#error "PPP_IPV6_SUPPORT needs LWIP_IPV6 turned on"
+#endif
+#if !LWIP_ETHERNET && (LWIP_ARP || PPPOE_SUPPORT)
+#error "LWIP_ETHERNET needs to be turned on for LWIP_ARP or PPPOE_SUPPORT"
+#endif
+#if LWIP_TCPIP_CORE_LOCKING_INPUT && !LWIP_TCPIP_CORE_LOCKING
+#error "When using LWIP_TCPIP_CORE_LOCKING_INPUT, LWIP_TCPIP_CORE_LOCKING must be enabled, too"
+#endif
+#if LWIP_TCP && LWIP_NETIF_TX_SINGLE_PBUF && !TCP_OVERSIZE
+#error "LWIP_NETIF_TX_SINGLE_PBUF needs TCP_OVERSIZE enabled to create single-pbuf TCP packets"
+#endif
+#if LWIP_NETCONN && LWIP_TCP
+#if NETCONN_COPY != TCP_WRITE_FLAG_COPY
+#error "NETCONN_COPY != TCP_WRITE_FLAG_COPY"
+#endif
+#if NETCONN_MORE != TCP_WRITE_FLAG_MORE
+#error "NETCONN_MORE != TCP_WRITE_FLAG_MORE"
+#endif
+#endif /* LWIP_NETCONN && LWIP_TCP */
+#if LWIP_SOCKET
+#endif /* LWIP_SOCKET */
+
+
+/* Compile-time checks for deprecated options.
+ */
+#ifdef MEMP_NUM_TCPIP_MSG
+#error "MEMP_NUM_TCPIP_MSG option is deprecated. Remove it from your lwipopts.h."
+#endif
+#ifdef TCP_REXMIT_DEBUG
+#error "TCP_REXMIT_DEBUG option is deprecated. Remove it from your lwipopts.h."
+#endif
+#ifdef RAW_STATS
+#error "RAW_STATS option is deprecated. Remove it from your lwipopts.h."
+#endif
+#ifdef ETHARP_QUEUE_FIRST
+#error "ETHARP_QUEUE_FIRST option is deprecated. Remove it from your lwipopts.h."
+#endif
+#ifdef ETHARP_ALWAYS_INSERT
+#error "ETHARP_ALWAYS_INSERT option is deprecated. Remove it from your lwipopts.h."
+#endif
+#if !NO_SYS && LWIP_TCPIP_CORE_LOCKING && LWIP_COMPAT_MUTEX && !defined(LWIP_COMPAT_MUTEX_ALLOWED)
+#error "LWIP_COMPAT_MUTEX cannot prevent priority inversion. It is recommended to implement priority-aware mutexes. (Define LWIP_COMPAT_MUTEX_ALLOWED to disable this error.)"
+#endif
+
+#ifndef LWIP_DISABLE_TCP_SANITY_CHECKS
+#define LWIP_DISABLE_TCP_SANITY_CHECKS 0
+#endif
+#ifndef LWIP_DISABLE_MEMP_SANITY_CHECKS
+#define LWIP_DISABLE_MEMP_SANITY_CHECKS 0
+#endif
+
+/* MEMP sanity checks */
+#if MEMP_MEM_MALLOC
+#if !LWIP_DISABLE_MEMP_SANITY_CHECKS
+#if LWIP_NETCONN || LWIP_SOCKET
+#if !MEMP_NUM_NETCONN && LWIP_SOCKET
+#error "lwip_sanity_check: WARNING: MEMP_NUM_NETCONN cannot be 0 when using sockets!"
+#endif
+#else /* MEMP_MEM_MALLOC */
+#if MEMP_NUM_NETCONN > (MEMP_NUM_TCP_PCB+MEMP_NUM_TCP_PCB_LISTEN+MEMP_NUM_UDP_PCB+MEMP_NUM_RAW_PCB)
+#error "lwip_sanity_check: WARNING: MEMP_NUM_NETCONN should be less than the sum of MEMP_NUM_{TCP,RAW,UDP}_PCB+MEMP_NUM_TCP_PCB_LISTEN. If you know what you are doing, define LWIP_DISABLE_MEMP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#endif /* LWIP_NETCONN || LWIP_SOCKET */
+#endif /* !LWIP_DISABLE_MEMP_SANITY_CHECKS */
+#if MEM_USE_POOLS
+#error "MEMP_MEM_MALLOC and MEM_USE_POOLS cannot be enabled at the same time"
+#endif
+#ifdef LWIP_HOOK_MEMP_AVAILABLE
+#error "LWIP_HOOK_MEMP_AVAILABLE doesn't make sense with MEMP_MEM_MALLOC"
+#endif
+#endif /* MEMP_MEM_MALLOC */
+
+/* TCP sanity checks */
+#if !LWIP_DISABLE_TCP_SANITY_CHECKS
+#if LWIP_TCP
+#if !MEMP_MEM_MALLOC && (MEMP_NUM_TCP_SEG < TCP_SND_QUEUELEN)
+#error "lwip_sanity_check: WARNING: MEMP_NUM_TCP_SEG should be at least as big as TCP_SND_QUEUELEN. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#if TCP_SND_BUF < (2 * TCP_MSS)
+#error "lwip_sanity_check: WARNING: TCP_SND_BUF must be at least as much as (2 * TCP_MSS) for things to work smoothly. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#if TCP_SND_QUEUELEN < (2 * (TCP_SND_BUF / TCP_MSS))
+#error "lwip_sanity_check: WARNING: TCP_SND_QUEUELEN must be at least as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#if TCP_SNDLOWAT >= TCP_SND_BUF
+#error "lwip_sanity_check: WARNING: TCP_SNDLOWAT must be less than TCP_SND_BUF. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#if TCP_SNDLOWAT >= (0xFFFF - (4 * TCP_MSS))
+#error "lwip_sanity_check: WARNING: TCP_SNDLOWAT must at least be 4*MSS below u16_t overflow!"
+#endif
+#if TCP_SNDQUEUELOWAT >= TCP_SND_QUEUELEN
+#error "lwip_sanity_check: WARNING: TCP_SNDQUEUELOWAT must be less than TCP_SND_QUEUELEN. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#if !MEMP_MEM_MALLOC && PBUF_POOL_SIZE && (PBUF_POOL_BUFSIZE <= (PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN))
+#error "lwip_sanity_check: WARNING: PBUF_POOL_BUFSIZE does not provide enough space for protocol headers. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#if !MEMP_MEM_MALLOC && PBUF_POOL_SIZE && (TCP_WND > (PBUF_POOL_SIZE * (PBUF_POOL_BUFSIZE - (PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN))))
+#error "lwip_sanity_check: WARNING: TCP_WND is larger than space provided by PBUF_POOL_SIZE * (PBUF_POOL_BUFSIZE - protocol headers). If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#if TCP_WND < TCP_MSS
+#error "lwip_sanity_check: WARNING: TCP_WND is smaller than MSS. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#endif /* LWIP_TCP */
+#endif /* !LWIP_DISABLE_TCP_SANITY_CHECKS */
+
+/**
+ * @ingroup lwip_nosys
+ * Initialize all modules.
+ * Use this in NO_SYS mode. Use tcpip_init() otherwise.
+ */
+void
+lwip_init(void)
+{
+#ifndef LWIP_SKIP_CONST_CHECK
+ int a = 0;
+ LWIP_UNUSED_ARG(a);
+ LWIP_ASSERT("LWIP_CONST_CAST not implemented correctly. Check your lwIP port.", LWIP_CONST_CAST(void *, &a) == &a);
+#endif
+#ifndef LWIP_SKIP_PACKING_CHECK
+ LWIP_ASSERT("Struct packing not implemented correctly. Check your lwIP port.", sizeof(struct packed_struct_test) == PACKED_STRUCT_TEST_EXPECTED_SIZE);
+#endif
+
+ /* Modules initialization */
+ stats_init();
+#if !NO_SYS
+ sys_init();
+#endif /* !NO_SYS */
+ mem_init();
+ memp_init();
+ pbuf_init();
+ netif_init();
+#if LWIP_IPV4
+ ip_init();
+#if LWIP_ARP
+ etharp_init();
+#endif /* LWIP_ARP */
+#endif /* LWIP_IPV4 */
+#if LWIP_RAW
+ raw_init();
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ udp_init();
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ tcp_init();
+#endif /* LWIP_TCP */
+#if LWIP_IGMP
+ igmp_init();
+#endif /* LWIP_IGMP */
+#if LWIP_DNS
+ dns_init();
+#endif /* LWIP_DNS */
+#if PPP_SUPPORT
+ ppp_init();
+#endif
+
+#if LWIP_TIMERS
+ sys_timeouts_init();
+#endif /* LWIP_TIMERS */
+}
diff --git a/resources/ethernet/LwIP/core/ip.c b/resources/ethernet/LwIP/core/ip.c
new file mode 100644
index 00000000..d611315b
--- /dev/null
+++ b/resources/ethernet/LwIP/core/ip.c
@@ -0,0 +1,167 @@
+/**
+ * @file
+ * Common IPv4 and IPv6 code
+ *
+ * @defgroup ip IP
+ * @ingroup callbackstyle_api
+ *
+ * @defgroup ip4 IPv4
+ * @ingroup ip
+ *
+ * @defgroup ip6 IPv6
+ * @ingroup ip
+ *
+ * @defgroup ipaddr IP address handling
+ * @ingroup infrastructure
+ *
+ * @defgroup ip4addr IPv4 only
+ * @ingroup ipaddr
+ *
+ * @defgroup ip6addr IPv6 only
+ * @ingroup ipaddr
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV4 || LWIP_IPV6
+
+#include "lwip/ip_addr.h"
+#include "lwip/ip.h"
+
+/** Global data for both IPv4 and IPv6 */
+struct ip_globals ip_data;
+
+#if LWIP_IPV4 && LWIP_IPV6
+
+const ip_addr_t ip_addr_any_type = IPADDR_ANY_TYPE_INIT;
+
+/**
+ * @ingroup ipaddr
+ * Convert numeric IP address (both versions) into ASCII representation.
+ * returns ptr to static buffer; not reentrant!
+ *
+ * @param addr ip address in network order to convert
+ * @return pointer to a global static (!) buffer that holds the ASCII
+ * representation of addr
+ */
+char *ipaddr_ntoa(const ip_addr_t *addr)
+{
+ if (addr == NULL) {
+ return NULL;
+ }
+ if (IP_IS_V6(addr)) {
+ return ip6addr_ntoa(ip_2_ip6(addr));
+ } else {
+ return ip4addr_ntoa(ip_2_ip4(addr));
+ }
+}
+
+/**
+ * @ingroup ipaddr
+ * Same as ipaddr_ntoa, but reentrant since a user-supplied buffer is used.
+ *
+ * @param addr ip address in network order to convert
+ * @param buf target buffer where the string is stored
+ * @param buflen length of buf
+ * @return either pointer to buf which now holds the ASCII
+ * representation of addr or NULL if buf was too small
+ */
+char *ipaddr_ntoa_r(const ip_addr_t *addr, char *buf, int buflen)
+{
+ if (addr == NULL) {
+ return NULL;
+ }
+ if (IP_IS_V6(addr)) {
+ return ip6addr_ntoa_r(ip_2_ip6(addr), buf, buflen);
+ } else {
+ return ip4addr_ntoa_r(ip_2_ip4(addr), buf, buflen);
+ }
+}
+
+/**
+ * @ingroup ipaddr
+ * Convert IP address string (both versions) to numeric.
+ * The version is auto-detected from the string.
+ *
+ * @param cp IP address string to convert
+ * @param addr conversion result is stored here
+ * @return 1 on success, 0 on error
+ */
+int
+ipaddr_aton(const char *cp, ip_addr_t *addr)
+{
+ if (cp != NULL) {
+ const char *c;
+ for (c = cp; *c != 0; c++) {
+ if (*c == ':') {
+ /* contains a colon: IPv6 address */
+ if (addr) {
+ IP_SET_TYPE_VAL(*addr, IPADDR_TYPE_V6);
+ }
+ return ip6addr_aton(cp, ip_2_ip6(addr));
+ } else if (*c == '.') {
+ /* contains a dot: IPv4 address */
+ break;
+ }
+ }
+ /* call ip4addr_aton as fallback or if IPv4 was found */
+ if (addr) {
+ IP_SET_TYPE_VAL(*addr, IPADDR_TYPE_V4);
+ }
+ return ip4addr_aton(cp, ip_2_ip4(addr));
+ }
+ return 0;
+}
+
+/**
+ * @ingroup lwip_nosys
+ * If both IP versions are enabled, this function can dispatch packets to the correct one.
+ * Don't call directly, pass to netif_add() and call netif->input().
+ */
+err_t
+ip_input(struct pbuf *p, struct netif *inp)
+{
+ if (p != NULL) {
+ if (IP_HDR_GET_VERSION(p->payload) == 6) {
+ return ip6_input(p, inp);
+ }
+ return ip4_input(p, inp);
+ }
+ return ERR_VAL;
+}
+
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+#endif /* LWIP_IPV4 || LWIP_IPV6 */
diff --git a/resources/ethernet/LwIP/core/ipv4/Makefile b/resources/ethernet/LwIP/core/ipv4/Makefile
new file mode 100644
index 00000000..66f2781d
--- /dev/null
+++ b/resources/ethernet/LwIP/core/ipv4/Makefile
@@ -0,0 +1,10 @@
+SRC_FILES += autoip.c \
+ dhcp.c \
+ etharp.c \
+ icmp.c \
+ igmp.c \
+ ip4_addr.c \
+ ip4_frag.c \
+ ip4.c
+
+include $(KERNEL_ROOT)/compiler.mk
diff --git a/resources/ethernet/LwIP/core/ipv4/autoip.c b/resources/ethernet/LwIP/core/ipv4/autoip.c
new file mode 100644
index 00000000..a4545049
--- /dev/null
+++ b/resources/ethernet/LwIP/core/ipv4/autoip.c
@@ -0,0 +1,527 @@
+/**
+ * @file
+ * AutoIP Automatic LinkLocal IP Configuration
+ *
+ * This is a AutoIP implementation for the lwIP TCP/IP stack. It aims to conform
+ * with RFC 3927.
+ *
+ * @defgroup autoip AUTOIP
+ * @ingroup ip4
+ * AUTOIP related functions
+ * USAGE:
+ *
+ * define @ref LWIP_AUTOIP 1 in your lwipopts.h
+ * Options:
+ * AUTOIP_TMR_INTERVAL msecs,
+ * I recommend a value of 100. The value must divide 1000 with a remainder almost 0.
+ * Possible values are 1000, 500, 333, 250, 200, 166, 142, 125, 111, 100 ....
+ *
+ * Without DHCP:
+ * - Call autoip_start() after netif_add().
+ *
+ * With DHCP:
+ * - define @ref LWIP_DHCP_AUTOIP_COOP 1 in your lwipopts.h.
+ * - Configure your DHCP Client.
+ *
+ * @see netifapi_autoip
+ */
+
+/*
+ *
+ * Copyright (c) 2007 Dominik Spies
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Dominik Spies
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV4 && LWIP_AUTOIP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/mem.h"
+/* #include "lwip/udp.h" */
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/autoip.h"
+#include "lwip/etharp.h"
+#include "lwip/prot/autoip.h"
+
+#include
+
+/** Pseudo random macro based on netif informations.
+ * You could use "rand()" from the C Library if you define LWIP_AUTOIP_RAND in lwipopts.h */
+#ifndef LWIP_AUTOIP_RAND
+#define LWIP_AUTOIP_RAND(netif) ( (((u32_t)((netif->hwaddr[5]) & 0xff) << 24) | \
+ ((u32_t)((netif->hwaddr[3]) & 0xff) << 16) | \
+ ((u32_t)((netif->hwaddr[2]) & 0xff) << 8) | \
+ ((u32_t)((netif->hwaddr[4]) & 0xff))) + \
+ (netif_autoip_data(netif)? netif_autoip_data(netif)->tried_llipaddr : 0))
+#endif /* LWIP_AUTOIP_RAND */
+
+/**
+ * Macro that generates the initial IP address to be tried by AUTOIP.
+ * If you want to override this, define it to something else in lwipopts.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 /* LWIP_AUTOIP_CREATE_SEED_ADDR */
+
+/* static functions */
+static err_t autoip_arp_announce(struct netif *netif);
+static void autoip_start_probing(struct netif *netif);
+
+/**
+ * @ingroup autoip
+ * Set a statically allocated struct autoip to work with.
+ * Using this prevents autoip_start to allocate it using mem_malloc.
+ *
+ * @param netif the netif for which to set the struct autoip
+ * @param autoip (uninitialised) autoip struct allocated by the application
+ */
+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);
+
+ /* clear data structure */
+ memset(autoip, 0, sizeof(struct autoip));
+ /* autoip->state = AUTOIP_STATE_OFF; */
+ netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_AUTOIP, autoip);
+}
+
+/** Restart AutoIP client and check the next address (conflict detected)
+ *
+ * @param netif The netif under AutoIP control
+ */
+static void
+autoip_restart(struct netif *netif)
+{
+ struct autoip *autoip = netif_autoip_data(netif);
+ autoip->tried_llipaddr++;
+ autoip_start(netif);
+}
+
+/**
+ * Handle a IP address conflict after an ARP conflict detection
+ */
+static void
+autoip_handle_arp_conflict(struct netif *netif)
+{
+ struct autoip *autoip = netif_autoip_data(netif);
+
+ /* RFC3927, 2.5 "Conflict Detection and Defense" allows two options where
+ a) means retreat on the first conflict and
+ b) allows to keep an already configured address when having only one
+ conflict in 10 seconds
+ We use option b) since it helps to improve the chance that one of the two
+ conflicting hosts may be able to retain its address. */
+
+ if (autoip->lastconflict > 0) {
+ /* retreat, there was a conflicting ARP in the last DEFEND_INTERVAL seconds */
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_handle_arp_conflict(): we are defending, but in DEFEND_INTERVAL, retreating\n"));
+
+ /* Active TCP sessions are aborted when removing the ip addresss */
+ autoip_restart(netif);
+ } else {
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_handle_arp_conflict(): we are defend, send ARP Announce\n"));
+ autoip_arp_announce(netif);
+ autoip->lastconflict = DEFEND_INTERVAL * AUTOIP_TICKS_PER_SECOND;
+ }
+}
+
+/**
+ * Create an IP-Address out of range 169.254.1.0 to 169.254.254.255
+ *
+ * @param netif network interface on which create the IP-Address
+ * @param ipaddr ip address to initialize
+ */
+static void
+autoip_create_addr(struct netif *netif, ip4_addr_t *ipaddr)
+{
+ struct autoip *autoip = netif_autoip_data(netif);
+
+ /* Here we create an IP-Address out of range 169.254.1.0 to 169.254.254.255
+ * compliant to RFC 3927 Section 2.1
+ * We have 254 * 256 possibilities */
+
+ u32_t addr = lwip_ntohl(LWIP_AUTOIP_CREATE_SEED_ADDR(netif));
+ addr += autoip->tried_llipaddr;
+ addr = AUTOIP_NET | (addr & 0xffff);
+ /* Now, 169.254.0.0 <= addr <= 169.254.255.255 */
+
+ 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)));
+}
+
+/**
+ * Sends an ARP probe from a network interface
+ *
+ * @param netif network interface used to send the probe
+ */
+static err_t
+autoip_arp_probe(struct netif *netif)
+{
+ struct autoip *autoip = netif_autoip_data(netif);
+ /* this works because netif->ip_addr is ANY */
+ return etharp_request(netif, &autoip->llipaddr);
+}
+
+/**
+ * Sends an ARP announce from a network interface
+ *
+ * @param netif network interface used to send the announce
+ */
+static err_t
+autoip_arp_announce(struct netif *netif)
+{
+ return etharp_gratuitous(netif);
+}
+
+/**
+ * Configure interface for use with current LL IP-Address
+ *
+ * @param netif network interface to configure with current LL IP-Address
+ */
+static err_t
+autoip_bind(struct netif *netif)
+{
+ struct autoip *autoip = netif_autoip_data(netif);
+ ip4_addr_t sn_mask, gw_addr;
+
+ 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);
+ /* interface is used by routing now that an address is set */
+
+ return ERR_OK;
+}
+
+/**
+ * @ingroup autoip
+ * Start AutoIP client
+ *
+ * @param netif network interface on which start the AutoIP client
+ */
+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;);
+
+ /* Set IP-Address, Netmask and Gateway to 0 to make sure that
+ * ARP Packets are formed correctly
+ */
+ netif_set_addr(netif, IP4_ADDR_ANY4, IP4_ADDR_ANY4, IP4_ADDR_ANY4);
+
+ 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));
+ if (autoip == NULL) {
+ /* no AutoIP client attached yet? */
+ 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;
+ }
+ /* store this AutoIP client in the netif */
+ netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_AUTOIP, autoip);
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_start(): allocated autoip"));
+ } else {
+ autoip->state = AUTOIP_STATE_OFF;
+ autoip->ttw = 0;
+ autoip->sent_num = 0;
+ ip4_addr_set_zero(&autoip->llipaddr);
+ autoip->lastconflict = 0;
+ }
+
+ autoip_create_addr(netif, &(autoip->llipaddr));
+ autoip_start_probing(netif);
+
+ return result;
+}
+
+static void
+autoip_start_probing(struct netif *netif)
+{
+ struct autoip *autoip = netif_autoip_data(netif);
+
+ autoip->state = AUTOIP_STATE_PROBING;
+ autoip->sent_num = 0;
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_start_probing(): changing state to PROBING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(&autoip->llipaddr), ip4_addr2_16(&autoip->llipaddr),
+ ip4_addr3_16(&autoip->llipaddr), ip4_addr4_16(&autoip->llipaddr)));
+
+ /* time to wait to first probe, this is randomly
+ * chosen out of 0 to PROBE_WAIT seconds.
+ * compliant to RFC 3927 Section 2.2.1
+ */
+ autoip->ttw = (u16_t)(LWIP_AUTOIP_RAND(netif) % (PROBE_WAIT * AUTOIP_TICKS_PER_SECOND));
+
+ /*
+ * if we tried more then MAX_CONFLICTS we must limit our rate for
+ * acquiring and probing address
+ * compliant to RFC 3927 Section 2.2.1
+ */
+ if (autoip->tried_llipaddr > MAX_CONFLICTS) {
+ autoip->ttw = RATE_LIMIT_INTERVAL * AUTOIP_TICKS_PER_SECOND;
+ }
+}
+
+/**
+ * Handle a possible change in the network configuration.
+ *
+ * If there is an AutoIP address configured, take the interface down
+ * and begin probing with the same address.
+ */
+void
+autoip_network_changed(struct netif *netif)
+{
+ struct autoip *autoip = netif_autoip_data(netif);
+
+ if (autoip && (autoip->state != AUTOIP_STATE_OFF)) {
+ autoip_start_probing(netif);
+ }
+}
+
+/**
+ * @ingroup autoip
+ * Stop AutoIP client
+ *
+ * @param netif network interface on which stop the AutoIP client
+ */
+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);
+ }
+ }
+ return ERR_OK;
+}
+
+/**
+ * Has to be called in loop every AUTOIP_TMR_INTERVAL milliseconds
+ */
+void
+autoip_tmr(void)
+{
+ struct netif *netif;
+ /* loop through netif's */
+ NETIF_FOREACH(netif) {
+ struct autoip *autoip = netif_autoip_data(netif);
+ /* only act on AutoIP configured interfaces */
+ if (autoip != NULL) {
+ if (autoip->lastconflict > 0) {
+ autoip->lastconflict--;
+ }
+
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+ ("autoip_tmr() AutoIP-State: %"U16_F", ttw=%"U16_F"\n",
+ (u16_t)(autoip->state), autoip->ttw));
+
+ if (autoip->ttw > 0) {
+ autoip->ttw--;
+ }
+
+ switch (autoip->state) {
+ case AUTOIP_STATE_PROBING:
+ if (autoip->ttw == 0) {
+ if (autoip->sent_num >= PROBE_NUM) {
+ /* Switch to ANNOUNCING: now we can bind to an IP address and use it */
+ autoip->state = AUTOIP_STATE_ANNOUNCING;
+ autoip_bind(netif);
+ /* autoip_bind() calls netif_set_addr(): this triggers a gratuitous ARP
+ which counts as an announcement */
+ autoip->sent_num = 1;
+ autoip->ttw = ANNOUNCE_WAIT * AUTOIP_TICKS_PER_SECOND;
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_tmr(): changing state to ANNOUNCING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(&autoip->llipaddr), ip4_addr2_16(&autoip->llipaddr),
+ ip4_addr3_16(&autoip->llipaddr), ip4_addr4_16(&autoip->llipaddr)));
+ } else {
+ autoip_arp_probe(netif);
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_tmr() PROBING Sent Probe\n"));
+ autoip->sent_num++;
+ if (autoip->sent_num == PROBE_NUM) {
+ /* calculate time to wait to for announce */
+ autoip->ttw = ANNOUNCE_WAIT * AUTOIP_TICKS_PER_SECOND;
+ } else {
+ /* calculate time to wait to next probe */
+ autoip->ttw = (u16_t)((LWIP_AUTOIP_RAND(netif) %
+ ((PROBE_MAX - PROBE_MIN) * AUTOIP_TICKS_PER_SECOND) ) +
+ PROBE_MIN * AUTOIP_TICKS_PER_SECOND);
+ }
+ }
+ }
+ break;
+
+ case AUTOIP_STATE_ANNOUNCING:
+ if (autoip->ttw == 0) {
+ autoip_arp_announce(netif);
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_tmr() ANNOUNCING Sent Announce\n"));
+ autoip->ttw = ANNOUNCE_INTERVAL * AUTOIP_TICKS_PER_SECOND;
+ autoip->sent_num++;
+
+ if (autoip->sent_num >= ANNOUNCE_NUM) {
+ autoip->state = AUTOIP_STATE_BOUND;
+ autoip->sent_num = 0;
+ autoip->ttw = 0;
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_tmr(): changing state to BOUND: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(&autoip->llipaddr), ip4_addr2_16(&autoip->llipaddr),
+ ip4_addr3_16(&autoip->llipaddr), ip4_addr4_16(&autoip->llipaddr)));
+ }
+ }
+ break;
+
+ default:
+ /* nothing to do in other states */
+ break;
+ }
+ }
+ }
+}
+
+/**
+ * Handles every incoming ARP Packet, called by etharp_input().
+ *
+ * @param netif network interface to use for autoip processing
+ * @param hdr Incoming ARP packet
+ */
+void
+autoip_arp_reply(struct netif *netif, struct etharp_hdr *hdr)
+{
+ struct autoip *autoip = netif_autoip_data(netif);
+
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_arp_reply()\n"));
+ if ((autoip != NULL) && (autoip->state != AUTOIP_STATE_OFF)) {
+ /* when ip.src == llipaddr && hw.src != netif->hwaddr
+ *
+ * when probing ip.dst == llipaddr && hw.src != netif->hwaddr
+ * we have a conflict and must solve it
+ */
+ ip4_addr_t sipaddr, dipaddr;
+ struct eth_addr netifaddr;
+ SMEMCPY(netifaddr.addr, netif->hwaddr, ETH_HWADDR_LEN);
+
+ /* Copy struct ip4_addr_wordaligned to aligned ip4_addr, to support compilers without
+ * structure packing (not using structure copy which breaks strict-aliasing rules).
+ */
+ IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T(&sipaddr, &hdr->sipaddr);
+ IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T(&dipaddr, &hdr->dipaddr);
+
+ if (autoip->state == AUTOIP_STATE_PROBING) {
+ /* RFC 3927 Section 2.2.1:
+ * from beginning to after ANNOUNCE_WAIT
+ * seconds we have a conflict if
+ * ip.src == llipaddr OR
+ * ip.dst == llipaddr && hw.src != own hwaddr
+ */
+ if ((ip4_addr_cmp(&sipaddr, &autoip->llipaddr)) ||
+ (ip4_addr_isany_val(sipaddr) &&
+ ip4_addr_cmp(&dipaddr, &autoip->llipaddr) &&
+ !eth_addr_cmp(&netifaddr, &hdr->shwaddr))) {
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
+ ("autoip_arp_reply(): Probe Conflict detected\n"));
+ autoip_restart(netif);
+ }
+ } else {
+ /* RFC 3927 Section 2.5:
+ * in any state we have a conflict if
+ * ip.src == llipaddr && hw.src != own hwaddr
+ */
+ if (ip4_addr_cmp(&sipaddr, &autoip->llipaddr) &&
+ !eth_addr_cmp(&netifaddr, &hdr->shwaddr)) {
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
+ ("autoip_arp_reply(): Conflicting ARP-Packet detected\n"));
+ autoip_handle_arp_conflict(netif);
+ }
+ }
+ }
+}
+
+/** check if AutoIP supplied netif->ip_addr
+ *
+ * @param netif the netif to check
+ * @return 1 if AutoIP supplied netif->ip_addr (state BOUND or ANNOUNCING),
+ * 0 otherwise
+ */
+u8_t
+autoip_supplied_address(const struct netif *netif)
+{
+ if ((netif != NULL) && (netif_autoip_data(netif) != NULL)) {
+ struct autoip *autoip = netif_autoip_data(netif);
+ return (autoip->state == AUTOIP_STATE_BOUND) || (autoip->state == AUTOIP_STATE_ANNOUNCING);
+ }
+ return 0;
+}
+
+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_cmp(addr, &(autoip->llipaddr));
+}
+
+#endif /* LWIP_IPV4 && LWIP_AUTOIP */
diff --git a/resources/ethernet/LwIP/core/ipv4/dhcp.c b/resources/ethernet/LwIP/core/ipv4/dhcp.c
new file mode 100644
index 00000000..89f9c416
--- /dev/null
+++ b/resources/ethernet/LwIP/core/ipv4/dhcp.c
@@ -0,0 +1,1990 @@
+/**
+ * @file
+ * Dynamic Host Configuration Protocol client
+ *
+ * @defgroup dhcp4 DHCPv4
+ * @ingroup ip4
+ * DHCP (IPv4) related functions
+ * This is a DHCP client for the lwIP TCP/IP stack. It aims to conform
+ * with RFC 2131 and RFC 2132.
+ *
+ * @todo:
+ * - Support for interfaces other than Ethernet (SLIP, PPP, ...)
+ *
+ * Options:
+ * @ref DHCP_COARSE_TIMER_SECS (recommended 60 which is a minute)
+ * @ref DHCP_FINE_TIMER_MSECS (recommended 500 which equals TCP coarse timer)
+ *
+ * dhcp_start() starts a DHCP client instance which
+ * configures the interface by obtaining an IP address lease and maintaining it.
+ *
+ * Use dhcp_release() to end the lease and use dhcp_stop()
+ * to remove the DHCP client.
+ *
+ * @see LWIP_HOOK_DHCP_APPEND_OPTIONS
+ * @see LWIP_HOOK_DHCP_PARSE_OPTION
+ *
+ * @see netifapi_dhcp4
+ */
+
+/*
+ * Copyright (c) 2001-2004 Leon Woestenberg
+ * Copyright (c) 2001-2004 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ * The Swedish Institute of Computer Science and Adam Dunkels
+ * are specifically granted permission to redistribute this
+ * source code.
+ *
+ * Author: Leon Woestenberg
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV4 && LWIP_DHCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/stats.h"
+#include "lwip/mem.h"
+#include "lwip/udp.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/def.h"
+#include "lwip/dhcp.h"
+#include "lwip/autoip.h"
+#include "lwip/dns.h"
+#include "lwip/etharp.h"
+#include "lwip/prot/dhcp.h"
+#include "lwip/prot/iana.h"
+
+#include
+
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+#ifndef LWIP_HOOK_DHCP_APPEND_OPTIONS
+#define LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, state, msg, msg_type, options_len_ptr)
+#endif
+#ifndef LWIP_HOOK_DHCP_PARSE_OPTION
+#define LWIP_HOOK_DHCP_PARSE_OPTION(netif, dhcp, state, msg, msg_type, option, len, pbuf, offset) do { LWIP_UNUSED_ARG(msg); } while(0)
+#endif
+
+/** DHCP_CREATE_RAND_XID: if this is set to 1, the xid is created using
+ * LWIP_RAND() (this overrides DHCP_GLOBAL_XID)
+ */
+#ifndef DHCP_CREATE_RAND_XID
+#define DHCP_CREATE_RAND_XID 1
+#endif
+
+/** Default for DHCP_GLOBAL_XID is 0xABCD0000
+ * This can be changed by defining DHCP_GLOBAL_XID and DHCP_GLOBAL_XID_HEADER, e.g.
+ * \#define DHCP_GLOBAL_XID_HEADER "stdlib.h"
+ * \#define DHCP_GLOBAL_XID rand()
+ */
+#ifdef DHCP_GLOBAL_XID_HEADER
+#include DHCP_GLOBAL_XID_HEADER /* include optional starting XID generation prototypes */
+#endif
+
+/** DHCP_OPTION_MAX_MSG_SIZE is set to the MTU
+ * MTU is checked to be big enough in dhcp_start */
+#define DHCP_MAX_MSG_LEN(netif) (netif->mtu)
+#define DHCP_MAX_MSG_LEN_MIN_REQUIRED 576
+/** Minimum length for reply before packet is parsed */
+#define DHCP_MIN_REPLY_LEN 44
+
+#define REBOOT_TRIES 2
+
+#if LWIP_DNS && LWIP_DHCP_MAX_DNS_SERVERS
+#if DNS_MAX_SERVERS > LWIP_DHCP_MAX_DNS_SERVERS
+#define LWIP_DHCP_PROVIDE_DNS_SERVERS LWIP_DHCP_MAX_DNS_SERVERS
+#else
+#define LWIP_DHCP_PROVIDE_DNS_SERVERS DNS_MAX_SERVERS
+#endif
+#else
+#define LWIP_DHCP_PROVIDE_DNS_SERVERS 0
+#endif
+
+/** Option handling: options are parsed in dhcp_parse_reply
+ * and saved in an array where other functions can load them from.
+ * This might be moved into the struct dhcp (not necessarily since
+ * lwIP is single-threaded and the array is only used while in recv
+ * callback). */
+enum dhcp_option_idx {
+ DHCP_OPTION_IDX_OVERLOAD = 0,
+ DHCP_OPTION_IDX_MSG_TYPE,
+ DHCP_OPTION_IDX_SERVER_ID,
+ DHCP_OPTION_IDX_LEASE_TIME,
+ DHCP_OPTION_IDX_T1,
+ DHCP_OPTION_IDX_T2,
+ DHCP_OPTION_IDX_SUBNET_MASK,
+ DHCP_OPTION_IDX_ROUTER,
+#if LWIP_DHCP_PROVIDE_DNS_SERVERS
+ DHCP_OPTION_IDX_DNS_SERVER,
+ DHCP_OPTION_IDX_DNS_SERVER_LAST = DHCP_OPTION_IDX_DNS_SERVER + LWIP_DHCP_PROVIDE_DNS_SERVERS - 1,
+#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS */
+#if LWIP_DHCP_GET_NTP_SRV
+ DHCP_OPTION_IDX_NTP_SERVER,
+ DHCP_OPTION_IDX_NTP_SERVER_LAST = DHCP_OPTION_IDX_NTP_SERVER + LWIP_DHCP_MAX_NTP_SERVERS - 1,
+#endif /* LWIP_DHCP_GET_NTP_SRV */
+ DHCP_OPTION_IDX_MAX
+};
+
+/** Holds the decoded option values, only valid while in dhcp_recv.
+ @todo: move this into struct dhcp? */
+u32_t dhcp_rx_options_val[DHCP_OPTION_IDX_MAX];
+/** Holds a flag which option was received and is contained in dhcp_rx_options_val,
+ only valid while in dhcp_recv.
+ @todo: move this into struct dhcp? */
+u8_t dhcp_rx_options_given[DHCP_OPTION_IDX_MAX];
+
+static u8_t dhcp_discover_request_options[] = {
+ DHCP_OPTION_SUBNET_MASK,
+ DHCP_OPTION_ROUTER,
+ DHCP_OPTION_BROADCAST
+#if LWIP_DHCP_PROVIDE_DNS_SERVERS
+ , DHCP_OPTION_DNS_SERVER
+#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS */
+#if LWIP_DHCP_GET_NTP_SRV
+ , DHCP_OPTION_NTP
+#endif /* LWIP_DHCP_GET_NTP_SRV */
+};
+
+#ifdef DHCP_GLOBAL_XID
+static u32_t xid;
+static u8_t xid_initialised;
+#endif /* DHCP_GLOBAL_XID */
+
+#define dhcp_option_given(dhcp, idx) (dhcp_rx_options_given[idx] != 0)
+#define dhcp_got_option(dhcp, idx) (dhcp_rx_options_given[idx] = 1)
+#define dhcp_clear_option(dhcp, idx) (dhcp_rx_options_given[idx] = 0)
+#define dhcp_clear_all_options(dhcp) (memset(dhcp_rx_options_given, 0, sizeof(dhcp_rx_options_given)))
+#define dhcp_get_option_value(dhcp, idx) (dhcp_rx_options_val[idx])
+#define dhcp_set_option_value(dhcp, idx, val) (dhcp_rx_options_val[idx] = (val))
+
+static struct udp_pcb *dhcp_pcb;
+static u8_t dhcp_pcb_refcount;
+
+/* DHCP client state machine functions */
+static err_t dhcp_discover(struct netif *netif);
+static err_t dhcp_select(struct netif *netif);
+static void dhcp_bind(struct netif *netif);
+#if DHCP_DOES_ARP_CHECK
+static err_t dhcp_decline(struct netif *netif);
+#endif /* DHCP_DOES_ARP_CHECK */
+static err_t dhcp_rebind(struct netif *netif);
+static err_t dhcp_reboot(struct netif *netif);
+static void dhcp_set_state(struct dhcp *dhcp, u8_t new_state);
+
+/* receive, unfold, parse and free incoming messages */
+static void dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port);
+
+/* set the DHCP timers */
+static void dhcp_timeout(struct netif *netif);
+static void dhcp_t1_timeout(struct netif *netif);
+static void dhcp_t2_timeout(struct netif *netif);
+
+/* build outgoing messages */
+/* create a DHCP message, fill in common headers */
+static struct pbuf *dhcp_create_msg(struct netif *netif, struct dhcp *dhcp, u8_t message_type, u16_t *options_out_len);
+/* add a DHCP option (type, then length in bytes) */
+static u16_t dhcp_option(u16_t options_out_len, u8_t *options, u8_t option_type, u8_t option_len);
+/* add option values */
+static u16_t dhcp_option_byte(u16_t options_out_len, u8_t *options, u8_t value);
+static u16_t dhcp_option_short(u16_t options_out_len, u8_t *options, u16_t value);
+static u16_t dhcp_option_long(u16_t options_out_len, u8_t *options, u32_t value);
+#if LWIP_NETIF_HOSTNAME
+static u16_t dhcp_option_hostname(u16_t options_out_len, u8_t *options, struct netif *netif);
+#endif /* LWIP_NETIF_HOSTNAME */
+/* always add the DHCP options trailer to end and pad */
+static void dhcp_option_trailer(u16_t options_out_len, u8_t *options, struct pbuf *p_out);
+
+/** Ensure DHCP PCB is allocated and bound */
+static err_t
+dhcp_inc_pcb_refcount(void)
+{
+ if (dhcp_pcb_refcount == 0) {
+ LWIP_ASSERT("dhcp_inc_pcb_refcount(): memory leak", dhcp_pcb == NULL);
+
+ /* allocate UDP PCB */
+ dhcp_pcb = udp_new();
+
+ if (dhcp_pcb == NULL) {
+ return ERR_MEM;
+ }
+
+ ip_set_option(dhcp_pcb, SOF_BROADCAST);
+
+ /* set up local and remote port for the pcb -> listen on all interfaces on all src/dest IPs */
+ udp_bind(dhcp_pcb, IP4_ADDR_ANY, LWIP_IANA_PORT_DHCP_CLIENT);
+ udp_connect(dhcp_pcb, IP4_ADDR_ANY, LWIP_IANA_PORT_DHCP_SERVER);
+ udp_recv(dhcp_pcb, dhcp_recv, NULL);
+ }
+
+ dhcp_pcb_refcount++;
+
+ return ERR_OK;
+}
+
+/** Free DHCP PCB if the last netif stops using it */
+static void
+dhcp_dec_pcb_refcount(void)
+{
+ LWIP_ASSERT("dhcp_pcb_refcount(): refcount error", (dhcp_pcb_refcount > 0));
+ dhcp_pcb_refcount--;
+
+ if (dhcp_pcb_refcount == 0) {
+ udp_remove(dhcp_pcb);
+ dhcp_pcb = NULL;
+ }
+}
+
+/**
+ * Back-off the DHCP client (because of a received NAK response).
+ *
+ * Back-off the DHCP client because of a received NAK. Receiving a
+ * NAK means the client asked for something non-sensible, for
+ * example when it tries to renew a lease obtained on another network.
+ *
+ * We clear any existing set IP address and restart DHCP negotiation
+ * afresh (as per RFC2131 3.2.3).
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_handle_nak(struct netif *netif)
+{
+ struct dhcp *dhcp = netif_dhcp_data(netif);
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_handle_nak(netif=%p) %c%c%"U16_F"\n",
+ (void *)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+ /* Change to a defined state - set this before assigning the address
+ to ensure the callback can use dhcp_supplied_address() */
+ dhcp_set_state(dhcp, DHCP_STATE_BACKING_OFF);
+ /* remove IP address from interface (must no longer be used, as per RFC2131) */
+ netif_set_addr(netif, IP4_ADDR_ANY4, IP4_ADDR_ANY4, IP4_ADDR_ANY4);
+ /* We can immediately restart discovery */
+ dhcp_discover(netif);
+}
+
+#if DHCP_DOES_ARP_CHECK
+/**
+ * Checks if the offered IP address is already in use.
+ *
+ * It does so by sending an ARP request for the offered address and
+ * entering CHECKING state. If no ARP reply is received within a small
+ * interval, the address is assumed to be free for use by us.
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_check(struct netif *netif)
+{
+ struct dhcp *dhcp = netif_dhcp_data(netif);
+ err_t result;
+ u16_t msecs;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_check(netif=%p) %c%c\n", (void *)netif, (s16_t)netif->name[0],
+ (s16_t)netif->name[1]));
+ dhcp_set_state(dhcp, DHCP_STATE_CHECKING);
+ /* create an ARP query for the offered IP address, expecting that no host
+ responds, as the IP address should not be in use. */
+ result = etharp_query(netif, &dhcp->offered_ip_addr, NULL);
+ if (result != ERR_OK) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("dhcp_check: could not perform ARP query\n"));
+ }
+ if (dhcp->tries < 255) {
+ dhcp->tries++;
+ }
+ msecs = 500;
+ dhcp->request_timeout = (u16_t)((msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_check(): set request timeout %"U16_F" msecs\n", msecs));
+}
+#endif /* DHCP_DOES_ARP_CHECK */
+
+/**
+ * Remember the configuration offered by a DHCP server.
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_handle_offer(struct netif *netif, struct dhcp_msg *msg_in)
+{
+ struct dhcp *dhcp = netif_dhcp_data(netif);
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_handle_offer(netif=%p) %c%c%"U16_F"\n",
+ (void *)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+ /* obtain the server address */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_SERVER_ID)) {
+ dhcp->request_timeout = 0; /* stop timer */
+
+ ip_addr_set_ip4_u32(&dhcp->server_ip_addr, lwip_htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_SERVER_ID)));
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_handle_offer(): server 0x%08"X32_F"\n",
+ ip4_addr_get_u32(ip_2_ip4(&dhcp->server_ip_addr))));
+ /* remember offered address */
+ ip4_addr_copy(dhcp->offered_ip_addr, msg_in->yiaddr);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_handle_offer(): offer for 0x%08"X32_F"\n",
+ ip4_addr_get_u32(&dhcp->offered_ip_addr)));
+
+ dhcp_select(netif);
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("dhcp_handle_offer(netif=%p) did not get server ID!\n", (void *)netif));
+ }
+}
+
+/**
+ * Select a DHCP server offer out of all offers.
+ *
+ * Simply select the first offer received.
+ *
+ * @param netif the netif under DHCP control
+ * @return lwIP specific error (see error.h)
+ */
+static err_t
+dhcp_select(struct netif *netif)
+{
+ struct dhcp *dhcp;
+ err_t result;
+ u16_t msecs;
+ u8_t i;
+ struct pbuf *p_out;
+ u16_t options_out_len;
+
+ LWIP_ERROR("dhcp_select: netif != NULL", (netif != NULL), return ERR_ARG;);
+ dhcp = netif_dhcp_data(netif);
+ LWIP_ERROR("dhcp_select: dhcp != NULL", (dhcp != NULL), return ERR_VAL;);
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_select(netif=%p) %c%c%"U16_F"\n", (void *)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+ dhcp_set_state(dhcp, DHCP_STATE_REQUESTING);
+
+ /* create and initialize the DHCP message header */
+ p_out = dhcp_create_msg(netif, dhcp, DHCP_REQUEST, &options_out_len);
+ if (p_out != NULL) {
+ struct dhcp_msg *msg_out = (struct dhcp_msg *)p_out->payload;
+ options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ options_out_len = dhcp_option_short(options_out_len, msg_out->options, DHCP_MAX_MSG_LEN(netif));
+
+ /* MUST request the offered IP address */
+ options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_REQUESTED_IP, 4);
+ options_out_len = dhcp_option_long(options_out_len, msg_out->options, lwip_ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr)));
+
+ options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_SERVER_ID, 4);
+ options_out_len = dhcp_option_long(options_out_len, msg_out->options, lwip_ntohl(ip4_addr_get_u32(ip_2_ip4(&dhcp->server_ip_addr))));
+
+ options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_PARAMETER_REQUEST_LIST, LWIP_ARRAYSIZE(dhcp_discover_request_options));
+ for (i = 0; i < LWIP_ARRAYSIZE(dhcp_discover_request_options); i++) {
+ options_out_len = dhcp_option_byte(options_out_len, msg_out->options, dhcp_discover_request_options[i]);
+ }
+
+#if LWIP_NETIF_HOSTNAME
+ options_out_len = dhcp_option_hostname(options_out_len, msg_out->options, netif);
+#endif /* LWIP_NETIF_HOSTNAME */
+
+ LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, DHCP_STATE_REQUESTING, msg_out, DHCP_REQUEST, &options_out_len);
+ dhcp_option_trailer(options_out_len, msg_out->options, p_out);
+
+ /* send broadcast to any DHCP server */
+ result = udp_sendto_if_src(dhcp_pcb, p_out, IP_ADDR_BROADCAST, LWIP_IANA_PORT_DHCP_SERVER, netif, IP4_ADDR_ANY);
+ pbuf_free(p_out);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_select: REQUESTING\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("dhcp_select: could not allocate DHCP request\n"));
+ result = ERR_MEM;
+ }
+ if (dhcp->tries < 255) {
+ dhcp->tries++;
+ }
+ msecs = (u16_t)((dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 1000);
+ dhcp->request_timeout = (u16_t)((msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_select(): set request timeout %"U16_F" msecs\n", msecs));
+ return result;
+}
+
+/**
+ * The DHCP timer that checks for lease renewal/rebind timeouts.
+ * Must be called once a minute (see @ref DHCP_COARSE_TIMER_SECS).
+ */
+void
+dhcp_coarse_tmr(void)
+{
+ struct netif *netif;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_coarse_tmr()\n"));
+ /* iterate through all network interfaces */
+ NETIF_FOREACH(netif) {
+ /* only act on DHCP configured interfaces */
+ struct dhcp *dhcp = netif_dhcp_data(netif);
+ if ((dhcp != NULL) && (dhcp->state != DHCP_STATE_OFF)) {
+ /* compare lease time to expire timeout */
+ if (dhcp->t0_timeout && (++dhcp->lease_used == dhcp->t0_timeout)) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t0 timeout\n"));
+ /* this clients' lease time has expired */
+ dhcp_release_and_stop(netif);
+ dhcp_start(netif);
+ /* timer is active (non zero), and triggers (zeroes) now? */
+ } else if (dhcp->t2_rebind_time && (dhcp->t2_rebind_time-- == 1)) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t2 timeout\n"));
+ /* this clients' rebind timeout triggered */
+ dhcp_t2_timeout(netif);
+ /* timer is active (non zero), and triggers (zeroes) now */
+ } else if (dhcp->t1_renew_time && (dhcp->t1_renew_time-- == 1)) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t1 timeout\n"));
+ /* this clients' renewal timeout triggered */
+ dhcp_t1_timeout(netif);
+ }
+ }
+ }
+}
+
+/**
+ * DHCP transaction timeout handling (this function must be called every 500ms,
+ * see @ref DHCP_FINE_TIMER_MSECS).
+ *
+ * A DHCP server is expected to respond within a short period of time.
+ * This timer checks whether an outstanding DHCP request is timed out.
+ */
+void
+dhcp_fine_tmr(void)
+{
+ struct netif *netif;
+ /* loop through netif's */
+ NETIF_FOREACH(netif) {
+ struct dhcp *dhcp = netif_dhcp_data(netif);
+ /* only act on DHCP configured interfaces */
+ if (dhcp != NULL) {
+ /* timer is active (non zero), and is about to trigger now */
+ if (dhcp->request_timeout > 1) {
+ dhcp->request_timeout--;
+ } else if (dhcp->request_timeout == 1) {
+ dhcp->request_timeout--;
+ /* { dhcp->request_timeout == 0 } */
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_fine_tmr(): request timeout\n"));
+ /* this client's request timeout triggered */
+ dhcp_timeout(netif);
+ }
+ }
+ }
+}
+
+/**
+ * A DHCP negotiation transaction, or ARP request, has timed out.
+ *
+ * The timer that was started with the DHCP or ARP request has
+ * timed out, indicating no response was received in time.
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_timeout(struct netif *netif)
+{
+ struct dhcp *dhcp = netif_dhcp_data(netif);
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_timeout()\n"));
+ /* back-off period has passed, or server selection timed out */
+ if ((dhcp->state == DHCP_STATE_BACKING_OFF) || (dhcp->state == DHCP_STATE_SELECTING)) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_timeout(): restarting discovery\n"));
+ dhcp_discover(netif);
+ /* receiving the requested lease timed out */
+ } else if (dhcp->state == DHCP_STATE_REQUESTING) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REQUESTING, DHCP request timed out\n"));
+ if (dhcp->tries <= 5) {
+ dhcp_select(netif);
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REQUESTING, releasing, restarting\n"));
+ dhcp_release_and_stop(netif);
+ dhcp_start(netif);
+ }
+#if DHCP_DOES_ARP_CHECK
+ /* received no ARP reply for the offered address (which is good) */
+ } else if (dhcp->state == DHCP_STATE_CHECKING) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): CHECKING, ARP request timed out\n"));
+ if (dhcp->tries <= 1) {
+ dhcp_check(netif);
+ /* no ARP replies on the offered address,
+ looks like the IP address is indeed free */
+ } else {
+ /* bind the interface to the offered address */
+ dhcp_bind(netif);
+ }
+#endif /* DHCP_DOES_ARP_CHECK */
+ } else if (dhcp->state == DHCP_STATE_REBOOTING) {
+ if (dhcp->tries < REBOOT_TRIES) {
+ dhcp_reboot(netif);
+ } else {
+ dhcp_discover(netif);
+ }
+ }
+}
+
+/**
+ * The renewal period has timed out.
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_t1_timeout(struct netif *netif)
+{
+ struct dhcp *dhcp = netif_dhcp_data(netif);
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_t1_timeout()\n"));
+ if ((dhcp->state == DHCP_STATE_REQUESTING) || (dhcp->state == DHCP_STATE_BOUND) ||
+ (dhcp->state == DHCP_STATE_RENEWING)) {
+ /* just retry to renew - note that the rebind timer (t2) will
+ * eventually time-out if renew tries fail. */
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("dhcp_t1_timeout(): must renew\n"));
+ /* This slightly different to RFC2131: DHCPREQUEST will be sent from state
+ DHCP_STATE_RENEWING, not DHCP_STATE_BOUND */
+ dhcp_renew(netif);
+ /* Calculate next timeout */
+ if (((dhcp->t2_timeout - dhcp->lease_used) / 2) >= ((60 + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS)) {
+ dhcp->t1_renew_time = (u16_t)((dhcp->t2_timeout - dhcp->lease_used) / 2);
+ }
+ }
+}
+
+/**
+ * The rebind period has timed out.
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_t2_timeout(struct netif *netif)
+{
+ struct dhcp *dhcp = netif_dhcp_data(netif);
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_t2_timeout()\n"));
+ if ((dhcp->state == DHCP_STATE_REQUESTING) || (dhcp->state == DHCP_STATE_BOUND) ||
+ (dhcp->state == DHCP_STATE_RENEWING) || (dhcp->state == DHCP_STATE_REBINDING)) {
+ /* just retry to rebind */
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("dhcp_t2_timeout(): must rebind\n"));
+ /* This slightly different to RFC2131: DHCPREQUEST will be sent from state
+ DHCP_STATE_REBINDING, not DHCP_STATE_BOUND */
+ dhcp_rebind(netif);
+ /* Calculate next timeout */
+ if (((dhcp->t0_timeout - dhcp->lease_used) / 2) >= ((60 + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS)) {
+ dhcp->t2_rebind_time = (u16_t)((dhcp->t0_timeout - dhcp->lease_used) / 2);
+ }
+ }
+}
+
+/**
+ * Handle a DHCP ACK packet
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_handle_ack(struct netif *netif, struct dhcp_msg *msg_in)
+{
+ struct dhcp *dhcp = netif_dhcp_data(netif);
+
+#if LWIP_DHCP_PROVIDE_DNS_SERVERS || LWIP_DHCP_GET_NTP_SRV
+ u8_t n;
+#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS || LWIP_DHCP_GET_NTP_SRV */
+#if LWIP_DHCP_GET_NTP_SRV
+ ip4_addr_t ntp_server_addrs[LWIP_DHCP_MAX_NTP_SERVERS];
+#endif
+
+ /* clear options we might not get from the ACK */
+ ip4_addr_set_zero(&dhcp->offered_sn_mask);
+ ip4_addr_set_zero(&dhcp->offered_gw_addr);
+#if LWIP_DHCP_BOOTP_FILE
+ ip4_addr_set_zero(&dhcp->offered_si_addr);
+#endif /* LWIP_DHCP_BOOTP_FILE */
+
+ /* lease time given? */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_LEASE_TIME)) {
+ /* remember offered lease time */
+ dhcp->offered_t0_lease = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_LEASE_TIME);
+ }
+ /* renewal period given? */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_T1)) {
+ /* remember given renewal period */
+ dhcp->offered_t1_renew = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_T1);
+ } else {
+ /* calculate safe periods for renewal */
+ dhcp->offered_t1_renew = dhcp->offered_t0_lease / 2;
+ }
+
+ /* renewal period given? */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_T2)) {
+ /* remember given rebind period */
+ dhcp->offered_t2_rebind = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_T2);
+ } else {
+ /* calculate safe periods for rebinding (offered_t0_lease * 0.875 -> 87.5%)*/
+ dhcp->offered_t2_rebind = (dhcp->offered_t0_lease * 7U) / 8U;
+ }
+
+ /* (y)our internet address */
+ ip4_addr_copy(dhcp->offered_ip_addr, msg_in->yiaddr);
+
+#if LWIP_DHCP_BOOTP_FILE
+ /* copy boot server address,
+ boot file name copied in dhcp_parse_reply if not overloaded */
+ ip4_addr_copy(dhcp->offered_si_addr, msg_in->siaddr);
+#endif /* LWIP_DHCP_BOOTP_FILE */
+
+ /* subnet mask given? */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_SUBNET_MASK)) {
+ /* remember given subnet mask */
+ ip4_addr_set_u32(&dhcp->offered_sn_mask, lwip_htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_SUBNET_MASK)));
+ dhcp->subnet_mask_given = 1;
+ } else {
+ dhcp->subnet_mask_given = 0;
+ }
+
+ /* gateway router */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_ROUTER)) {
+ ip4_addr_set_u32(&dhcp->offered_gw_addr, lwip_htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_ROUTER)));
+ }
+
+#if LWIP_DHCP_GET_NTP_SRV
+ /* NTP servers */
+ for (n = 0; (n < LWIP_DHCP_MAX_NTP_SERVERS) && dhcp_option_given(dhcp, DHCP_OPTION_IDX_NTP_SERVER + n); n++) {
+ ip4_addr_set_u32(&ntp_server_addrs[n], lwip_htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_NTP_SERVER + n)));
+ }
+ dhcp_set_ntp_servers(n, ntp_server_addrs);
+#endif /* LWIP_DHCP_GET_NTP_SRV */
+
+#if LWIP_DHCP_PROVIDE_DNS_SERVERS
+ /* DNS servers */
+ for (n = 0; (n < LWIP_DHCP_PROVIDE_DNS_SERVERS) && dhcp_option_given(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n); n++) {
+ ip_addr_t dns_addr;
+ ip_addr_set_ip4_u32_val(dns_addr, lwip_htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n)));
+ dns_setserver(n, &dns_addr);
+ }
+#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS */
+}
+
+/**
+ * @ingroup dhcp4
+ * Set a statically allocated struct dhcp to work with.
+ * Using this prevents dhcp_start to allocate it using mem_malloc.
+ *
+ * @param netif the netif for which to set the struct dhcp
+ * @param dhcp (uninitialised) dhcp struct allocated by the application
+ */
+void
+dhcp_set_struct(struct netif *netif, struct dhcp *dhcp)
+{
+ LWIP_ASSERT_CORE_LOCKED();
+ LWIP_ASSERT("netif != NULL", netif != NULL);
+ LWIP_ASSERT("dhcp != NULL", dhcp != NULL);
+ LWIP_ASSERT("netif already has a struct dhcp set", netif_dhcp_data(netif) == NULL);
+
+ /* clear data structure */
+ memset(dhcp, 0, sizeof(struct dhcp));
+ /* dhcp_set_state(&dhcp, DHCP_STATE_OFF); */
+ netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP, dhcp);
+}
+
+/**
+ * @ingroup dhcp4
+ * Removes a struct dhcp from a netif.
+ *
+ * ATTENTION: Only use this when not using dhcp_set_struct() to allocate the
+ * struct dhcp since the memory is passed back to the heap.
+ *
+ * @param netif the netif from which to remove the struct dhcp
+ */
+void dhcp_cleanup(struct netif *netif)
+{
+ LWIP_ASSERT_CORE_LOCKED();
+ LWIP_ASSERT("netif != NULL", netif != NULL);
+
+ if (netif_dhcp_data(netif) != NULL) {
+ mem_free(netif_dhcp_data(netif));
+ netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP, NULL);
+ }
+}
+
+/**
+ * @ingroup dhcp4
+ * Start DHCP negotiation for a network interface.
+ *
+ * If no DHCP client instance was attached to this interface,
+ * a new client is created first. If a DHCP client instance
+ * was already present, it restarts negotiation.
+ *
+ * @param netif The lwIP network interface
+ * @return lwIP error code
+ * - ERR_OK - No error
+ * - ERR_MEM - Out of memory
+ */
+err_t
+dhcp_start(struct netif *netif)
+{
+ struct dhcp *dhcp;
+ err_t result;
+
+ LWIP_ASSERT_CORE_LOCKED();
+ LWIP_ERROR("netif != NULL", (netif != NULL), return ERR_ARG;);
+ LWIP_ERROR("netif is not up, old style port?", netif_is_up(netif), return ERR_ARG;);
+ dhcp = netif_dhcp_data(netif);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_start(netif=%p) %c%c%"U16_F"\n", (void *)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+
+ /* check MTU of the netif */
+ if (netif->mtu < DHCP_MAX_MSG_LEN_MIN_REQUIRED) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): Cannot use this netif with DHCP: MTU is too small\n"));
+ return ERR_MEM;
+ }
+
+ /* no DHCP client attached yet? */
+ if (dhcp == NULL) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): mallocing new DHCP client\n"));
+ dhcp = (struct dhcp *)mem_malloc(sizeof(struct dhcp));
+ if (dhcp == NULL) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): could not allocate dhcp\n"));
+ return ERR_MEM;
+ }
+
+ /* store this dhcp client in the netif */
+ netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP, dhcp);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): allocated dhcp"));
+ /* already has DHCP client attached */
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_start(): restarting DHCP configuration\n"));
+
+ if (dhcp->pcb_allocated != 0) {
+ dhcp_dec_pcb_refcount(); /* free DHCP PCB if not needed any more */
+ }
+ /* dhcp is cleared below, no need to reset flag*/
+ }
+
+ /* clear data structure */
+ memset(dhcp, 0, sizeof(struct dhcp));
+ /* dhcp_set_state(&dhcp, DHCP_STATE_OFF); */
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): starting DHCP configuration\n"));
+
+ if (dhcp_inc_pcb_refcount() != ERR_OK) { /* ensure DHCP PCB is allocated */
+ return ERR_MEM;
+ }
+ dhcp->pcb_allocated = 1;
+
+ if (!netif_is_link_up(netif)) {
+ /* set state INIT and wait for dhcp_network_changed() to call dhcp_discover() */
+ dhcp_set_state(dhcp, DHCP_STATE_INIT);
+ return ERR_OK;
+ }
+
+ /* (re)start the DHCP negotiation */
+ result = dhcp_discover(netif);
+ if (result != ERR_OK) {
+ /* free resources allocated above */
+ dhcp_release_and_stop(netif);
+ return ERR_MEM;
+ }
+ return result;
+}
+
+/**
+ * @ingroup dhcp4
+ * Inform a DHCP server of our manual configuration.
+ *
+ * This informs DHCP servers of our fixed IP address configuration
+ * by sending an INFORM message. It does not involve DHCP address
+ * configuration, it is just here to be nice to the network.
+ *
+ * @param netif The lwIP network interface
+ */
+void
+dhcp_inform(struct netif *netif)
+{
+ struct dhcp dhcp;
+ struct pbuf *p_out;
+ u16_t options_out_len;
+
+ LWIP_ASSERT_CORE_LOCKED();
+ LWIP_ERROR("netif != NULL", (netif != NULL), return;);
+
+ if (dhcp_inc_pcb_refcount() != ERR_OK) { /* ensure DHCP PCB is allocated */
+ return;
+ }
+
+ memset(&dhcp, 0, sizeof(struct dhcp));
+ dhcp_set_state(&dhcp, DHCP_STATE_INFORMING);
+
+ /* create and initialize the DHCP message header */
+ p_out = dhcp_create_msg(netif, &dhcp, DHCP_INFORM, &options_out_len);
+ if (p_out != NULL) {
+ struct dhcp_msg *msg_out = (struct dhcp_msg *)p_out->payload;
+ options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ options_out_len = dhcp_option_short(options_out_len, msg_out->options, DHCP_MAX_MSG_LEN(netif));
+
+ LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, &dhcp, DHCP_STATE_INFORMING, msg_out, DHCP_INFORM, &options_out_len);
+ dhcp_option_trailer(options_out_len, msg_out->options, p_out);
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_inform: INFORMING\n"));
+
+ udp_sendto_if(dhcp_pcb, p_out, IP_ADDR_BROADCAST, LWIP_IANA_PORT_DHCP_SERVER, netif);
+
+ pbuf_free(p_out);
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_inform: could not allocate DHCP request\n"));
+ }
+
+ dhcp_dec_pcb_refcount(); /* delete DHCP PCB if not needed any more */
+}
+
+/** Handle a possible change in the network configuration.
+ *
+ * This enters the REBOOTING state to verify that the currently bound
+ * address is still valid.
+ */
+void
+dhcp_network_changed(struct netif *netif)
+{
+ struct dhcp *dhcp = netif_dhcp_data(netif);
+
+ if (!dhcp) {
+ return;
+ }
+ switch (dhcp->state) {
+ case DHCP_STATE_REBINDING:
+ case DHCP_STATE_RENEWING:
+ case DHCP_STATE_BOUND:
+ case DHCP_STATE_REBOOTING:
+ dhcp->tries = 0;
+ dhcp_reboot(netif);
+ break;
+ case DHCP_STATE_OFF:
+ /* stay off */
+ break;
+ default:
+ LWIP_ASSERT("invalid dhcp->state", dhcp->state <= DHCP_STATE_BACKING_OFF);
+ /* INIT/REQUESTING/CHECKING/BACKING_OFF restart with new 'rid' because the
+ state changes, SELECTING: continue with current 'rid' as we stay in the
+ same state */
+#if LWIP_DHCP_AUTOIP_COOP
+ if (dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) {
+ autoip_stop(netif);
+ dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF;
+ }
+#endif /* LWIP_DHCP_AUTOIP_COOP */
+ /* ensure we start with short timeouts, even if already discovering */
+ dhcp->tries = 0;
+ dhcp_discover(netif);
+ break;
+ }
+}
+
+#if DHCP_DOES_ARP_CHECK
+/**
+ * Match an ARP reply with the offered IP address:
+ * check whether the offered IP address is not in use using ARP
+ *
+ * @param netif the network interface on which the reply was received
+ * @param addr The IP address we received a reply from
+ */
+void
+dhcp_arp_reply(struct netif *netif, const ip4_addr_t *addr)
+{
+ struct dhcp *dhcp;
+
+ LWIP_ERROR("netif != NULL", (netif != NULL), return;);
+ dhcp = netif_dhcp_data(netif);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_arp_reply()\n"));
+ /* is a DHCP client doing an ARP check? */
+ if ((dhcp != NULL) && (dhcp->state == DHCP_STATE_CHECKING)) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_arp_reply(): CHECKING, arp reply for 0x%08"X32_F"\n",
+ ip4_addr_get_u32(addr)));
+ /* did a host respond with the address we
+ were offered by the DHCP server? */
+ if (ip4_addr_cmp(addr, &dhcp->offered_ip_addr)) {
+ /* we will not accept the offered address */
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
+ ("dhcp_arp_reply(): arp reply matched with offered address, declining\n"));
+ dhcp_decline(netif);
+ }
+ }
+}
+
+/**
+ * Decline an offered lease.
+ *
+ * Tell the DHCP server we do not accept the offered address.
+ * One reason to decline the lease is when we find out the address
+ * is already in use by another host (through ARP).
+ *
+ * @param netif the netif under DHCP control
+ */
+static err_t
+dhcp_decline(struct netif *netif)
+{
+ struct dhcp *dhcp = netif_dhcp_data(netif);
+ err_t result;
+ u16_t msecs;
+ struct pbuf *p_out;
+ u16_t options_out_len;
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_decline()\n"));
+ dhcp_set_state(dhcp, DHCP_STATE_BACKING_OFF);
+ /* create and initialize the DHCP message header */
+ p_out = dhcp_create_msg(netif, dhcp, DHCP_DECLINE, &options_out_len);
+ if (p_out != NULL) {
+ struct dhcp_msg *msg_out = (struct dhcp_msg *)p_out->payload;
+ options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_REQUESTED_IP, 4);
+ options_out_len = dhcp_option_long(options_out_len, msg_out->options, lwip_ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr)));
+
+ LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, DHCP_STATE_BACKING_OFF, msg_out, DHCP_DECLINE, &options_out_len);
+ dhcp_option_trailer(options_out_len, msg_out->options, p_out);
+
+ /* per section 4.4.4, broadcast DECLINE messages */
+ result = udp_sendto_if_src(dhcp_pcb, p_out, IP_ADDR_BROADCAST, LWIP_IANA_PORT_DHCP_SERVER, netif, IP4_ADDR_ANY);
+ pbuf_free(p_out);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_decline: BACKING OFF\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("dhcp_decline: could not allocate DHCP request\n"));
+ result = ERR_MEM;
+ }
+ if (dhcp->tries < 255) {
+ dhcp->tries++;
+ }
+ msecs = 10 * 1000;
+ dhcp->request_timeout = (u16_t)((msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_decline(): set request timeout %"U16_F" msecs\n", msecs));
+ return result;
+}
+#endif /* DHCP_DOES_ARP_CHECK */
+
+
+/**
+ * Start the DHCP process, discover a DHCP server.
+ *
+ * @param netif the netif under DHCP control
+ */
+static err_t
+dhcp_discover(struct netif *netif)
+{
+ struct dhcp *dhcp = netif_dhcp_data(netif);
+ err_t result = ERR_OK;
+ u16_t msecs;
+ u8_t i;
+ struct pbuf *p_out;
+ u16_t options_out_len;
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover()\n"));
+
+ ip4_addr_set_any(&dhcp->offered_ip_addr);
+ dhcp_set_state(dhcp, DHCP_STATE_SELECTING);
+ /* create and initialize the DHCP message header */
+ p_out = dhcp_create_msg(netif, dhcp, DHCP_DISCOVER, &options_out_len);
+ if (p_out != NULL) {
+ struct dhcp_msg *msg_out = (struct dhcp_msg *)p_out->payload;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: making request\n"));
+
+ options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ options_out_len = dhcp_option_short(options_out_len, msg_out->options, DHCP_MAX_MSG_LEN(netif));
+
+ options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_PARAMETER_REQUEST_LIST, LWIP_ARRAYSIZE(dhcp_discover_request_options));
+ for (i = 0; i < LWIP_ARRAYSIZE(dhcp_discover_request_options); i++) {
+ options_out_len = dhcp_option_byte(options_out_len, msg_out->options, dhcp_discover_request_options[i]);
+ }
+ LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, DHCP_STATE_SELECTING, msg_out, DHCP_DISCOVER, &options_out_len);
+ dhcp_option_trailer(options_out_len, msg_out->options, p_out);
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: sendto(DISCOVER, IP_ADDR_BROADCAST, LWIP_IANA_PORT_DHCP_SERVER)\n"));
+ udp_sendto_if_src(dhcp_pcb, p_out, IP_ADDR_BROADCAST, LWIP_IANA_PORT_DHCP_SERVER, netif, IP4_ADDR_ANY);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: deleting()ing\n"));
+ pbuf_free(p_out);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_discover: SELECTING\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_discover: could not allocate DHCP request\n"));
+ }
+ if (dhcp->tries < 255) {
+ dhcp->tries++;
+ }
+#if LWIP_DHCP_AUTOIP_COOP
+ if (dhcp->tries >= LWIP_DHCP_AUTOIP_COOP_TRIES && dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_OFF) {
+ dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_ON;
+ autoip_start(netif);
+ }
+#endif /* LWIP_DHCP_AUTOIP_COOP */
+ msecs = (u16_t)((dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 1000);
+ dhcp->request_timeout = (u16_t)((msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_discover(): set request timeout %"U16_F" msecs\n", msecs));
+ return result;
+}
+
+
+/**
+ * Bind the interface to the offered IP address.
+ *
+ * @param netif network interface to bind to the offered address
+ */
+static void
+dhcp_bind(struct netif *netif)
+{
+ u32_t timeout;
+ struct dhcp *dhcp;
+ ip4_addr_t sn_mask, gw_addr;
+ LWIP_ERROR("dhcp_bind: netif != NULL", (netif != NULL), return;);
+ dhcp = netif_dhcp_data(netif);
+ LWIP_ERROR("dhcp_bind: dhcp != NULL", (dhcp != NULL), return;);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(netif=%p) %c%c%"U16_F"\n", (void *)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+
+ /* reset time used of lease */
+ dhcp->lease_used = 0;
+
+ if (dhcp->offered_t0_lease != 0xffffffffUL) {
+ /* set renewal period timer */
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t0 renewal timer %"U32_F" secs\n", dhcp->offered_t0_lease));
+ timeout = (dhcp->offered_t0_lease + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS;
+ if (timeout > 0xffff) {
+ timeout = 0xffff;
+ }
+ dhcp->t0_timeout = (u16_t)timeout;
+ if (dhcp->t0_timeout == 0) {
+ dhcp->t0_timeout = 1;
+ }
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t0_lease * 1000));
+ }
+
+ /* temporary DHCP lease? */
+ if (dhcp->offered_t1_renew != 0xffffffffUL) {
+ /* set renewal period timer */
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t1 renewal timer %"U32_F" secs\n", dhcp->offered_t1_renew));
+ timeout = (dhcp->offered_t1_renew + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS;
+ if (timeout > 0xffff) {
+ timeout = 0xffff;
+ }
+ dhcp->t1_timeout = (u16_t)timeout;
+ if (dhcp->t1_timeout == 0) {
+ dhcp->t1_timeout = 1;
+ }
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t1_renew * 1000));
+ dhcp->t1_renew_time = dhcp->t1_timeout;
+ }
+ /* set renewal period timer */
+ if (dhcp->offered_t2_rebind != 0xffffffffUL) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t2 rebind timer %"U32_F" secs\n", dhcp->offered_t2_rebind));
+ timeout = (dhcp->offered_t2_rebind + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS;
+ if (timeout > 0xffff) {
+ timeout = 0xffff;
+ }
+ dhcp->t2_timeout = (u16_t)timeout;
+ if (dhcp->t2_timeout == 0) {
+ dhcp->t2_timeout = 1;
+ }
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t2_rebind * 1000));
+ dhcp->t2_rebind_time = dhcp->t2_timeout;
+ }
+
+ /* If we have sub 1 minute lease, t2 and t1 will kick in at the same time. */
+ if ((dhcp->t1_timeout >= dhcp->t2_timeout) && (dhcp->t2_timeout > 0)) {
+ dhcp->t1_timeout = 0;
+ }
+
+ if (dhcp->subnet_mask_given) {
+ /* copy offered network mask */
+ ip4_addr_copy(sn_mask, dhcp->offered_sn_mask);
+ } else {
+ /* subnet mask not given, choose a safe subnet mask given the network class */
+ u8_t first_octet = ip4_addr1(&dhcp->offered_ip_addr);
+ if (first_octet <= 127) {
+ ip4_addr_set_u32(&sn_mask, PP_HTONL(0xff000000UL));
+ } else if (first_octet >= 192) {
+ ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffffff00UL));
+ } else {
+ ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffff0000UL));
+ }
+ }
+
+ ip4_addr_copy(gw_addr, dhcp->offered_gw_addr);
+ /* gateway address not given? */
+ if (ip4_addr_isany_val(gw_addr)) {
+ /* copy network address */
+ ip4_addr_get_network(&gw_addr, &dhcp->offered_ip_addr, &sn_mask);
+ /* use first host address on network as gateway */
+ ip4_addr_set_u32(&gw_addr, ip4_addr_get_u32(&gw_addr) | PP_HTONL(0x00000001UL));
+ }
+
+#if LWIP_DHCP_AUTOIP_COOP
+ if (dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) {
+ autoip_stop(netif);
+ dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF;
+ }
+#endif /* LWIP_DHCP_AUTOIP_COOP */
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): IP: 0x%08"X32_F" SN: 0x%08"X32_F" GW: 0x%08"X32_F"\n",
+ ip4_addr_get_u32(&dhcp->offered_ip_addr), ip4_addr_get_u32(&sn_mask), ip4_addr_get_u32(&gw_addr)));
+ /* netif is now bound to DHCP leased address - set this before assigning the address
+ to ensure the callback can use dhcp_supplied_address() */
+ dhcp_set_state(dhcp, DHCP_STATE_BOUND);
+
+ netif_set_addr(netif, &dhcp->offered_ip_addr, &sn_mask, &gw_addr);
+ /* interface is used by routing now that an address is set */
+}
+
+/**
+ * @ingroup dhcp4
+ * Renew an existing DHCP lease at the involved DHCP server.
+ *
+ * @param netif network interface which must renew its lease
+ */
+err_t
+dhcp_renew(struct netif *netif)
+{
+ struct dhcp *dhcp = netif_dhcp_data(netif);
+ err_t result;
+ u16_t msecs;
+ u8_t i;
+ struct pbuf *p_out;
+ u16_t options_out_len;
+
+ LWIP_ASSERT_CORE_LOCKED();
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_renew()\n"));
+ dhcp_set_state(dhcp, DHCP_STATE_RENEWING);
+
+ /* create and initialize the DHCP message header */
+ p_out = dhcp_create_msg(netif, dhcp, DHCP_REQUEST, &options_out_len);
+ if (p_out != NULL) {
+ struct dhcp_msg *msg_out = (struct dhcp_msg *)p_out->payload;
+ options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ options_out_len = dhcp_option_short(options_out_len, msg_out->options, DHCP_MAX_MSG_LEN(netif));
+
+ options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_PARAMETER_REQUEST_LIST, LWIP_ARRAYSIZE(dhcp_discover_request_options));
+ for (i = 0; i < LWIP_ARRAYSIZE(dhcp_discover_request_options); i++) {
+ options_out_len = dhcp_option_byte(options_out_len, msg_out->options, dhcp_discover_request_options[i]);
+ }
+
+#if LWIP_NETIF_HOSTNAME
+ options_out_len = dhcp_option_hostname(options_out_len, msg_out->options, netif);
+#endif /* LWIP_NETIF_HOSTNAME */
+
+ LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, DHCP_STATE_RENEWING, msg_out, DHCP_REQUEST, &options_out_len);
+ dhcp_option_trailer(options_out_len, msg_out->options, p_out);
+
+ result = udp_sendto_if(dhcp_pcb, p_out, &dhcp->server_ip_addr, LWIP_IANA_PORT_DHCP_SERVER, netif);
+ pbuf_free(p_out);
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_renew: RENEWING\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_renew: could not allocate DHCP request\n"));
+ result = ERR_MEM;
+ }
+ if (dhcp->tries < 255) {
+ dhcp->tries++;
+ }
+ /* back-off on retries, but to a maximum of 20 seconds */
+ msecs = (u16_t)(dhcp->tries < 10 ? dhcp->tries * 2000 : 20 * 1000);
+ dhcp->request_timeout = (u16_t)((msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_renew(): set request timeout %"U16_F" msecs\n", msecs));
+ return result;
+}
+
+/**
+ * Rebind with a DHCP server for an existing DHCP lease.
+ *
+ * @param netif network interface which must rebind with a DHCP server
+ */
+static err_t
+dhcp_rebind(struct netif *netif)
+{
+ struct dhcp *dhcp = netif_dhcp_data(netif);
+ err_t result;
+ u16_t msecs;
+ u8_t i;
+ struct pbuf *p_out;
+ u16_t options_out_len;
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind()\n"));
+ dhcp_set_state(dhcp, DHCP_STATE_REBINDING);
+
+ /* create and initialize the DHCP message header */
+ p_out = dhcp_create_msg(netif, dhcp, DHCP_REQUEST, &options_out_len);
+ if (p_out != NULL) {
+ struct dhcp_msg *msg_out = (struct dhcp_msg *)p_out->payload;
+ options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ options_out_len = dhcp_option_short(options_out_len, msg_out->options, DHCP_MAX_MSG_LEN(netif));
+
+ options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_PARAMETER_REQUEST_LIST, LWIP_ARRAYSIZE(dhcp_discover_request_options));
+ for (i = 0; i < LWIP_ARRAYSIZE(dhcp_discover_request_options); i++) {
+ options_out_len = dhcp_option_byte(options_out_len, msg_out->options, dhcp_discover_request_options[i]);
+ }
+
+#if LWIP_NETIF_HOSTNAME
+ options_out_len = dhcp_option_hostname(options_out_len, msg_out->options, netif);
+#endif /* LWIP_NETIF_HOSTNAME */
+
+ LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, DHCP_STATE_REBINDING, msg_out, DHCP_DISCOVER, &options_out_len);
+ dhcp_option_trailer(options_out_len, msg_out->options, p_out);
+
+ /* broadcast to server */
+ result = udp_sendto_if(dhcp_pcb, p_out, IP_ADDR_BROADCAST, LWIP_IANA_PORT_DHCP_SERVER, netif);
+ pbuf_free(p_out);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind: REBINDING\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_rebind: could not allocate DHCP request\n"));
+ result = ERR_MEM;
+ }
+ if (dhcp->tries < 255) {
+ dhcp->tries++;
+ }
+ msecs = (u16_t)(dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000);
+ dhcp->request_timeout = (u16_t)((msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind(): set request timeout %"U16_F" msecs\n", msecs));
+ return result;
+}
+
+/**
+ * Enter REBOOTING state to verify an existing lease
+ *
+ * @param netif network interface which must reboot
+ */
+static err_t
+dhcp_reboot(struct netif *netif)
+{
+ struct dhcp *dhcp = netif_dhcp_data(netif);
+ err_t result;
+ u16_t msecs;
+ u8_t i;
+ struct pbuf *p_out;
+ u16_t options_out_len;
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot()\n"));
+ dhcp_set_state(dhcp, DHCP_STATE_REBOOTING);
+
+ /* create and initialize the DHCP message header */
+ p_out = dhcp_create_msg(netif, dhcp, DHCP_REQUEST, &options_out_len);
+ if (p_out != NULL) {
+ struct dhcp_msg *msg_out = (struct dhcp_msg *)p_out->payload;
+ options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ options_out_len = dhcp_option_short(options_out_len, msg_out->options, DHCP_MAX_MSG_LEN_MIN_REQUIRED);
+
+ options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_REQUESTED_IP, 4);
+ options_out_len = dhcp_option_long(options_out_len, msg_out->options, lwip_ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr)));
+
+ options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_PARAMETER_REQUEST_LIST, LWIP_ARRAYSIZE(dhcp_discover_request_options));
+ for (i = 0; i < LWIP_ARRAYSIZE(dhcp_discover_request_options); i++) {
+ options_out_len = dhcp_option_byte(options_out_len, msg_out->options, dhcp_discover_request_options[i]);
+ }
+
+#if LWIP_NETIF_HOSTNAME
+ options_out_len = dhcp_option_hostname(options_out_len, msg_out->options, netif);
+#endif /* LWIP_NETIF_HOSTNAME */
+
+ LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, DHCP_STATE_REBOOTING, msg_out, DHCP_REQUEST, &options_out_len);
+ dhcp_option_trailer(options_out_len, msg_out->options, p_out);
+
+ /* broadcast to server */
+ result = udp_sendto_if(dhcp_pcb, p_out, IP_ADDR_BROADCAST, LWIP_IANA_PORT_DHCP_SERVER, netif);
+ pbuf_free(p_out);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot: REBOOTING\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_reboot: could not allocate DHCP request\n"));
+ result = ERR_MEM;
+ }
+ if (dhcp->tries < 255) {
+ dhcp->tries++;
+ }
+ msecs = (u16_t)(dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000);
+ dhcp->request_timeout = (u16_t)((msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot(): set request timeout %"U16_F" msecs\n", msecs));
+ return result;
+}
+
+/**
+ * @ingroup dhcp4
+ * Release a DHCP lease and stop DHCP statemachine (and AUTOIP if LWIP_DHCP_AUTOIP_COOP).
+ *
+ * @param netif network interface
+ */
+void
+dhcp_release_and_stop(struct netif *netif)
+{
+ struct dhcp *dhcp = netif_dhcp_data(netif);
+ ip_addr_t server_ip_addr;
+
+ LWIP_ASSERT_CORE_LOCKED();
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_release_and_stop()\n"));
+ if (dhcp == NULL) {
+ return;
+ }
+
+ /* already off? -> nothing to do */
+ if (dhcp->state == DHCP_STATE_OFF) {
+ return;
+ }
+
+ ip_addr_copy(server_ip_addr, dhcp->server_ip_addr);
+
+ /* clean old DHCP offer */
+ ip_addr_set_zero_ip4(&dhcp->server_ip_addr);
+ ip4_addr_set_zero(&dhcp->offered_ip_addr);
+ ip4_addr_set_zero(&dhcp->offered_sn_mask);
+ ip4_addr_set_zero(&dhcp->offered_gw_addr);
+#if LWIP_DHCP_BOOTP_FILE
+ ip4_addr_set_zero(&dhcp->offered_si_addr);
+#endif /* LWIP_DHCP_BOOTP_FILE */
+ dhcp->offered_t0_lease = dhcp->offered_t1_renew = dhcp->offered_t2_rebind = 0;
+ dhcp->t1_renew_time = dhcp->t2_rebind_time = dhcp->lease_used = dhcp->t0_timeout = 0;
+
+ /* send release message when current IP was assigned via DHCP */
+ if (dhcp_supplied_address(netif)) {
+ /* create and initialize the DHCP message header */
+ struct pbuf *p_out;
+ u16_t options_out_len;
+ p_out = dhcp_create_msg(netif, dhcp, DHCP_RELEASE, &options_out_len);
+ if (p_out != NULL) {
+ struct dhcp_msg *msg_out = (struct dhcp_msg *)p_out->payload;
+ options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_SERVER_ID, 4);
+ options_out_len = dhcp_option_long(options_out_len, msg_out->options, lwip_ntohl(ip4_addr_get_u32(ip_2_ip4(&server_ip_addr))));
+
+ LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, dhcp->state, msg_out, DHCP_RELEASE, &options_out_len);
+ dhcp_option_trailer(options_out_len, msg_out->options, p_out);
+
+ udp_sendto_if(dhcp_pcb, p_out, &server_ip_addr, LWIP_IANA_PORT_DHCP_SERVER, netif);
+ pbuf_free(p_out);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_release: RELEASED, DHCP_STATE_OFF\n"));
+ } else {
+ /* sending release failed, but that's not a problem since the correct behaviour of dhcp does not rely on release */
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_release: could not allocate DHCP request\n"));
+ }
+ }
+
+ /* remove IP address from interface (prevents routing from selecting this interface) */
+ netif_set_addr(netif, IP4_ADDR_ANY4, IP4_ADDR_ANY4, IP4_ADDR_ANY4);
+
+#if LWIP_DHCP_AUTOIP_COOP
+ if (dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) {
+ autoip_stop(netif);
+ dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF;
+ }
+#endif /* LWIP_DHCP_AUTOIP_COOP */
+
+ dhcp_set_state(dhcp, DHCP_STATE_OFF);
+
+ if (dhcp->pcb_allocated != 0) {
+ dhcp_dec_pcb_refcount(); /* free DHCP PCB if not needed any more */
+ dhcp->pcb_allocated = 0;
+ }
+}
+
+/**
+ * @ingroup dhcp4
+ * This function calls dhcp_release_and_stop() internally.
+ * @deprecated Use dhcp_release_and_stop() instead.
+ */
+err_t
+dhcp_release(struct netif *netif)
+{
+ dhcp_release_and_stop(netif);
+ return ERR_OK;
+}
+
+/**
+ * @ingroup dhcp4
+ * This function calls dhcp_release_and_stop() internally.
+ * @deprecated Use dhcp_release_and_stop() instead.
+ */
+void
+dhcp_stop(struct netif *netif)
+{
+ dhcp_release_and_stop(netif);
+}
+
+/*
+ * Set the DHCP state of a DHCP client.
+ *
+ * If the state changed, reset the number of tries.
+ */
+static void
+dhcp_set_state(struct dhcp *dhcp, u8_t new_state)
+{
+ if (new_state != dhcp->state) {
+ dhcp->state = new_state;
+ dhcp->tries = 0;
+ dhcp->request_timeout = 0;
+ }
+}
+
+/*
+ * Concatenate an option type and length field to the outgoing
+ * DHCP message.
+ *
+ */
+static u16_t
+dhcp_option(u16_t options_out_len, u8_t *options, u8_t option_type, u8_t option_len)
+{
+ LWIP_ASSERT("dhcp_option: options_out_len + 2 + option_len <= DHCP_OPTIONS_LEN", options_out_len + 2U + option_len <= DHCP_OPTIONS_LEN);
+ options[options_out_len++] = option_type;
+ options[options_out_len++] = option_len;
+ return options_out_len;
+}
+/*
+ * Concatenate a single byte to the outgoing DHCP message.
+ *
+ */
+static u16_t
+dhcp_option_byte(u16_t options_out_len, u8_t *options, u8_t value)
+{
+ LWIP_ASSERT("dhcp_option_byte: options_out_len < DHCP_OPTIONS_LEN", options_out_len < DHCP_OPTIONS_LEN);
+ options[options_out_len++] = value;
+ return options_out_len;
+}
+
+static u16_t
+dhcp_option_short(u16_t options_out_len, u8_t *options, u16_t value)
+{
+ LWIP_ASSERT("dhcp_option_short: options_out_len + 2 <= DHCP_OPTIONS_LEN", options_out_len + 2U <= DHCP_OPTIONS_LEN);
+ options[options_out_len++] = (u8_t)((value & 0xff00U) >> 8);
+ options[options_out_len++] = (u8_t) (value & 0x00ffU);
+ return options_out_len;
+}
+
+static u16_t
+dhcp_option_long(u16_t options_out_len, u8_t *options, u32_t value)
+{
+ LWIP_ASSERT("dhcp_option_long: options_out_len + 4 <= DHCP_OPTIONS_LEN", options_out_len + 4U <= DHCP_OPTIONS_LEN);
+ options[options_out_len++] = (u8_t)((value & 0xff000000UL) >> 24);
+ options[options_out_len++] = (u8_t)((value & 0x00ff0000UL) >> 16);
+ options[options_out_len++] = (u8_t)((value & 0x0000ff00UL) >> 8);
+ options[options_out_len++] = (u8_t)((value & 0x000000ffUL));
+ return options_out_len;
+}
+
+#if LWIP_NETIF_HOSTNAME
+static u16_t
+dhcp_option_hostname(u16_t options_out_len, u8_t *options, struct netif *netif)
+{
+ if (netif->hostname != NULL) {
+ size_t namelen = strlen(netif->hostname);
+ if (namelen > 0) {
+ size_t len;
+ const char *p = netif->hostname;
+ /* Shrink len to available bytes (need 2 bytes for OPTION_HOSTNAME
+ and 1 byte for trailer) */
+ size_t available = DHCP_OPTIONS_LEN - options_out_len - 3;
+ LWIP_ASSERT("DHCP: hostname is too long!", namelen <= available);
+ len = LWIP_MIN(namelen, available);
+ LWIP_ASSERT("DHCP: hostname is too long!", len <= 0xFF);
+ options_out_len = dhcp_option(options_out_len, options, DHCP_OPTION_HOSTNAME, (u8_t)len);
+ while (len--) {
+ options_out_len = dhcp_option_byte(options_out_len, options, *p++);
+ }
+ }
+ }
+ return options_out_len;
+}
+#endif /* LWIP_NETIF_HOSTNAME */
+
+/**
+ * Extract the DHCP message and the DHCP options.
+ *
+ * Extract the DHCP message and the DHCP options, each into a contiguous
+ * piece of memory. As a DHCP message is variable sized by its options,
+ * and also allows overriding some fields for options, the easy approach
+ * is to first unfold the options into a contiguous piece of memory, and
+ * use that further on.
+ *
+ */
+static err_t
+dhcp_parse_reply(struct pbuf *p, struct dhcp *dhcp)
+{
+ u8_t *options;
+ u16_t offset;
+ u16_t offset_max;
+ u16_t options_idx;
+ u16_t options_idx_max;
+ struct pbuf *q;
+ int parse_file_as_options = 0;
+ int parse_sname_as_options = 0;
+ struct dhcp_msg *msg_in;
+#if LWIP_DHCP_BOOTP_FILE
+ int file_overloaded = 0;
+#endif
+
+ LWIP_UNUSED_ARG(dhcp);
+
+ /* clear received options */
+ dhcp_clear_all_options(dhcp);
+ /* check that beginning of dhcp_msg (up to and including chaddr) is in first pbuf */
+ if (p->len < DHCP_SNAME_OFS) {
+ return ERR_BUF;
+ }
+ msg_in = (struct dhcp_msg *)p->payload;
+#if LWIP_DHCP_BOOTP_FILE
+ /* clear boot file name */
+ dhcp->boot_file_name[0] = 0;
+#endif /* LWIP_DHCP_BOOTP_FILE */
+
+ /* parse options */
+
+ /* start with options field */
+ options_idx = DHCP_OPTIONS_OFS;
+ /* parse options to the end of the received packet */
+ options_idx_max = p->tot_len;
+again:
+ q = p;
+ while ((q != NULL) && (options_idx >= q->len)) {
+ options_idx = (u16_t)(options_idx - q->len);
+ options_idx_max = (u16_t)(options_idx_max - q->len);
+ q = q->next;
+ }
+ if (q == NULL) {
+ return ERR_BUF;
+ }
+ offset = options_idx;
+ offset_max = options_idx_max;
+ options = (u8_t *)q->payload;
+ /* at least 1 byte to read and no end marker, then at least 3 bytes to read? */
+ while ((q != NULL) && (offset < offset_max) && (options[offset] != DHCP_OPTION_END)) {
+ u8_t op = options[offset];
+ u8_t len;
+ u8_t decode_len = 0;
+ int decode_idx = -1;
+ u16_t val_offset = (u16_t)(offset + 2);
+ if (val_offset < offset) {
+ /* overflow */
+ return ERR_BUF;
+ }
+ /* len byte might be in the next pbuf */
+ if ((offset + 1) < q->len) {
+ len = options[offset + 1];
+ } else {
+ len = (q->next != NULL ? ((u8_t *)q->next->payload)[0] : 0);
+ }
+ /* LWIP_DEBUGF(DHCP_DEBUG, ("msg_offset=%"U16_F", q->len=%"U16_F, msg_offset, q->len)); */
+ decode_len = len;
+ switch (op) {
+ /* case(DHCP_OPTION_END): handled above */
+ case (DHCP_OPTION_PAD):
+ /* special option: no len encoded */
+ decode_len = len = 0;
+ /* will be increased below */
+ break;
+ case (DHCP_OPTION_SUBNET_MASK):
+ LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
+ decode_idx = DHCP_OPTION_IDX_SUBNET_MASK;
+ break;
+ case (DHCP_OPTION_ROUTER):
+ decode_len = 4; /* only copy the first given router */
+ LWIP_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;);
+ decode_idx = DHCP_OPTION_IDX_ROUTER;
+ break;
+#if LWIP_DHCP_PROVIDE_DNS_SERVERS
+ case (DHCP_OPTION_DNS_SERVER):
+ /* special case: there might be more than one server */
+ LWIP_ERROR("len %% 4 == 0", len % 4 == 0, return ERR_VAL;);
+ /* limit number of DNS servers */
+ decode_len = LWIP_MIN(len, 4 * DNS_MAX_SERVERS);
+ LWIP_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;);
+ decode_idx = DHCP_OPTION_IDX_DNS_SERVER;
+ break;
+#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS */
+ case (DHCP_OPTION_LEASE_TIME):
+ LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
+ decode_idx = DHCP_OPTION_IDX_LEASE_TIME;
+ break;
+#if LWIP_DHCP_GET_NTP_SRV
+ case (DHCP_OPTION_NTP):
+ /* special case: there might be more than one server */
+ LWIP_ERROR("len %% 4 == 0", len % 4 == 0, return ERR_VAL;);
+ /* limit number of NTP servers */
+ decode_len = LWIP_MIN(len, 4 * LWIP_DHCP_MAX_NTP_SERVERS);
+ LWIP_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;);
+ decode_idx = DHCP_OPTION_IDX_NTP_SERVER;
+ break;
+#endif /* LWIP_DHCP_GET_NTP_SRV*/
+ case (DHCP_OPTION_OVERLOAD):
+ LWIP_ERROR("len == 1", len == 1, return ERR_VAL;);
+ /* decode overload only in options, not in file/sname: invalid packet */
+ LWIP_ERROR("overload in file/sname", options_idx == DHCP_OPTIONS_OFS, return ERR_VAL;);
+ decode_idx = DHCP_OPTION_IDX_OVERLOAD;
+ break;
+ case (DHCP_OPTION_MESSAGE_TYPE):
+ LWIP_ERROR("len == 1", len == 1, return ERR_VAL;);
+ decode_idx = DHCP_OPTION_IDX_MSG_TYPE;
+ break;
+ case (DHCP_OPTION_SERVER_ID):
+ LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
+ decode_idx = DHCP_OPTION_IDX_SERVER_ID;
+ break;
+ case (DHCP_OPTION_T1):
+ LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
+ decode_idx = DHCP_OPTION_IDX_T1;
+ break;
+ case (DHCP_OPTION_T2):
+ LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
+ decode_idx = DHCP_OPTION_IDX_T2;
+ break;
+ default:
+ decode_len = 0;
+ LWIP_DEBUGF(DHCP_DEBUG, ("skipping option %"U16_F" in options\n", (u16_t)op));
+ LWIP_HOOK_DHCP_PARSE_OPTION(ip_current_netif(), dhcp, dhcp->state, msg_in,
+ dhcp_option_given(dhcp, DHCP_OPTION_IDX_MSG_TYPE) ? (u8_t)dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE) : 0,
+ op, len, q, val_offset);
+ break;
+ }
+ if (op == DHCP_OPTION_PAD) {
+ offset++;
+ } else {
+ if (offset + len + 2 > 0xFFFF) {
+ /* overflow */
+ return ERR_BUF;
+ }
+ offset = (u16_t)(offset + len + 2);
+ if (decode_len > 0) {
+ u32_t value = 0;
+ u16_t copy_len;
+decode_next:
+ LWIP_ASSERT("check decode_idx", decode_idx >= 0 && decode_idx < DHCP_OPTION_IDX_MAX);
+ if (!dhcp_option_given(dhcp, decode_idx)) {
+ copy_len = LWIP_MIN(decode_len, 4);
+ if (pbuf_copy_partial(q, &value, copy_len, val_offset) != copy_len) {
+ return ERR_BUF;
+ }
+ if (decode_len > 4) {
+ /* decode more than one u32_t */
+ u16_t next_val_offset;
+ LWIP_ERROR("decode_len %% 4 == 0", decode_len % 4 == 0, return ERR_VAL;);
+ dhcp_got_option(dhcp, decode_idx);
+ dhcp_set_option_value(dhcp, decode_idx, lwip_htonl(value));
+ decode_len = (u8_t)(decode_len - 4);
+ next_val_offset = (u16_t)(val_offset + 4);
+ if (next_val_offset < val_offset) {
+ /* overflow */
+ return ERR_BUF;
+ }
+ val_offset = next_val_offset;
+ decode_idx++;
+ goto decode_next;
+ } else if (decode_len == 4) {
+ value = lwip_ntohl(value);
+ } else {
+ LWIP_ERROR("invalid decode_len", decode_len == 1, return ERR_VAL;);
+ value = ((u8_t *)&value)[0];
+ }
+ dhcp_got_option(dhcp, decode_idx);
+ dhcp_set_option_value(dhcp, decode_idx, value);
+ }
+ }
+ }
+ if (offset >= q->len) {
+ offset = (u16_t)(offset - q->len);
+ offset_max = (u16_t)(offset_max - q->len);
+ if (offset < offset_max) {
+ q = q->next;
+ LWIP_ERROR("next pbuf was null", q != NULL, return ERR_VAL;);
+ options = (u8_t *)q->payload;
+ } else {
+ /* We've run out of bytes, probably no end marker. Don't proceed. */
+ return ERR_BUF;
+ }
+ }
+ }
+ /* is this an overloaded message? */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_OVERLOAD)) {
+ u32_t overload = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_OVERLOAD);
+ dhcp_clear_option(dhcp, DHCP_OPTION_IDX_OVERLOAD);
+ if (overload == DHCP_OVERLOAD_FILE) {
+ parse_file_as_options = 1;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded file field\n"));
+ } else if (overload == DHCP_OVERLOAD_SNAME) {
+ parse_sname_as_options = 1;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded sname field\n"));
+ } else if (overload == DHCP_OVERLOAD_SNAME_FILE) {
+ parse_sname_as_options = 1;
+ parse_file_as_options = 1;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded sname and file field\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("invalid overload option: %d\n", (int)overload));
+ }
+ }
+ if (parse_file_as_options) {
+ /* if both are overloaded, parse file first and then sname (RFC 2131 ch. 4.1) */
+ parse_file_as_options = 0;
+ options_idx = DHCP_FILE_OFS;
+ options_idx_max = DHCP_FILE_OFS + DHCP_FILE_LEN;
+#if LWIP_DHCP_BOOTP_FILE
+ file_overloaded = 1;
+#endif
+ goto again;
+ } else if (parse_sname_as_options) {
+ parse_sname_as_options = 0;
+ options_idx = DHCP_SNAME_OFS;
+ options_idx_max = DHCP_SNAME_OFS + DHCP_SNAME_LEN;
+ goto again;
+ }
+#if LWIP_DHCP_BOOTP_FILE
+ if (!file_overloaded) {
+ /* only do this for ACK messages */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_MSG_TYPE) &&
+ (dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE) == DHCP_ACK))
+ /* copy bootp file name, don't care for sname (server hostname) */
+ if (pbuf_copy_partial(p, dhcp->boot_file_name, DHCP_FILE_LEN-1, DHCP_FILE_OFS) != (DHCP_FILE_LEN-1)) {
+ return ERR_BUF;
+ }
+ /* make sure the string is really NULL-terminated */
+ dhcp->boot_file_name[DHCP_FILE_LEN-1] = 0;
+ }
+#endif /* LWIP_DHCP_BOOTP_FILE */
+ return ERR_OK;
+}
+
+/**
+ * If an incoming DHCP message is in response to us, then trigger the state machine
+ */
+static void
+dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
+{
+ struct netif *netif = ip_current_input_netif();
+ struct dhcp *dhcp = netif_dhcp_data(netif);
+ struct dhcp_msg *reply_msg = (struct dhcp_msg *)p->payload;
+ u8_t msg_type;
+ u8_t i;
+ struct dhcp_msg *msg_in;
+
+ LWIP_UNUSED_ARG(arg);
+
+ /* Caught DHCP message from netif that does not have DHCP enabled? -> not interested */
+ if ((dhcp == NULL) || (dhcp->pcb_allocated == 0)) {
+ goto free_pbuf_and_return;
+ }
+
+ LWIP_ASSERT("invalid server address type", IP_IS_V4(addr));
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_recv(pbuf = %p) from DHCP server %"U16_F".%"U16_F".%"U16_F".%"U16_F" port %"U16_F"\n", (void *)p,
+ ip4_addr1_16(ip_2_ip4(addr)), ip4_addr2_16(ip_2_ip4(addr)), ip4_addr3_16(ip_2_ip4(addr)), ip4_addr4_16(ip_2_ip4(addr)), port));
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("pbuf->len = %"U16_F"\n", p->len));
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("pbuf->tot_len = %"U16_F"\n", p->tot_len));
+ /* prevent warnings about unused arguments */
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_UNUSED_ARG(addr);
+ LWIP_UNUSED_ARG(port);
+
+ if (p->len < DHCP_MIN_REPLY_LEN) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCP reply message or pbuf too short\n"));
+ goto free_pbuf_and_return;
+ }
+
+ if (reply_msg->op != DHCP_BOOTREPLY) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("not a DHCP reply message, but type %"U16_F"\n", (u16_t)reply_msg->op));
+ goto free_pbuf_and_return;
+ }
+ /* iterate through hardware address and match against DHCP message */
+ for (i = 0; i < netif->hwaddr_len && i < LWIP_MIN(DHCP_CHADDR_LEN, NETIF_MAX_HWADDR_LEN); i++) {
+ if (netif->hwaddr[i] != reply_msg->chaddr[i]) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("netif->hwaddr[%"U16_F"]==%02"X16_F" != reply_msg->chaddr[%"U16_F"]==%02"X16_F"\n",
+ (u16_t)i, (u16_t)netif->hwaddr[i], (u16_t)i, (u16_t)reply_msg->chaddr[i]));
+ goto free_pbuf_and_return;
+ }
+ }
+ /* match transaction ID against what we expected */
+ if (lwip_ntohl(reply_msg->xid) != dhcp->xid) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("transaction id mismatch reply_msg->xid(%"X32_F")!=dhcp->xid(%"X32_F")\n", lwip_ntohl(reply_msg->xid), dhcp->xid));
+ goto free_pbuf_and_return;
+ }
+ /* option fields could be unfold? */
+ if (dhcp_parse_reply(p, dhcp) != ERR_OK) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("problem unfolding DHCP message - too short on memory?\n"));
+ goto free_pbuf_and_return;
+ }
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("searching DHCP_OPTION_MESSAGE_TYPE\n"));
+ /* obtain pointer to DHCP message type */
+ if (!dhcp_option_given(dhcp, DHCP_OPTION_IDX_MSG_TYPE)) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCP_OPTION_MESSAGE_TYPE option not found\n"));
+ goto free_pbuf_and_return;
+ }
+
+ msg_in = (struct dhcp_msg *)p->payload;
+ /* read DHCP message type */
+ msg_type = (u8_t)dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE);
+ /* message type is DHCP ACK? */
+ if (msg_type == DHCP_ACK) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_ACK received\n"));
+ /* in requesting state? */
+ if (dhcp->state == DHCP_STATE_REQUESTING) {
+ dhcp_handle_ack(netif, msg_in);
+#if DHCP_DOES_ARP_CHECK
+ if ((netif->flags & NETIF_FLAG_ETHARP) != 0) {
+ /* check if the acknowledged lease address is already in use */
+ dhcp_check(netif);
+ } else {
+ /* bind interface to the acknowledged lease address */
+ dhcp_bind(netif);
+ }
+#else
+ /* bind interface to the acknowledged lease address */
+ dhcp_bind(netif);
+#endif
+ }
+ /* already bound to the given lease address? */
+ else if ((dhcp->state == DHCP_STATE_REBOOTING) || (dhcp->state == DHCP_STATE_REBINDING) ||
+ (dhcp->state == DHCP_STATE_RENEWING)) {
+ dhcp_handle_ack(netif, msg_in);
+ dhcp_bind(netif);
+ }
+ }
+ /* received a DHCP_NAK in appropriate state? */
+ else if ((msg_type == DHCP_NAK) &&
+ ((dhcp->state == DHCP_STATE_REBOOTING) || (dhcp->state == DHCP_STATE_REQUESTING) ||
+ (dhcp->state == DHCP_STATE_REBINDING) || (dhcp->state == DHCP_STATE_RENEWING ))) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_NAK received\n"));
+ dhcp_handle_nak(netif);
+ }
+ /* received a DHCP_OFFER in DHCP_STATE_SELECTING state? */
+ else if ((msg_type == DHCP_OFFER) && (dhcp->state == DHCP_STATE_SELECTING)) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_OFFER received in DHCP_STATE_SELECTING state\n"));
+ /* remember offered lease */
+ dhcp_handle_offer(netif, msg_in);
+ }
+
+free_pbuf_and_return:
+ pbuf_free(p);
+}
+
+/**
+ * Create a DHCP request, fill in common headers
+ *
+ * @param netif the netif under DHCP control
+ * @param dhcp dhcp control struct
+ * @param message_type message type of the request
+ */
+static struct pbuf *
+dhcp_create_msg(struct netif *netif, struct dhcp *dhcp, u8_t message_type, u16_t *options_out_len)
+{
+ u16_t i;
+ struct pbuf *p_out;
+ struct dhcp_msg *msg_out;
+ u16_t options_out_len_loc;
+
+#ifndef DHCP_GLOBAL_XID
+ /** default global transaction identifier starting value (easy to match
+ * with a packet analyser). We simply increment for each new request.
+ * Predefine DHCP_GLOBAL_XID to a better value or a function call to generate one
+ * at runtime, any supporting function prototypes can be defined in DHCP_GLOBAL_XID_HEADER */
+#if DHCP_CREATE_RAND_XID && defined(LWIP_RAND)
+ static u32_t xid;
+#else /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */
+ static u32_t xid = 0xABCD0000;
+#endif /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */
+#else
+ if (!xid_initialised) {
+ xid = DHCP_GLOBAL_XID;
+ xid_initialised = !xid_initialised;
+ }
+#endif
+ LWIP_ERROR("dhcp_create_msg: netif != NULL", (netif != NULL), return NULL;);
+ LWIP_ERROR("dhcp_create_msg: dhcp != NULL", (dhcp != NULL), return NULL;);
+ p_out = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcp_msg), PBUF_RAM);
+ if (p_out == NULL) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("dhcp_create_msg(): could not allocate pbuf\n"));
+ return NULL;
+ }
+ LWIP_ASSERT("dhcp_create_msg: check that first pbuf can hold struct dhcp_msg",
+ (p_out->len >= sizeof(struct dhcp_msg)));
+
+ /* DHCP_REQUEST should reuse 'xid' from DHCPOFFER */
+ if ((message_type != DHCP_REQUEST) || (dhcp->state == DHCP_STATE_REBOOTING)) {
+ /* reuse transaction identifier in retransmissions */
+ if (dhcp->tries == 0) {
+#if DHCP_CREATE_RAND_XID && defined(LWIP_RAND)
+ xid = LWIP_RAND();
+#else /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */
+ xid++;
+#endif /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */
+ }
+ dhcp->xid = xid;
+ }
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE,
+ ("transaction id xid(%"X32_F")\n", xid));
+
+ msg_out = (struct dhcp_msg *)p_out->payload;
+ memset(msg_out, 0, sizeof(struct dhcp_msg));
+
+ msg_out->op = DHCP_BOOTREQUEST;
+ /* @todo: make link layer independent */
+ msg_out->htype = LWIP_IANA_HWTYPE_ETHERNET;
+ msg_out->hlen = netif->hwaddr_len;
+ msg_out->xid = lwip_htonl(dhcp->xid);
+ /* we don't need the broadcast flag since we can receive unicast traffic
+ before being fully configured! */
+ /* set ciaddr to netif->ip_addr based on message_type and state */
+ if ((message_type == DHCP_INFORM) || (message_type == DHCP_DECLINE) || (message_type == DHCP_RELEASE) ||
+ ((message_type == DHCP_REQUEST) && /* DHCP_STATE_BOUND not used for sending! */
+ ((dhcp->state == DHCP_STATE_RENEWING) || dhcp->state == DHCP_STATE_REBINDING))) {
+ ip4_addr_copy(msg_out->ciaddr, *netif_ip4_addr(netif));
+ }
+ for (i = 0; i < LWIP_MIN(DHCP_CHADDR_LEN, NETIF_MAX_HWADDR_LEN); i++) {
+ /* copy netif hardware address (padded with zeroes through memset already) */
+ msg_out->chaddr[i] = netif->hwaddr[i];
+ }
+ msg_out->cookie = PP_HTONL(DHCP_MAGIC_COOKIE);
+ /* Add option MESSAGE_TYPE */
+ options_out_len_loc = dhcp_option(0, msg_out->options, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN);
+ options_out_len_loc = dhcp_option_byte(options_out_len_loc, msg_out->options, message_type);
+ if (options_out_len) {
+ *options_out_len = options_out_len_loc;
+ }
+ return p_out;
+}
+
+/**
+ * Add a DHCP message trailer
+ *
+ * Adds the END option to the DHCP message, and if
+ * necessary, up to three padding bytes.
+ */
+static void
+dhcp_option_trailer(u16_t options_out_len, u8_t *options, struct pbuf *p_out)
+{
+ options[options_out_len++] = DHCP_OPTION_END;
+ /* packet is too small, or not 4 byte aligned? */
+ while (((options_out_len < DHCP_MIN_OPTIONS_LEN) || (options_out_len & 3)) &&
+ (options_out_len < DHCP_OPTIONS_LEN)) {
+ /* add a fill/padding byte */
+ options[options_out_len++] = 0;
+ }
+ /* shrink the pbuf to the actual content length */
+ pbuf_realloc(p_out, (u16_t)(sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + options_out_len));
+}
+
+/** check if DHCP supplied netif->ip_addr
+ *
+ * @param netif the netif to check
+ * @return 1 if DHCP supplied netif->ip_addr (states BOUND or RENEWING),
+ * 0 otherwise
+ */
+u8_t
+dhcp_supplied_address(const struct netif *netif)
+{
+ if ((netif != NULL) && (netif_dhcp_data(netif) != NULL)) {
+ struct dhcp *dhcp = netif_dhcp_data(netif);
+ return (dhcp->state == DHCP_STATE_BOUND) || (dhcp->state == DHCP_STATE_RENEWING) ||
+ (dhcp->state == DHCP_STATE_REBINDING);
+ }
+ return 0;
+}
+
+#endif /* LWIP_IPV4 && LWIP_DHCP */
diff --git a/resources/ethernet/LwIP/core/ipv4/etharp.c b/resources/ethernet/LwIP/core/ipv4/etharp.c
new file mode 100644
index 00000000..94ef3449
--- /dev/null
+++ b/resources/ethernet/LwIP/core/ipv4/etharp.c
@@ -0,0 +1,1204 @@
+/**
+ * @file
+ * Address Resolution Protocol module for IP over Ethernet
+ *
+ * Functionally, ARP is divided into two parts. The first maps an IP address
+ * to a physical address when sending a packet, and the second part answers
+ * requests from other machines for our physical address.
+ *
+ * This implementation complies with RFC 826 (Ethernet ARP). It supports
+ * Gratuitious ARP from RFC3220 (IP Mobility Support for IPv4) section 4.6
+ * if an interface calls etharp_gratuitous(our_netif) upon address change.
+ */
+
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * Copyright (c) 2003-2004 Leon Woestenberg
+ * Copyright (c) 2003-2004 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV4 && LWIP_ARP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/etharp.h"
+#include "lwip/stats.h"
+#include "lwip/snmp.h"
+#include "lwip/dhcp.h"
+#include "lwip/autoip.h"
+#include "lwip/prot/iana.h"
+#include "netif/ethernet.h"
+
+#include
+
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+
+/** Re-request a used ARP entry 1 minute before it would expire to prevent
+ * breaking a steadily used connection because the ARP entry timed out. */
+#define ARP_AGE_REREQUEST_USED_UNICAST (ARP_MAXAGE - 30)
+#define ARP_AGE_REREQUEST_USED_BROADCAST (ARP_MAXAGE - 15)
+
+/** the time an ARP entry stays pending after first request,
+ * for ARP_TMR_INTERVAL = 1000, this is
+ * 10 seconds.
+ *
+ * @internal Keep this number at least 2, otherwise it might
+ * run out instantly if the timeout occurs directly after a request.
+ */
+#define ARP_MAXPENDING 5
+
+/** ARP states */
+enum etharp_state {
+ ETHARP_STATE_EMPTY = 0,
+ ETHARP_STATE_PENDING,
+ ETHARP_STATE_STABLE,
+ ETHARP_STATE_STABLE_REREQUESTING_1,
+ ETHARP_STATE_STABLE_REREQUESTING_2
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+ , ETHARP_STATE_STATIC
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+};
+
+struct etharp_entry {
+#if ARP_QUEUEING
+ /** Pointer to queue of pending outgoing packets on this ARP entry. */
+ struct etharp_q_entry *q;
+#else /* ARP_QUEUEING */
+ /** Pointer to a single pending outgoing packet on this ARP entry. */
+ struct pbuf *q;
+#endif /* ARP_QUEUEING */
+ ip4_addr_t ipaddr;
+ struct netif *netif;
+ struct eth_addr ethaddr;
+ u16_t ctime;
+ u8_t state;
+};
+
+static struct etharp_entry arp_table[ARP_TABLE_SIZE];
+
+#if !LWIP_NETIF_HWADDRHINT
+static netif_addr_idx_t etharp_cached_entry;
+#endif /* !LWIP_NETIF_HWADDRHINT */
+
+/** Try hard to create a new entry - we want the IP address to appear in
+ the cache (even if this means removing an active entry or so). */
+#define ETHARP_FLAG_TRY_HARD 1
+#define ETHARP_FLAG_FIND_ONLY 2
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+#define ETHARP_FLAG_STATIC_ENTRY 4
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+
+#if LWIP_NETIF_HWADDRHINT
+#define ETHARP_SET_ADDRHINT(netif, addrhint) do { if (((netif) != NULL) && ((netif)->hints != NULL)) { \
+ (netif)->hints->addr_hint = (addrhint); }} while(0)
+#else /* LWIP_NETIF_HWADDRHINT */
+#define ETHARP_SET_ADDRHINT(netif, addrhint) (etharp_cached_entry = (addrhint))
+#endif /* LWIP_NETIF_HWADDRHINT */
+
+
+/* Check for maximum ARP_TABLE_SIZE */
+#if (ARP_TABLE_SIZE > NETIF_ADDR_IDX_MAX)
+#error "ARP_TABLE_SIZE must fit in an s16_t, you have to reduce it in your lwipopts.h"
+#endif
+
+
+static err_t etharp_request_dst(struct netif *netif, const ip4_addr_t *ipaddr, const struct eth_addr *hw_dst_addr);
+static err_t etharp_raw(struct netif *netif,
+ const struct eth_addr *ethsrc_addr, const struct eth_addr *ethdst_addr,
+ const struct eth_addr *hwsrc_addr, const ip4_addr_t *ipsrc_addr,
+ const struct eth_addr *hwdst_addr, const ip4_addr_t *ipdst_addr,
+ const u16_t opcode);
+
+#if ARP_QUEUEING
+/**
+ * Free a complete queue of etharp entries
+ *
+ * @param q a qeueue of etharp_q_entry's to free
+ */
+static void
+free_etharp_q(struct etharp_q_entry *q)
+{
+ struct etharp_q_entry *r;
+ LWIP_ASSERT("q != NULL", q != NULL);
+ while (q) {
+ r = q;
+ q = q->next;
+ LWIP_ASSERT("r->p != NULL", (r->p != NULL));
+ pbuf_free(r->p);
+ memp_free(MEMP_ARP_QUEUE, r);
+ }
+}
+#else /* ARP_QUEUEING */
+
+/** Compatibility define: free the queued pbuf */
+#define free_etharp_q(q) pbuf_free(q)
+
+#endif /* ARP_QUEUEING */
+
+/** Clean up ARP table entries */
+static void
+etharp_free_entry(int i)
+{
+ /* remove from SNMP ARP index tree */
+ mib2_remove_arp_entry(arp_table[i].netif, &arp_table[i].ipaddr);
+ /* and empty packet queue */
+ if (arp_table[i].q != NULL) {
+ /* remove all queued packets */
+ LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_free_entry: freeing entry %"U16_F", packet queue %p.\n", (u16_t)i, (void *)(arp_table[i].q)));
+ free_etharp_q(arp_table[i].q);
+ arp_table[i].q = NULL;
+ }
+ /* recycle entry for re-use */
+ arp_table[i].state = ETHARP_STATE_EMPTY;
+#ifdef LWIP_DEBUG
+ /* for debugging, clean out the complete entry */
+ arp_table[i].ctime = 0;
+ arp_table[i].netif = NULL;
+ ip4_addr_set_zero(&arp_table[i].ipaddr);
+ arp_table[i].ethaddr = ethzero;
+#endif /* LWIP_DEBUG */
+}
+
+/**
+ * Clears expired entries in the ARP table.
+ *
+ * This function should be called every ARP_TMR_INTERVAL milliseconds (1 second),
+ * in order to expire entries in the ARP table.
+ */
+void
+etharp_tmr(void)
+{
+ int i;
+
+ LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer\n"));
+ /* remove expired entries from the ARP table */
+ for (i = 0; i < ARP_TABLE_SIZE; ++i) {
+ u8_t state = arp_table[i].state;
+ if (state != ETHARP_STATE_EMPTY
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+ && (state != ETHARP_STATE_STATIC)
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+ ) {
+ arp_table[i].ctime++;
+ if ((arp_table[i].ctime >= ARP_MAXAGE) ||
+ ((arp_table[i].state == ETHARP_STATE_PENDING) &&
+ (arp_table[i].ctime >= ARP_MAXPENDING))) {
+ /* pending or stable entry has become old! */
+ LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: expired %s entry %d.\n",
+ arp_table[i].state >= ETHARP_STATE_STABLE ? "stable" : "pending", i));
+ /* clean up entries that have just been expired */
+ etharp_free_entry(i);
+ } else if (arp_table[i].state == ETHARP_STATE_STABLE_REREQUESTING_1) {
+ /* Don't send more than one request every 2 seconds. */
+ arp_table[i].state = ETHARP_STATE_STABLE_REREQUESTING_2;
+ } else if (arp_table[i].state == ETHARP_STATE_STABLE_REREQUESTING_2) {
+ /* Reset state to stable, so that the next transmitted packet will
+ re-send an ARP request. */
+ arp_table[i].state = ETHARP_STATE_STABLE;
+ } else if (arp_table[i].state == ETHARP_STATE_PENDING) {
+ /* still pending, resend an ARP query */
+ etharp_request(arp_table[i].netif, &arp_table[i].ipaddr);
+ }
+ }
+ }
+}
+
+/**
+ * Search the ARP table for a matching or new entry.
+ *
+ * If an IP address is given, return a pending or stable ARP entry that matches
+ * the address. If no match is found, create a new entry with this address set,
+ * but in state ETHARP_EMPTY. The caller must check and possibly change the
+ * state of the returned entry.
+ *
+ * If ipaddr is NULL, return a initialized new entry in state ETHARP_EMPTY.
+ *
+ * In all cases, attempt to create new entries from an empty entry. If no
+ * empty entries are available and ETHARP_FLAG_TRY_HARD flag is set, recycle
+ * old entries. Heuristic choose the least important entry for recycling.
+ *
+ * @param ipaddr IP address to find in ARP cache, or to add if not found.
+ * @param flags See @ref etharp_state
+ * @param netif netif related to this address (used for NETIF_HWADDRHINT)
+ *
+ * @return The ARP entry index that matched or is created, ERR_MEM if no
+ * entry is found or could be recycled.
+ */
+static s16_t
+etharp_find_entry(const ip4_addr_t *ipaddr, u8_t flags, struct netif *netif)
+{
+ s16_t old_pending = ARP_TABLE_SIZE, old_stable = ARP_TABLE_SIZE;
+ s16_t empty = ARP_TABLE_SIZE;
+ s16_t i = 0;
+ /* oldest entry with packets on queue */
+ s16_t old_queue = ARP_TABLE_SIZE;
+ /* its age */
+ u16_t age_queue = 0, age_pending = 0, age_stable = 0;
+
+ LWIP_UNUSED_ARG(netif);
+
+ /**
+ * a) do a search through the cache, remember candidates
+ * b) select candidate entry
+ * c) create new entry
+ */
+
+ /* a) in a single search sweep, do all of this
+ * 1) remember the first empty entry (if any)
+ * 2) remember the oldest stable entry (if any)
+ * 3) remember the oldest pending entry without queued packets (if any)
+ * 4) remember the oldest pending entry with queued packets (if any)
+ * 5) search for a matching IP entry, either pending or stable
+ * until 5 matches, or all entries are searched for.
+ */
+
+ for (i = 0; i < ARP_TABLE_SIZE; ++i) {
+ u8_t state = arp_table[i].state;
+ /* no empty entry found yet and now we do find one? */
+ if ((empty == ARP_TABLE_SIZE) && (state == ETHARP_STATE_EMPTY)) {
+ LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_find_entry: found empty entry %d\n", (int)i));
+ /* remember first empty entry */
+ empty = i;
+ } else if (state != ETHARP_STATE_EMPTY) {
+ LWIP_ASSERT("state == ETHARP_STATE_PENDING || state >= ETHARP_STATE_STABLE",
+ state == ETHARP_STATE_PENDING || state >= ETHARP_STATE_STABLE);
+ /* if given, does IP address match IP address in ARP entry? */
+ if (ipaddr && ip4_addr_cmp(ipaddr, &arp_table[i].ipaddr)
+#if ETHARP_TABLE_MATCH_NETIF
+ && ((netif == NULL) || (netif == arp_table[i].netif))
+#endif /* ETHARP_TABLE_MATCH_NETIF */
+ ) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: found matching entry %d\n", (int)i));
+ /* found exact IP address match, simply bail out */
+ return i;
+ }
+ /* pending entry? */
+ if (state == ETHARP_STATE_PENDING) {
+ /* pending with queued packets? */
+ if (arp_table[i].q != NULL) {
+ if (arp_table[i].ctime >= age_queue) {
+ old_queue = i;
+ age_queue = arp_table[i].ctime;
+ }
+ } else
+ /* pending without queued packets? */
+ {
+ if (arp_table[i].ctime >= age_pending) {
+ old_pending = i;
+ age_pending = arp_table[i].ctime;
+ }
+ }
+ /* stable entry? */
+ } else if (state >= ETHARP_STATE_STABLE) {
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+ /* don't record old_stable for static entries since they never expire */
+ if (state < ETHARP_STATE_STATIC)
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+ {
+ /* remember entry with oldest stable entry in oldest, its age in maxtime */
+ if (arp_table[i].ctime >= age_stable) {
+ old_stable = i;
+ age_stable = arp_table[i].ctime;
+ }
+ }
+ }
+ }
+ }
+ /* { we have no match } => try to create a new entry */
+
+ /* don't create new entry, only search? */
+ if (((flags & ETHARP_FLAG_FIND_ONLY) != 0) ||
+ /* or no empty entry found and not allowed to recycle? */
+ ((empty == ARP_TABLE_SIZE) && ((flags & ETHARP_FLAG_TRY_HARD) == 0))) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: no empty entry found and not allowed to recycle\n"));
+ return (s16_t)ERR_MEM;
+ }
+
+ /* b) choose the least destructive entry to recycle:
+ * 1) empty entry
+ * 2) oldest stable entry
+ * 3) oldest pending entry without queued packets
+ * 4) oldest pending entry with queued packets
+ *
+ * { ETHARP_FLAG_TRY_HARD is set at this point }
+ */
+
+ /* 1) empty entry available? */
+ if (empty < ARP_TABLE_SIZE) {
+ i = empty;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting empty entry %d\n", (int)i));
+ } else {
+ /* 2) found recyclable stable entry? */
+ if (old_stable < ARP_TABLE_SIZE) {
+ /* recycle oldest stable*/
+ i = old_stable;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest stable entry %d\n", (int)i));
+ /* no queued packets should exist on stable entries */
+ LWIP_ASSERT("arp_table[i].q == NULL", arp_table[i].q == NULL);
+ /* 3) found recyclable pending entry without queued packets? */
+ } else if (old_pending < ARP_TABLE_SIZE) {
+ /* recycle oldest pending */
+ i = old_pending;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest pending entry %d (without queue)\n", (int)i));
+ /* 4) found recyclable pending entry with queued packets? */
+ } else if (old_queue < ARP_TABLE_SIZE) {
+ /* recycle oldest pending (queued packets are free in etharp_free_entry) */
+ i = old_queue;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest pending entry %d, freeing packet queue %p\n", (int)i, (void *)(arp_table[i].q)));
+ /* no empty or recyclable entries found */
+ } else {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: no empty or recyclable entries found\n"));
+ return (s16_t)ERR_MEM;
+ }
+
+ /* { empty or recyclable entry found } */
+ LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE);
+ etharp_free_entry(i);
+ }
+
+ LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE);
+ LWIP_ASSERT("arp_table[i].state == ETHARP_STATE_EMPTY",
+ arp_table[i].state == ETHARP_STATE_EMPTY);
+
+ /* IP address given? */
+ if (ipaddr != NULL) {
+ /* set IP address */
+ ip4_addr_copy(arp_table[i].ipaddr, *ipaddr);
+ }
+ arp_table[i].ctime = 0;
+#if ETHARP_TABLE_MATCH_NETIF
+ arp_table[i].netif = netif;
+#endif /* ETHARP_TABLE_MATCH_NETIF */
+ return (s16_t)i;
+}
+
+/**
+ * Update (or insert) a IP/MAC address pair in the ARP cache.
+ *
+ * If a pending entry is resolved, any queued packets will be sent
+ * at this point.
+ *
+ * @param netif netif related to this entry (used for NETIF_ADDRHINT)
+ * @param ipaddr IP address of the inserted ARP entry.
+ * @param ethaddr Ethernet address of the inserted ARP entry.
+ * @param flags See @ref etharp_state
+ *
+ * @return
+ * - ERR_OK Successfully updated ARP cache.
+ * - ERR_MEM If we could not add a new ARP entry when ETHARP_FLAG_TRY_HARD was set.
+ * - ERR_ARG Non-unicast address given, those will not appear in ARP cache.
+ *
+ * @see pbuf_free()
+ */
+static err_t
+etharp_update_arp_entry(struct netif *netif, const ip4_addr_t *ipaddr, struct eth_addr *ethaddr, u8_t flags)
+{
+ s16_t i;
+ LWIP_ASSERT("netif->hwaddr_len == ETH_HWADDR_LEN", netif->hwaddr_len == ETH_HWADDR_LEN);
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n",
+ ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr),
+ (u16_t)ethaddr->addr[0], (u16_t)ethaddr->addr[1], (u16_t)ethaddr->addr[2],
+ (u16_t)ethaddr->addr[3], (u16_t)ethaddr->addr[4], (u16_t)ethaddr->addr[5]));
+ /* non-unicast address? */
+ if (ip4_addr_isany(ipaddr) ||
+ ip4_addr_isbroadcast(ipaddr, netif) ||
+ ip4_addr_ismulticast(ipaddr)) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: will not add non-unicast IP address to ARP cache\n"));
+ return ERR_ARG;
+ }
+ /* find or create ARP entry */
+ i = etharp_find_entry(ipaddr, flags, netif);
+ /* bail out if no entry could be found */
+ if (i < 0) {
+ return (err_t)i;
+ }
+
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+ if (flags & ETHARP_FLAG_STATIC_ENTRY) {
+ /* record static type */
+ arp_table[i].state = ETHARP_STATE_STATIC;
+ } else if (arp_table[i].state == ETHARP_STATE_STATIC) {
+ /* found entry is a static type, don't overwrite it */
+ return ERR_VAL;
+ } else
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+ {
+ /* mark it stable */
+ arp_table[i].state = ETHARP_STATE_STABLE;
+ }
+
+ /* record network interface */
+ arp_table[i].netif = netif;
+ /* insert in SNMP ARP index tree */
+ mib2_add_arp_entry(netif, &arp_table[i].ipaddr);
+
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: updating stable entry %"S16_F"\n", i));
+ /* update address */
+ SMEMCPY(&arp_table[i].ethaddr, ethaddr, ETH_HWADDR_LEN);
+ /* reset time stamp */
+ arp_table[i].ctime = 0;
+ /* this is where we will send out queued packets! */
+#if ARP_QUEUEING
+ while (arp_table[i].q != NULL) {
+ struct pbuf *p;
+ /* remember remainder of queue */
+ struct etharp_q_entry *q = arp_table[i].q;
+ /* pop first item off the queue */
+ arp_table[i].q = q->next;
+ /* get the packet pointer */
+ p = q->p;
+ /* now queue entry can be freed */
+ memp_free(MEMP_ARP_QUEUE, q);
+#else /* ARP_QUEUEING */
+ if (arp_table[i].q != NULL) {
+ struct pbuf *p = arp_table[i].q;
+ arp_table[i].q = NULL;
+#endif /* ARP_QUEUEING */
+ /* send the queued IP packet */
+ ethernet_output(netif, p, (struct eth_addr *)(netif->hwaddr), ethaddr, ETHTYPE_IP);
+ /* free the queued IP packet */
+ pbuf_free(p);
+ }
+ return ERR_OK;
+}
+
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+/** Add a new static entry to the ARP table. If an entry exists for the
+ * specified IP address, this entry is overwritten.
+ * If packets are queued for the specified IP address, they are sent out.
+ *
+ * @param ipaddr IP address for the new static entry
+ * @param ethaddr ethernet address for the new static entry
+ * @return See return values of etharp_add_static_entry
+ */
+err_t
+etharp_add_static_entry(const ip4_addr_t *ipaddr, struct eth_addr *ethaddr)
+{
+ struct netif *netif;
+ LWIP_ASSERT_CORE_LOCKED();
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_add_static_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n",
+ ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr),
+ (u16_t)ethaddr->addr[0], (u16_t)ethaddr->addr[1], (u16_t)ethaddr->addr[2],
+ (u16_t)ethaddr->addr[3], (u16_t)ethaddr->addr[4], (u16_t)ethaddr->addr[5]));
+
+ netif = ip4_route(ipaddr);
+ if (netif == NULL) {
+ return ERR_RTE;
+ }
+
+ return etharp_update_arp_entry(netif, ipaddr, ethaddr, ETHARP_FLAG_TRY_HARD | ETHARP_FLAG_STATIC_ENTRY);
+}
+
+/** Remove a static entry from the ARP table previously added with a call to
+ * etharp_add_static_entry.
+ *
+ * @param ipaddr IP address of the static entry to remove
+ * @return ERR_OK: entry removed
+ * ERR_MEM: entry wasn't found
+ * ERR_ARG: entry wasn't a static entry but a dynamic one
+ */
+err_t
+etharp_remove_static_entry(const ip4_addr_t *ipaddr)
+{
+ s16_t i;
+ LWIP_ASSERT_CORE_LOCKED();
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_remove_static_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr)));
+
+ /* find or create ARP entry */
+ i = etharp_find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY, NULL);
+ /* bail out if no entry could be found */
+ if (i < 0) {
+ return (err_t)i;
+ }
+
+ if (arp_table[i].state != ETHARP_STATE_STATIC) {
+ /* entry wasn't a static entry, cannot remove it */
+ return ERR_ARG;
+ }
+ /* entry found, free it */
+ etharp_free_entry(i);
+ return ERR_OK;
+}
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+
+/**
+ * Remove all ARP table entries of the specified netif.
+ *
+ * @param netif points to a network interface
+ */
+void
+etharp_cleanup_netif(struct netif *netif)
+{
+ int i;
+
+ for (i = 0; i < ARP_TABLE_SIZE; ++i) {
+ u8_t state = arp_table[i].state;
+ if ((state != ETHARP_STATE_EMPTY) && (arp_table[i].netif == netif)) {
+ etharp_free_entry(i);
+ }
+ }
+}
+
+/**
+ * Finds (stable) ethernet/IP address pair from ARP table
+ * using interface and IP address index.
+ * @note the addresses in the ARP table are in network order!
+ *
+ * @param netif points to interface index
+ * @param ipaddr points to the (network order) IP address index
+ * @param eth_ret points to return pointer
+ * @param ip_ret points to return pointer
+ * @return table index if found, -1 otherwise
+ */
+ssize_t
+etharp_find_addr(struct netif *netif, const ip4_addr_t *ipaddr,
+ struct eth_addr **eth_ret, const ip4_addr_t **ip_ret)
+{
+ s16_t i;
+
+ LWIP_ASSERT("eth_ret != NULL && ip_ret != NULL",
+ eth_ret != NULL && ip_ret != NULL);
+
+ LWIP_UNUSED_ARG(netif);
+
+ i = etharp_find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY, netif);
+ if ((i >= 0) && (arp_table[i].state >= ETHARP_STATE_STABLE)) {
+ *eth_ret = &arp_table[i].ethaddr;
+ *ip_ret = &arp_table[i].ipaddr;
+ return i;
+ }
+ return -1;
+}
+
+/**
+ * Possibility to iterate over stable ARP table entries
+ *
+ * @param i entry number, 0 to ARP_TABLE_SIZE
+ * @param ipaddr return value: IP address
+ * @param netif return value: points to interface
+ * @param eth_ret return value: ETH address
+ * @return 1 on valid index, 0 otherwise
+ */
+int
+etharp_get_entry(size_t i, ip4_addr_t **ipaddr, struct netif **netif, struct eth_addr **eth_ret)
+{
+ LWIP_ASSERT("ipaddr != NULL", ipaddr != NULL);
+ LWIP_ASSERT("netif != NULL", netif != NULL);
+ LWIP_ASSERT("eth_ret != NULL", eth_ret != NULL);
+
+ if ((i < ARP_TABLE_SIZE) && (arp_table[i].state >= ETHARP_STATE_STABLE)) {
+ *ipaddr = &arp_table[i].ipaddr;
+ *netif = arp_table[i].netif;
+ *eth_ret = &arp_table[i].ethaddr;
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/**
+ * Responds to ARP requests to us. Upon ARP replies to us, add entry to cache
+ * send out queued IP packets. Updates cache with snooped address pairs.
+ *
+ * Should be called for incoming ARP packets. The pbuf in the argument
+ * is freed by this function.
+ *
+ * @param p The ARP packet that arrived on netif. Is freed by this function.
+ * @param netif The lwIP network interface on which the ARP packet pbuf arrived.
+ *
+ * @see pbuf_free()
+ */
+void
+etharp_input(struct pbuf *p, struct netif *netif)
+{
+ struct etharp_hdr *hdr;
+ /* these are aligned properly, whereas the ARP header fields might not be */
+ ip4_addr_t sipaddr, dipaddr;
+ u8_t for_us;
+
+ LWIP_ASSERT_CORE_LOCKED();
+
+ LWIP_ERROR("netif != NULL", (netif != NULL), return;);
+
+ hdr = (struct etharp_hdr *)p->payload;
+
+ /* RFC 826 "Packet Reception": */
+ if ((hdr->hwtype != PP_HTONS(LWIP_IANA_HWTYPE_ETHERNET)) ||
+ (hdr->hwlen != ETH_HWADDR_LEN) ||
+ (hdr->protolen != sizeof(ip4_addr_t)) ||
+ (hdr->proto != PP_HTONS(ETHTYPE_IP))) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("etharp_input: packet dropped, wrong hw type, hwlen, proto, protolen or ethernet type (%"U16_F"/%"U16_F"/%"U16_F"/%"U16_F")\n",
+ hdr->hwtype, (u16_t)hdr->hwlen, hdr->proto, (u16_t)hdr->protolen));
+ ETHARP_STATS_INC(etharp.proterr);
+ ETHARP_STATS_INC(etharp.drop);
+ pbuf_free(p);
+ return;
+ }
+ ETHARP_STATS_INC(etharp.recv);
+
+#if LWIP_AUTOIP
+ /* We have to check if a host already has configured our random
+ * created link local address and continuously check if there is
+ * a host with this IP-address so we can detect collisions */
+ autoip_arp_reply(netif, hdr);
+#endif /* LWIP_AUTOIP */
+
+ /* Copy struct ip4_addr_wordaligned to aligned ip4_addr, to support compilers without
+ * structure packing (not using structure copy which breaks strict-aliasing rules). */
+ IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T(&sipaddr, &hdr->sipaddr);
+ IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T(&dipaddr, &hdr->dipaddr);
+
+ /* this interface is not configured? */
+ if (ip4_addr_isany_val(*netif_ip4_addr(netif))) {
+ for_us = 0;
+ } else {
+ /* ARP packet directed to us? */
+ for_us = (u8_t)ip4_addr_cmp(&dipaddr, netif_ip4_addr(netif));
+ }
+
+ /* ARP message directed to us?
+ -> add IP address in ARP cache; assume requester wants to talk to us,
+ can result in directly sending the queued packets for this host.
+ ARP message not directed to us?
+ -> update the source IP address in the cache, if present */
+ etharp_update_arp_entry(netif, &sipaddr, &(hdr->shwaddr),
+ for_us ? ETHARP_FLAG_TRY_HARD : ETHARP_FLAG_FIND_ONLY);
+
+ /* now act on the message itself */
+ switch (hdr->opcode) {
+ /* ARP request? */
+ case PP_HTONS(ARP_REQUEST):
+ /* ARP request. If it asked for our address, we send out a
+ * reply. In any case, we time-stamp any existing ARP entry,
+ * and possibly send out an IP packet that was queued on it. */
+ LWIP_DEBUGF (ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: incoming ARP request\n"));
+ /* ARP request for our address? */
+ if (for_us) {
+ /* send ARP response */
+ etharp_raw(netif,
+ (struct eth_addr *)netif->hwaddr, &hdr->shwaddr,
+ (struct eth_addr *)netif->hwaddr, netif_ip4_addr(netif),
+ &hdr->shwaddr, &sipaddr,
+ ARP_REPLY);
+
+ /* we are not configured? */
+ } else if (ip4_addr_isany_val(*netif_ip4_addr(netif))) {
+ /* { for_us == 0 and netif->ip_addr.addr == 0 } */
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: we are unconfigured, ARP request ignored.\n"));
+ /* request was not directed to us */
+ } else {
+ /* { for_us == 0 and netif->ip_addr.addr != 0 } */
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: ARP request was not for us.\n"));
+ }
+ break;
+ case PP_HTONS(ARP_REPLY):
+ /* ARP reply. We already updated the ARP cache earlier. */
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: incoming ARP reply\n"));
+#if (LWIP_DHCP && DHCP_DOES_ARP_CHECK)
+ /* DHCP wants to know about ARP replies from any host with an
+ * IP address also offered to us by the DHCP server. We do not
+ * want to take a duplicate IP address on a single network.
+ * @todo How should we handle redundant (fail-over) interfaces? */
+ dhcp_arp_reply(netif, &sipaddr);
+#endif /* (LWIP_DHCP && DHCP_DOES_ARP_CHECK) */
+ break;
+ default:
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: ARP unknown opcode type %"S16_F"\n", lwip_htons(hdr->opcode)));
+ ETHARP_STATS_INC(etharp.err);
+ break;
+ }
+ /* free ARP packet */
+ pbuf_free(p);
+}
+
+/** Just a small helper function that sends a pbuf to an ethernet address
+ * in the arp_table specified by the index 'arp_idx'.
+ */
+static err_t
+etharp_output_to_arp_index(struct netif *netif, struct pbuf *q, netif_addr_idx_t arp_idx)
+{
+ LWIP_ASSERT("arp_table[arp_idx].state >= ETHARP_STATE_STABLE",
+ arp_table[arp_idx].state >= ETHARP_STATE_STABLE);
+ /* if arp table entry is about to expire: re-request it,
+ but only if its state is ETHARP_STATE_STABLE to prevent flooding the
+ network with ARP requests if this address is used frequently. */
+ if (arp_table[arp_idx].state == ETHARP_STATE_STABLE) {
+ if (arp_table[arp_idx].ctime >= ARP_AGE_REREQUEST_USED_BROADCAST) {
+ /* issue a standard request using broadcast */
+ if (etharp_request(netif, &arp_table[arp_idx].ipaddr) == ERR_OK) {
+ arp_table[arp_idx].state = ETHARP_STATE_STABLE_REREQUESTING_1;
+ }
+ } else if (arp_table[arp_idx].ctime >= ARP_AGE_REREQUEST_USED_UNICAST) {
+ /* issue a unicast request (for 15 seconds) to prevent unnecessary broadcast */
+ if (etharp_request_dst(netif, &arp_table[arp_idx].ipaddr, &arp_table[arp_idx].ethaddr) == ERR_OK) {
+ arp_table[arp_idx].state = ETHARP_STATE_STABLE_REREQUESTING_1;
+ }
+ }
+ }
+
+ return ethernet_output(netif, q, (struct eth_addr *)(netif->hwaddr), &arp_table[arp_idx].ethaddr, ETHTYPE_IP);
+}
+
+/**
+ * Resolve and fill-in Ethernet address header for outgoing IP packet.
+ *
+ * For IP multicast and broadcast, corresponding Ethernet addresses
+ * are selected and the packet is transmitted on the link.
+ *
+ * For unicast addresses, the packet is submitted to etharp_query(). In
+ * case the IP address is outside the local network, the IP address of
+ * the gateway is used.
+ *
+ * @param netif The lwIP network interface which the IP packet will be sent on.
+ * @param q The pbuf(s) containing the IP packet to be sent.
+ * @param ipaddr The IP address of the packet destination.
+ *
+ * @return
+ * - ERR_RTE No route to destination (no gateway to external networks),
+ * or the return type of either etharp_query() or ethernet_output().
+ */
+err_t
+etharp_output(struct netif *netif, struct pbuf *q, const ip4_addr_t *ipaddr)
+{
+ const struct eth_addr *dest;
+ struct eth_addr mcastaddr;
+ const ip4_addr_t *dst_addr = ipaddr;
+
+ LWIP_ASSERT_CORE_LOCKED();
+ LWIP_ASSERT("netif != NULL", netif != NULL);
+ LWIP_ASSERT("q != NULL", q != NULL);
+ LWIP_ASSERT("ipaddr != NULL", ipaddr != NULL);
+
+ /* Determine on destination hardware address. Broadcasts and multicasts
+ * are special, other IP addresses are looked up in the ARP table. */
+
+ /* broadcast destination IP address? */
+ if (ip4_addr_isbroadcast(ipaddr, netif)) {
+ /* broadcast on Ethernet also */
+ dest = (const struct eth_addr *)ðbroadcast;
+ /* multicast destination IP address? */
+ } else if (ip4_addr_ismulticast(ipaddr)) {
+ /* Hash IP multicast address to MAC address.*/
+ mcastaddr.addr[0] = LL_IP4_MULTICAST_ADDR_0;
+ mcastaddr.addr[1] = LL_IP4_MULTICAST_ADDR_1;
+ mcastaddr.addr[2] = LL_IP4_MULTICAST_ADDR_2;
+ mcastaddr.addr[3] = ip4_addr2(ipaddr) & 0x7f;
+ mcastaddr.addr[4] = ip4_addr3(ipaddr);
+ mcastaddr.addr[5] = ip4_addr4(ipaddr);
+ /* destination Ethernet address is multicast */
+ dest = &mcastaddr;
+ /* unicast destination IP address? */
+ } else {
+ netif_addr_idx_t i;
+ /* outside local network? if so, this can neither be a global broadcast nor
+ a subnet broadcast. */
+ if (!ip4_addr_netcmp(ipaddr, netif_ip4_addr(netif), netif_ip4_netmask(netif)) &&
+ !ip4_addr_islinklocal(ipaddr)) {
+#if LWIP_AUTOIP
+ struct ip_hdr *iphdr = LWIP_ALIGNMENT_CAST(struct ip_hdr *, q->payload);
+ /* According to RFC 3297, chapter 2.6.2 (Forwarding Rules), a packet with
+ a link-local source address must always be "directly to its destination
+ on the same physical link. The host MUST NOT send the packet to any
+ router for forwarding". */
+ if (!ip4_addr_islinklocal(&iphdr->src))
+#endif /* LWIP_AUTOIP */
+ {
+#ifdef LWIP_HOOK_ETHARP_GET_GW
+ /* For advanced routing, a single default gateway might not be enough, so get
+ the IP address of the gateway to handle the current destination address. */
+ dst_addr = LWIP_HOOK_ETHARP_GET_GW(netif, ipaddr);
+ if (dst_addr == NULL)
+#endif /* LWIP_HOOK_ETHARP_GET_GW */
+ {
+ /* interface has default gateway? */
+ if (!ip4_addr_isany_val(*netif_ip4_gw(netif))) {
+ /* send to hardware address of default gateway IP address */
+ dst_addr = netif_ip4_gw(netif);
+ /* no default gateway available */
+ } else {
+ /* no route to destination error (default gateway missing) */
+ return ERR_RTE;
+ }
+ }
+ }
+ }
+#if LWIP_NETIF_HWADDRHINT
+ if (netif->hints != NULL) {
+ /* per-pcb cached entry was given */
+ netif_addr_idx_t etharp_cached_entry = netif->hints->addr_hint;
+ if (etharp_cached_entry < ARP_TABLE_SIZE) {
+#endif /* LWIP_NETIF_HWADDRHINT */
+ if ((arp_table[etharp_cached_entry].state >= ETHARP_STATE_STABLE) &&
+#if ETHARP_TABLE_MATCH_NETIF
+ (arp_table[etharp_cached_entry].netif == netif) &&
+#endif
+ (ip4_addr_cmp(dst_addr, &arp_table[etharp_cached_entry].ipaddr))) {
+ /* the per-pcb-cached entry is stable and the right one! */
+ ETHARP_STATS_INC(etharp.cachehit);
+ return etharp_output_to_arp_index(netif, q, etharp_cached_entry);
+ }
+#if LWIP_NETIF_HWADDRHINT
+ }
+ }
+#endif /* LWIP_NETIF_HWADDRHINT */
+
+ /* find stable entry: do this here since this is a critical path for
+ throughput and etharp_find_entry() is kind of slow */
+ for (i = 0; i < ARP_TABLE_SIZE; i++) {
+ if ((arp_table[i].state >= ETHARP_STATE_STABLE) &&
+#if ETHARP_TABLE_MATCH_NETIF
+ (arp_table[i].netif == netif) &&
+#endif
+ (ip4_addr_cmp(dst_addr, &arp_table[i].ipaddr))) {
+ /* found an existing, stable entry */
+ ETHARP_SET_ADDRHINT(netif, i);
+ return etharp_output_to_arp_index(netif, q, i);
+ }
+ }
+ /* no stable entry found, use the (slower) query function:
+ queue on destination Ethernet address belonging to ipaddr */
+ return etharp_query(netif, dst_addr, q);
+ }
+
+ /* continuation for multicast/broadcast destinations */
+ /* obtain source Ethernet address of the given interface */
+ /* send packet directly on the link */
+ return ethernet_output(netif, q, (struct eth_addr *)(netif->hwaddr), dest, ETHTYPE_IP);
+}
+
+/**
+ * Send an ARP request for the given IP address and/or queue a packet.
+ *
+ * If the IP address was not yet in the cache, a pending ARP cache entry
+ * is added and an ARP request is sent for the given address. The packet
+ * is queued on this entry.
+ *
+ * If the IP address was already pending in the cache, a new ARP request
+ * is sent for the given address. The packet is queued on this entry.
+ *
+ * If the IP address was already stable in the cache, and a packet is
+ * given, it is directly sent and no ARP request is sent out.
+ *
+ * If the IP address was already stable in the cache, and no packet is
+ * given, an ARP request is sent out.
+ *
+ * @param netif The lwIP network interface on which ipaddr
+ * must be queried for.
+ * @param ipaddr The IP address to be resolved.
+ * @param q If non-NULL, a pbuf that must be delivered to the IP address.
+ * q is not freed by this function.
+ *
+ * @note q must only be ONE packet, not a packet queue!
+ *
+ * @return
+ * - ERR_BUF Could not make room for Ethernet header.
+ * - ERR_MEM Hardware address unknown, and no more ARP entries available
+ * to query for address or queue the packet.
+ * - ERR_MEM Could not queue packet due to memory shortage.
+ * - ERR_RTE No route to destination (no gateway to external networks).
+ * - ERR_ARG Non-unicast address given, those will not appear in ARP cache.
+ *
+ */
+err_t
+etharp_query(struct netif *netif, const ip4_addr_t *ipaddr, struct pbuf *q)
+{
+ struct eth_addr *srcaddr = (struct eth_addr *)netif->hwaddr;
+ err_t result = ERR_MEM;
+ int is_new_entry = 0;
+ s16_t i_err;
+ netif_addr_idx_t i;
+
+ /* non-unicast address? */
+ if (ip4_addr_isbroadcast(ipaddr, netif) ||
+ ip4_addr_ismulticast(ipaddr) ||
+ ip4_addr_isany(ipaddr)) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: will not add non-unicast IP address to ARP cache\n"));
+ return ERR_ARG;
+ }
+
+ /* find entry in ARP cache, ask to create entry if queueing packet */
+ i_err = etharp_find_entry(ipaddr, ETHARP_FLAG_TRY_HARD, netif);
+
+ /* could not find or create entry? */
+ if (i_err < 0) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not create ARP entry\n"));
+ if (q) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: packet dropped\n"));
+ ETHARP_STATS_INC(etharp.memerr);
+ }
+ return (err_t)i_err;
+ }
+ LWIP_ASSERT("type overflow", (size_t)i_err < NETIF_ADDR_IDX_MAX);
+ i = (netif_addr_idx_t)i_err;
+
+ /* mark a fresh entry as pending (we just sent a request) */
+ if (arp_table[i].state == ETHARP_STATE_EMPTY) {
+ is_new_entry = 1;
+ arp_table[i].state = ETHARP_STATE_PENDING;
+ /* record network interface for re-sending arp request in etharp_tmr */
+ arp_table[i].netif = netif;
+ }
+
+ /* { i is either a STABLE or (new or existing) PENDING entry } */
+ LWIP_ASSERT("arp_table[i].state == PENDING or STABLE",
+ ((arp_table[i].state == ETHARP_STATE_PENDING) ||
+ (arp_table[i].state >= ETHARP_STATE_STABLE)));
+
+ /* do we have a new entry? or an implicit query request? */
+ if (is_new_entry || (q == NULL)) {
+ /* try to resolve it; send out ARP request */
+ result = etharp_request(netif, ipaddr);
+ if (result != ERR_OK) {
+ /* ARP request couldn't be sent */
+ /* We don't re-send arp request in etharp_tmr, but we still queue packets,
+ since this failure could be temporary, and the next packet calling
+ etharp_query again could lead to sending the queued packets. */
+ }
+ if (q == NULL) {
+ return result;
+ }
+ }
+
+ /* packet given? */
+ LWIP_ASSERT("q != NULL", q != NULL);
+ /* stable entry? */
+ if (arp_table[i].state >= ETHARP_STATE_STABLE) {
+ /* we have a valid IP->Ethernet address mapping */
+ ETHARP_SET_ADDRHINT(netif, i);
+ /* send the packet */
+ result = ethernet_output(netif, q, srcaddr, &(arp_table[i].ethaddr), ETHTYPE_IP);
+ /* pending entry? (either just created or already pending */
+ } else if (arp_table[i].state == ETHARP_STATE_PENDING) {
+ /* entry is still pending, queue the given packet 'q' */
+ struct pbuf *p;
+ int copy_needed = 0;
+ /* IF q includes a pbuf that must be copied, copy the whole chain into a
+ * new PBUF_RAM. See the definition of PBUF_NEEDS_COPY for details. */
+ p = q;
+ while (p) {
+ LWIP_ASSERT("no packet queues allowed!", (p->len != p->tot_len) || (p->next == 0));
+ if (PBUF_NEEDS_COPY(p)) {
+ copy_needed = 1;
+ break;
+ }
+ p = p->next;
+ }
+ if (copy_needed) {
+ /* copy the whole packet into new pbufs */
+ p = pbuf_clone(PBUF_LINK, PBUF_RAM, q);
+ } else {
+ /* referencing the old pbuf is enough */
+ p = q;
+ pbuf_ref(p);
+ }
+ /* packet could be taken over? */
+ if (p != NULL) {
+ /* queue packet ... */
+#if ARP_QUEUEING
+ struct etharp_q_entry *new_entry;
+ /* allocate a new arp queue entry */
+ new_entry = (struct etharp_q_entry *)memp_malloc(MEMP_ARP_QUEUE);
+ if (new_entry != NULL) {
+ unsigned int qlen = 0;
+ new_entry->next = 0;
+ new_entry->p = p;
+ if (arp_table[i].q != NULL) {
+ /* queue was already existent, append the new entry to the end */
+ struct etharp_q_entry *r;
+ r = arp_table[i].q;
+ qlen++;
+ while (r->next != NULL) {
+ r = r->next;
+ qlen++;
+ }
+ r->next = new_entry;
+ } else {
+ /* queue did not exist, first item in queue */
+ arp_table[i].q = new_entry;
+ }
+#if ARP_QUEUE_LEN
+ if (qlen >= ARP_QUEUE_LEN) {
+ struct etharp_q_entry *old;
+ old = arp_table[i].q;
+ arp_table[i].q = arp_table[i].q->next;
+ pbuf_free(old->p);
+ memp_free(MEMP_ARP_QUEUE, old);
+ }
+#endif
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"U16_F"\n", (void *)q, i));
+ result = ERR_OK;
+ } else {
+ /* the pool MEMP_ARP_QUEUE is empty */
+ pbuf_free(p);
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q));
+ result = ERR_MEM;
+ }
+#else /* ARP_QUEUEING */
+ /* always queue one packet per ARP request only, freeing a previously queued packet */
+ if (arp_table[i].q != NULL) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: dropped previously queued packet %p for ARP entry %"U16_F"\n", (void *)q, (u16_t)i));
+ pbuf_free(arp_table[i].q);
+ }
+ arp_table[i].q = p;
+ result = ERR_OK;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"U16_F"\n", (void *)q, (u16_t)i));
+#endif /* ARP_QUEUEING */
+ } else {
+ ETHARP_STATS_INC(etharp.memerr);
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q));
+ result = ERR_MEM;
+ }
+ }
+ return result;
+}
+
+/**
+ * Send a raw ARP packet (opcode and all addresses can be modified)
+ *
+ * @param netif the lwip network interface on which to send the ARP packet
+ * @param ethsrc_addr the source MAC address for the ethernet header
+ * @param ethdst_addr the destination MAC address for the ethernet header
+ * @param hwsrc_addr the source MAC address for the ARP protocol header
+ * @param ipsrc_addr the source IP address for the ARP protocol header
+ * @param hwdst_addr the destination MAC address for the ARP protocol header
+ * @param ipdst_addr the destination IP address for the ARP protocol header
+ * @param opcode the type of the ARP packet
+ * @return ERR_OK if the ARP packet has been sent
+ * ERR_MEM if the ARP packet couldn't be allocated
+ * any other err_t on failure
+ */
+static err_t
+etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr,
+ const struct eth_addr *ethdst_addr,
+ const struct eth_addr *hwsrc_addr, const ip4_addr_t *ipsrc_addr,
+ const struct eth_addr *hwdst_addr, const ip4_addr_t *ipdst_addr,
+ const u16_t opcode)
+{
+ struct pbuf *p;
+ err_t result = ERR_OK;
+ struct etharp_hdr *hdr;
+
+ LWIP_ASSERT("netif != NULL", netif != NULL);
+
+ /* allocate a pbuf for the outgoing ARP request packet */
+ p = pbuf_alloc(PBUF_LINK, SIZEOF_ETHARP_HDR, PBUF_RAM);
+ /* could allocate a pbuf for an ARP request? */
+ if (p == NULL) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("etharp_raw: could not allocate pbuf for ARP request.\n"));
+ ETHARP_STATS_INC(etharp.memerr);
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("check that first pbuf can hold struct etharp_hdr",
+ (p->len >= SIZEOF_ETHARP_HDR));
+
+ hdr = (struct etharp_hdr *)p->payload;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_raw: sending raw ARP packet.\n"));
+ hdr->opcode = lwip_htons(opcode);
+
+ LWIP_ASSERT("netif->hwaddr_len must be the same as ETH_HWADDR_LEN for etharp!",
+ (netif->hwaddr_len == ETH_HWADDR_LEN));
+
+ /* Write the ARP MAC-Addresses */
+ SMEMCPY(&hdr->shwaddr, hwsrc_addr, ETH_HWADDR_LEN);
+ SMEMCPY(&hdr->dhwaddr, hwdst_addr, ETH_HWADDR_LEN);
+ /* Copy struct ip4_addr_wordaligned to aligned ip4_addr, to support compilers without
+ * structure packing. */
+ IPADDR_WORDALIGNED_COPY_FROM_IP4_ADDR_T(&hdr->sipaddr, ipsrc_addr);
+ IPADDR_WORDALIGNED_COPY_FROM_IP4_ADDR_T(&hdr->dipaddr, ipdst_addr);
+
+ hdr->hwtype = PP_HTONS(LWIP_IANA_HWTYPE_ETHERNET);
+ hdr->proto = PP_HTONS(ETHTYPE_IP);
+ /* set hwlen and protolen */
+ hdr->hwlen = ETH_HWADDR_LEN;
+ hdr->protolen = sizeof(ip4_addr_t);
+
+ /* send ARP query */
+#if LWIP_AUTOIP
+ /* If we are using Link-Local, all ARP packets that contain a Link-Local
+ * 'sender IP address' MUST be sent using link-layer broadcast instead of
+ * link-layer unicast. (See RFC3927 Section 2.5, last paragraph) */
+ if (ip4_addr_islinklocal(ipsrc_addr)) {
+ ethernet_output(netif, p, ethsrc_addr, ðbroadcast, ETHTYPE_ARP);
+ } else
+#endif /* LWIP_AUTOIP */
+ {
+ ethernet_output(netif, p, ethsrc_addr, ethdst_addr, ETHTYPE_ARP);
+ }
+
+ ETHARP_STATS_INC(etharp.xmit);
+ /* free ARP query packet */
+ pbuf_free(p);
+ p = NULL;
+ /* could not allocate pbuf for ARP request */
+
+ return result;
+}
+
+/**
+ * Send an ARP request packet asking for ipaddr to a specific eth address.
+ * Used to send unicast request to refresh the ARP table just before an entry
+ * times out
+ *
+ * @param netif the lwip network interface on which to send the request
+ * @param ipaddr the IP address for which to ask
+ * @param hw_dst_addr the ethernet address to send this packet to
+ * @return ERR_OK if the request has been sent
+ * ERR_MEM if the ARP packet couldn't be allocated
+ * any other err_t on failure
+ */
+static err_t
+etharp_request_dst(struct netif *netif, const ip4_addr_t *ipaddr, const struct eth_addr *hw_dst_addr)
+{
+ return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, hw_dst_addr,
+ (struct eth_addr *)netif->hwaddr, netif_ip4_addr(netif), ðzero,
+ ipaddr, ARP_REQUEST);
+}
+
+/**
+ * Send an ARP request packet asking for ipaddr.
+ *
+ * @param netif the lwip network interface on which to send the request
+ * @param ipaddr the IP address for which to ask
+ * @return ERR_OK if the request has been sent
+ * ERR_MEM if the ARP packet couldn't be allocated
+ * any other err_t on failure
+ */
+err_t
+etharp_request(struct netif *netif, const ip4_addr_t *ipaddr)
+{
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_request: sending ARP request.\n"));
+ return etharp_request_dst(netif, ipaddr, ðbroadcast);
+}
+
+#endif /* LWIP_IPV4 && LWIP_ARP */
diff --git a/resources/ethernet/LwIP/core/ipv4/icmp.c b/resources/ethernet/LwIP/core/ipv4/icmp.c
new file mode 100644
index 00000000..fd9054b6
--- /dev/null
+++ b/resources/ethernet/LwIP/core/ipv4/icmp.c
@@ -0,0 +1,404 @@
+/**
+ * @file
+ * ICMP - Internet Control Message Protocol
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ */
+
+/* Some ICMP messages should be passed to the transport protocols. This
+ is not implemented. */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV4 && LWIP_ICMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/icmp.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/ip.h"
+#include "lwip/def.h"
+#include "lwip/stats.h"
+
+#include
+
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+
+/** Small optimization: set to 0 if incoming PBUF_POOL pbuf always can be
+ * used to modify and send a response packet (and to 1 if this is not the case,
+ * e.g. when link header is stripped off when receiving) */
+#ifndef LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
+#define LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN 1
+#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */
+
+/* The amount of data from the original packet to return in a dest-unreachable */
+#define ICMP_DEST_UNREACH_DATASIZE 8
+
+static void icmp_send_response(struct pbuf *p, u8_t type, u8_t code);
+
+/**
+ * Processes ICMP input packets, called from ip_input().
+ *
+ * Currently only processes icmp echo requests and sends
+ * out the echo response.
+ *
+ * @param p the icmp echo request packet, p->payload pointing to the icmp header
+ * @param inp the netif on which this packet was received
+ */
+void
+icmp_input(struct pbuf *p, struct netif *inp)
+{
+ u8_t type;
+#ifdef LWIP_DEBUG
+ u8_t code;
+#endif /* LWIP_DEBUG */
+ struct icmp_echo_hdr *iecho;
+ const struct ip_hdr *iphdr_in;
+ u16_t hlen;
+ const ip4_addr_t *src;
+
+ ICMP_STATS_INC(icmp.recv);
+ MIB2_STATS_INC(mib2.icmpinmsgs);
+
+ iphdr_in = ip4_current_header();
+ hlen = IPH_HL_BYTES(iphdr_in);
+ if (hlen < IP_HLEN) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: short IP header (%"S16_F" bytes) received\n", hlen));
+ goto lenerr;
+ }
+ if (p->len < sizeof(u16_t) * 2) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: short ICMP (%"U16_F" bytes) received\n", p->tot_len));
+ goto lenerr;
+ }
+
+ type = *((u8_t *)p->payload);
+#ifdef LWIP_DEBUG
+ code = *(((u8_t *)p->payload) + 1);
+ /* if debug is enabled but debug statement below is somehow disabled: */
+ LWIP_UNUSED_ARG(code);
+#endif /* LWIP_DEBUG */
+ switch (type) {
+ case ICMP_ER:
+ /* This is OK, echo reply might have been parsed by a raw PCB
+ (as obviously, an echo request has been sent, too). */
+ MIB2_STATS_INC(mib2.icmpinechoreps);
+ break;
+ case ICMP_ECHO:
+ MIB2_STATS_INC(mib2.icmpinechos);
+ src = ip4_current_dest_addr();
+ /* multicast destination address? */
+ if (ip4_addr_ismulticast(ip4_current_dest_addr())) {
+#if LWIP_MULTICAST_PING
+ /* For multicast, use address of receiving interface as source address */
+ src = netif_ip4_addr(inp);
+#else /* LWIP_MULTICAST_PING */
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: Not echoing to multicast pings\n"));
+ goto icmperr;
+#endif /* LWIP_MULTICAST_PING */
+ }
+ /* broadcast destination address? */
+ if (ip4_addr_isbroadcast(ip4_current_dest_addr(), ip_current_netif())) {
+#if LWIP_BROADCAST_PING
+ /* For broadcast, use address of receiving interface as source address */
+ src = netif_ip4_addr(inp);
+#else /* LWIP_BROADCAST_PING */
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: Not echoing to broadcast pings\n"));
+ goto icmperr;
+#endif /* LWIP_BROADCAST_PING */
+ }
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ping\n"));
+ if (p->tot_len < sizeof(struct icmp_echo_hdr)) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: bad ICMP echo received\n"));
+ goto lenerr;
+ }
+#if CHECKSUM_CHECK_ICMP
+ IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_ICMP) {
+ if (inet_chksum_pbuf(p) != 0) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo\n"));
+ pbuf_free(p);
+ ICMP_STATS_INC(icmp.chkerr);
+ MIB2_STATS_INC(mib2.icmpinerrors);
+ return;
+ }
+ }
+#endif
+#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
+ if (pbuf_add_header(p, hlen + PBUF_LINK_HLEN + PBUF_LINK_ENCAPSULATION_HLEN)) {
+ /* p is not big enough to contain link headers
+ * allocate a new one and copy p into it
+ */
+ struct pbuf *r;
+ u16_t alloc_len = (u16_t)(p->tot_len + hlen);
+ if (alloc_len < p->tot_len) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: allocating new pbuf failed (tot_len overflow)\n"));
+ goto icmperr;
+ }
+ /* allocate new packet buffer with space for link headers */
+ r = pbuf_alloc(PBUF_LINK, alloc_len, PBUF_RAM);
+ if (r == NULL) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: allocating new pbuf failed\n"));
+ goto icmperr;
+ }
+ if (r->len < hlen + sizeof(struct icmp_echo_hdr)) {
+ LWIP_DEBUGF(ICMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("first pbuf cannot hold the ICMP header"));
+ pbuf_free(r);
+ goto icmperr;
+ }
+ /* copy the ip header */
+ MEMCPY(r->payload, iphdr_in, hlen);
+ /* switch r->payload back to icmp header (cannot fail) */
+ if (pbuf_remove_header(r, hlen)) {
+ LWIP_ASSERT("icmp_input: moving r->payload to icmp header failed\n", 0);
+ pbuf_free(r);
+ goto icmperr;
+ }
+ /* copy the rest of the packet without ip header */
+ if (pbuf_copy(r, p) != ERR_OK) {
+ LWIP_DEBUGF(ICMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("icmp_input: copying to new pbuf failed"));
+ pbuf_free(r);
+ goto icmperr;
+ }
+ /* free the original p */
+ pbuf_free(p);
+ /* we now have an identical copy of p that has room for link headers */
+ p = r;
+ } else {
+ /* restore p->payload to point to icmp header (cannot fail) */
+ if (pbuf_remove_header(p, hlen + PBUF_LINK_HLEN + PBUF_LINK_ENCAPSULATION_HLEN)) {
+ LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0);
+ goto icmperr;
+ }
+ }
+#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */
+ /* At this point, all checks are OK. */
+ /* We generate an answer by switching the dest and src ip addresses,
+ * setting the icmp type to ECHO_RESPONSE and updating the checksum. */
+ iecho = (struct icmp_echo_hdr *)p->payload;
+ if (pbuf_add_header(p, hlen)) {
+ LWIP_DEBUGF(ICMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("Can't move over header in packet"));
+ } else {
+ err_t ret;
+ struct ip_hdr *iphdr = (struct ip_hdr *)p->payload;
+ ip4_addr_copy(iphdr->src, *src);
+ ip4_addr_copy(iphdr->dest, *ip4_current_src_addr());
+ ICMPH_TYPE_SET(iecho, ICMP_ER);
+#if CHECKSUM_GEN_ICMP
+ IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_GEN_ICMP) {
+ /* adjust the checksum */
+ if (iecho->chksum > PP_HTONS(0xffffU - (ICMP_ECHO << 8))) {
+ iecho->chksum = (u16_t)(iecho->chksum + PP_HTONS((u16_t)(ICMP_ECHO << 8)) + 1);
+ } else {
+ iecho->chksum = (u16_t)(iecho->chksum + PP_HTONS(ICMP_ECHO << 8));
+ }
+ }
+#if LWIP_CHECKSUM_CTRL_PER_NETIF
+ else {
+ iecho->chksum = 0;
+ }
+#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF */
+#else /* CHECKSUM_GEN_ICMP */
+ iecho->chksum = 0;
+#endif /* CHECKSUM_GEN_ICMP */
+
+ /* Set the correct TTL and recalculate the header checksum. */
+ IPH_TTL_SET(iphdr, ICMP_TTL);
+ IPH_CHKSUM_SET(iphdr, 0);
+#if CHECKSUM_GEN_IP
+ IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_GEN_IP) {
+ IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, hlen));
+ }
+#endif /* CHECKSUM_GEN_IP */
+
+ ICMP_STATS_INC(icmp.xmit);
+ /* increase number of messages attempted to send */
+ MIB2_STATS_INC(mib2.icmpoutmsgs);
+ /* increase number of echo replies attempted to send */
+ MIB2_STATS_INC(mib2.icmpoutechoreps);
+
+ /* send an ICMP packet */
+ ret = ip4_output_if(p, src, LWIP_IP_HDRINCL,
+ ICMP_TTL, 0, IP_PROTO_ICMP, inp);
+ if (ret != ERR_OK) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ip_output_if returned an error: %s\n", lwip_strerr(ret)));
+ }
+ }
+ break;
+ default:
+ if (type == ICMP_DUR) {
+ MIB2_STATS_INC(mib2.icmpindestunreachs);
+ } else if (type == ICMP_TE) {
+ MIB2_STATS_INC(mib2.icmpintimeexcds);
+ } else if (type == ICMP_PP) {
+ MIB2_STATS_INC(mib2.icmpinparmprobs);
+ } else if (type == ICMP_SQ) {
+ MIB2_STATS_INC(mib2.icmpinsrcquenchs);
+ } else if (type == ICMP_RD) {
+ MIB2_STATS_INC(mib2.icmpinredirects);
+ } else if (type == ICMP_TS) {
+ MIB2_STATS_INC(mib2.icmpintimestamps);
+ } else if (type == ICMP_TSR) {
+ MIB2_STATS_INC(mib2.icmpintimestampreps);
+ } else if (type == ICMP_AM) {
+ MIB2_STATS_INC(mib2.icmpinaddrmasks);
+ } else if (type == ICMP_AMR) {
+ MIB2_STATS_INC(mib2.icmpinaddrmaskreps);
+ }
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ICMP type %"S16_F" code %"S16_F" not supported.\n",
+ (s16_t)type, (s16_t)code));
+ ICMP_STATS_INC(icmp.proterr);
+ ICMP_STATS_INC(icmp.drop);
+ }
+ pbuf_free(p);
+ return;
+lenerr:
+ pbuf_free(p);
+ ICMP_STATS_INC(icmp.lenerr);
+ MIB2_STATS_INC(mib2.icmpinerrors);
+ return;
+#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN || !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING
+icmperr:
+ pbuf_free(p);
+ ICMP_STATS_INC(icmp.err);
+ MIB2_STATS_INC(mib2.icmpinerrors);
+ return;
+#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN || !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING */
+}
+
+/**
+ * Send an icmp 'destination unreachable' packet, called from ip_input() if
+ * the transport layer protocol is unknown and from udp_input() if the local
+ * port is not bound.
+ *
+ * @param p the input packet for which the 'unreachable' should be sent,
+ * p->payload pointing to the IP header
+ * @param t type of the 'unreachable' packet
+ */
+void
+icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t)
+{
+ MIB2_STATS_INC(mib2.icmpoutdestunreachs);
+ icmp_send_response(p, ICMP_DUR, t);
+}
+
+#if IP_FORWARD || IP_REASSEMBLY
+/**
+ * Send a 'time exceeded' packet, called from ip_forward() if TTL is 0.
+ *
+ * @param p the input packet for which the 'time exceeded' should be sent,
+ * p->payload pointing to the IP header
+ * @param t type of the 'time exceeded' packet
+ */
+void
+icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t)
+{
+ MIB2_STATS_INC(mib2.icmpouttimeexcds);
+ icmp_send_response(p, ICMP_TE, t);
+}
+
+#endif /* IP_FORWARD || IP_REASSEMBLY */
+
+/**
+ * Send an icmp packet in response to an incoming packet.
+ *
+ * @param p the input packet for which the 'unreachable' should be sent,
+ * p->payload pointing to the IP header
+ * @param type Type of the ICMP header
+ * @param code Code of the ICMP header
+ */
+static void
+icmp_send_response(struct pbuf *p, u8_t type, u8_t code)
+{
+ struct pbuf *q;
+ struct ip_hdr *iphdr;
+ /* we can use the echo header here */
+ struct icmp_echo_hdr *icmphdr;
+ ip4_addr_t iphdr_src;
+ struct netif *netif;
+
+ /* increase number of messages attempted to send */
+ MIB2_STATS_INC(mib2.icmpoutmsgs);
+
+ /* ICMP header + IP header + 8 bytes of data */
+ q = pbuf_alloc(PBUF_IP, sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE,
+ PBUF_RAM);
+ if (q == NULL) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMP packet.\n"));
+ MIB2_STATS_INC(mib2.icmpouterrors);
+ return;
+ }
+ LWIP_ASSERT("check that first pbuf can hold icmp message",
+ (q->len >= (sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE)));
+
+ iphdr = (struct ip_hdr *)p->payload;
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded from "));
+ ip4_addr_debug_print_val(ICMP_DEBUG, iphdr->src);
+ LWIP_DEBUGF(ICMP_DEBUG, (" to "));
+ ip4_addr_debug_print_val(ICMP_DEBUG, iphdr->dest);
+ LWIP_DEBUGF(ICMP_DEBUG, ("\n"));
+
+ icmphdr = (struct icmp_echo_hdr *)q->payload;
+ icmphdr->type = type;
+ icmphdr->code = code;
+ icmphdr->id = 0;
+ icmphdr->seqno = 0;
+
+ /* copy fields from original packet */
+ SMEMCPY((u8_t *)q->payload + sizeof(struct icmp_echo_hdr), (u8_t *)p->payload,
+ IP_HLEN + ICMP_DEST_UNREACH_DATASIZE);
+
+ ip4_addr_copy(iphdr_src, iphdr->src);
+#ifdef LWIP_HOOK_IP4_ROUTE_SRC
+ {
+ ip4_addr_t iphdr_dst;
+ ip4_addr_copy(iphdr_dst, iphdr->dest);
+ netif = ip4_route_src(&iphdr_dst, &iphdr_src);
+ }
+#else
+ netif = ip4_route(&iphdr_src);
+#endif
+ if (netif != NULL) {
+ /* calculate checksum */
+ icmphdr->chksum = 0;
+#if CHECKSUM_GEN_ICMP
+ IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP) {
+ icmphdr->chksum = inet_chksum(icmphdr, q->len);
+ }
+#endif
+ ICMP_STATS_INC(icmp.xmit);
+ ip4_output_if(q, NULL, &iphdr_src, ICMP_TTL, 0, IP_PROTO_ICMP, netif);
+ }
+ pbuf_free(q);
+}
+
+#endif /* LWIP_IPV4 && LWIP_ICMP */
diff --git a/resources/ethernet/LwIP/core/ipv4/igmp.c b/resources/ethernet/LwIP/core/ipv4/igmp.c
new file mode 100644
index 00000000..61f45d95
--- /dev/null
+++ b/resources/ethernet/LwIP/core/ipv4/igmp.c
@@ -0,0 +1,801 @@
+/**
+ * @file
+ * IGMP - Internet Group Management Protocol
+ *
+ * @defgroup igmp IGMP
+ * @ingroup ip4
+ * To be called from TCPIP thread
+ */
+
+/*
+ * Copyright (c) 2002 CITEL Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of CITEL Technologies Ltd nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CITEL TECHNOLOGIES AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL CITEL TECHNOLOGIES OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This file is a contribution to the lwIP TCP/IP stack.
+ * The Swedish Institute of Computer Science and Adam Dunkels
+ * are specifically granted permission to redistribute this
+ * source code.
+*/
+
+/*-------------------------------------------------------------
+Note 1)
+Although the rfc requires V1 AND V2 capability
+we will only support v2 since now V1 is very old (August 1989)
+V1 can be added if required
+
+a debug print and statistic have been implemented to
+show this up.
+-------------------------------------------------------------
+-------------------------------------------------------------
+Note 2)
+A query for a specific group address (as opposed to ALLHOSTS)
+has now been implemented as I am unsure if it is required
+
+a debug print and statistic have been implemented to
+show this up.
+-------------------------------------------------------------
+-------------------------------------------------------------
+Note 3)
+The router alert rfc 2113 is implemented in outgoing packets
+but not checked rigorously incoming
+-------------------------------------------------------------
+Steve Reynolds
+------------------------------------------------------------*/
+
+/*-----------------------------------------------------------------------------
+ * RFC 988 - Host extensions for IP multicasting - V0
+ * RFC 1054 - Host extensions for IP multicasting -
+ * RFC 1112 - Host extensions for IP multicasting - V1
+ * RFC 2236 - Internet Group Management Protocol, Version 2 - V2 <- this code is based on this RFC (it's the "de facto" standard)
+ * RFC 3376 - Internet Group Management Protocol, Version 3 - V3
+ * RFC 4604 - Using Internet Group Management Protocol Version 3... - V3+
+ * RFC 2113 - IP Router Alert Option -
+ *----------------------------------------------------------------------------*/
+
+/*-----------------------------------------------------------------------------
+ * Includes
+ *----------------------------------------------------------------------------*/
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV4 && LWIP_IGMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/igmp.h"
+#include "lwip/debug.h"
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/ip.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/netif.h"
+#include "lwip/stats.h"
+#include "lwip/prot/igmp.h"
+
+#include
+
+static struct igmp_group *igmp_lookup_group(struct netif *ifp, const ip4_addr_t *addr);
+static err_t igmp_remove_group(struct netif *netif, struct igmp_group *group);
+static void igmp_timeout(struct netif *netif, struct igmp_group *group);
+static void igmp_start_timer(struct igmp_group *group, u8_t max_time);
+static void igmp_delaying_member(struct igmp_group *group, u8_t maxresp);
+static err_t igmp_ip_output_if(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, struct netif *netif);
+static void igmp_send(struct netif *netif, struct igmp_group *group, u8_t type);
+
+static ip4_addr_t allsystems;
+static ip4_addr_t allrouters;
+
+/**
+ * Initialize the IGMP module
+ */
+void
+igmp_init(void)
+{
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_init: initializing\n"));
+
+ IP4_ADDR(&allsystems, 224, 0, 0, 1);
+ IP4_ADDR(&allrouters, 224, 0, 0, 2);
+}
+
+/**
+ * Start IGMP processing on interface
+ *
+ * @param netif network interface on which start IGMP processing
+ */
+err_t
+igmp_start(struct netif *netif)
+{
+ struct igmp_group *group;
+
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: starting IGMP processing on if %p\n", (void *)netif));
+
+ group = igmp_lookup_group(netif, &allsystems);
+
+ if (group != NULL) {
+ group->group_state = IGMP_GROUP_IDLE_MEMBER;
+ group->use++;
+
+ /* Allow the igmp messages at the MAC level */
+ if (netif->igmp_mac_filter != NULL) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: igmp_mac_filter(ADD "));
+ ip4_addr_debug_print_val(IGMP_DEBUG, allsystems);
+ LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void *)netif));
+ netif->igmp_mac_filter(netif, &allsystems, NETIF_ADD_MAC_FILTER);
+ }
+
+ return ERR_OK;
+ }
+
+ return ERR_MEM;
+}
+
+/**
+ * Stop IGMP processing on interface
+ *
+ * @param netif network interface on which stop IGMP processing
+ */
+err_t
+igmp_stop(struct netif *netif)
+{
+ struct igmp_group *group = netif_igmp_data(netif);
+
+ netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_IGMP, NULL);
+
+ while (group != NULL) {
+ struct igmp_group *next = group->next; /* avoid use-after-free below */
+
+ /* disable the group at the MAC level */
+ if (netif->igmp_mac_filter != NULL) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_stop: igmp_mac_filter(DEL "));
+ ip4_addr_debug_print_val(IGMP_DEBUG, group->group_address);
+ LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void *)netif));
+ netif->igmp_mac_filter(netif, &(group->group_address), NETIF_DEL_MAC_FILTER);
+ }
+
+ /* free group */
+ memp_free(MEMP_IGMP_GROUP, group);
+
+ /* move to "next" */
+ group = next;
+ }
+ return ERR_OK;
+}
+
+/**
+ * Report IGMP memberships for this interface
+ *
+ * @param netif network interface on which report IGMP memberships
+ */
+void
+igmp_report_groups(struct netif *netif)
+{
+ struct igmp_group *group = netif_igmp_data(netif);
+
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_report_groups: sending IGMP reports on if %p\n", (void *)netif));
+
+ /* Skip the first group in the list, it is always the allsystems group added in igmp_start() */
+ if (group != NULL) {
+ group = group->next;
+ }
+
+ while (group != NULL) {
+ igmp_delaying_member(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
+ group = group->next;
+ }
+}
+
+/**
+ * Search for a group in the netif's igmp group list
+ *
+ * @param ifp the network interface for which to look
+ * @param addr the group ip address to search for
+ * @return a struct igmp_group* if the group has been found,
+ * NULL if the group wasn't found.
+ */
+struct igmp_group *
+igmp_lookfor_group(struct netif *ifp, const ip4_addr_t *addr)
+{
+ struct igmp_group *group = netif_igmp_data(ifp);
+
+ while (group != NULL) {
+ if (ip4_addr_cmp(&(group->group_address), addr)) {
+ return group;
+ }
+ group = group->next;
+ }
+
+ /* to be clearer, we return NULL here instead of
+ * 'group' (which is also NULL at this point).
+ */
+ return NULL;
+}
+
+/**
+ * Search for a specific igmp group and create a new one if not found-
+ *
+ * @param ifp the network interface for which to look
+ * @param addr the group ip address to search
+ * @return a struct igmp_group*,
+ * NULL on memory error.
+ */
+static struct igmp_group *
+igmp_lookup_group(struct netif *ifp, const ip4_addr_t *addr)
+{
+ struct igmp_group *group;
+ struct igmp_group *list_head = netif_igmp_data(ifp);
+
+ /* Search if the group already exists */
+ group = igmp_lookfor_group(ifp, addr);
+ if (group != NULL) {
+ /* Group already exists. */
+ return group;
+ }
+
+ /* Group doesn't exist yet, create a new one */
+ group = (struct igmp_group *)memp_malloc(MEMP_IGMP_GROUP);
+ if (group != NULL) {
+ ip4_addr_set(&(group->group_address), addr);
+ group->timer = 0; /* Not running */
+ group->group_state = IGMP_GROUP_NON_MEMBER;
+ group->last_reporter_flag = 0;
+ group->use = 0;
+
+ /* Ensure allsystems group is always first in list */
+ if (list_head == NULL) {
+ /* this is the first entry in linked list */
+ LWIP_ASSERT("igmp_lookup_group: first group must be allsystems",
+ (ip4_addr_cmp(addr, &allsystems) != 0));
+ group->next = NULL;
+ netif_set_client_data(ifp, LWIP_NETIF_CLIENT_DATA_INDEX_IGMP, group);
+ } else {
+ /* append _after_ first entry */
+ LWIP_ASSERT("igmp_lookup_group: all except first group must not be allsystems",
+ (ip4_addr_cmp(addr, &allsystems) == 0));
+ group->next = list_head->next;
+ list_head->next = group;
+ }
+ }
+
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_lookup_group: %sallocated a new group with address ", (group ? "" : "impossible to ")));
+ ip4_addr_debug_print(IGMP_DEBUG, addr);
+ LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", (void *)ifp));
+
+ return group;
+}
+
+/**
+ * Remove a group from netif's igmp group list, but don't free it yet
+ *
+ * @param group the group to remove from the netif's igmp group list
+ * @return ERR_OK if group was removed from the list, an err_t otherwise
+ */
+static err_t
+igmp_remove_group(struct netif *netif, struct igmp_group *group)
+{
+ err_t err = ERR_OK;
+ struct igmp_group *tmp_group;
+
+ /* Skip the first group in the list, it is always the allsystems group added in igmp_start() */
+ for (tmp_group = netif_igmp_data(netif); tmp_group != NULL; tmp_group = tmp_group->next) {
+ if (tmp_group->next == group) {
+ tmp_group->next = group->next;
+ break;
+ }
+ }
+ /* Group not found in netif's igmp group list */
+ if (tmp_group == NULL) {
+ err = ERR_ARG;
+ }
+
+ return err;
+}
+
+/**
+ * Called from ip_input() if a new IGMP packet is received.
+ *
+ * @param p received igmp packet, p->payload pointing to the igmp header
+ * @param inp network interface on which the packet was received
+ * @param dest destination ip address of the igmp packet
+ */
+void
+igmp_input(struct pbuf *p, struct netif *inp, const ip4_addr_t *dest)
+{
+ struct igmp_msg *igmp;
+ struct igmp_group *group;
+ struct igmp_group *groupref;
+
+ IGMP_STATS_INC(igmp.recv);
+
+ /* Note that the length CAN be greater than 8 but only 8 are used - All are included in the checksum */
+ if (p->len < IGMP_MINLEN) {
+ pbuf_free(p);
+ IGMP_STATS_INC(igmp.lenerr);
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: length error\n"));
+ return;
+ }
+
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: message from "));
+ ip4_addr_debug_print_val(IGMP_DEBUG, ip4_current_header()->src);
+ LWIP_DEBUGF(IGMP_DEBUG, (" to address "));
+ ip4_addr_debug_print_val(IGMP_DEBUG, ip4_current_header()->dest);
+ LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", (void *)inp));
+
+ /* Now calculate and check the checksum */
+ igmp = (struct igmp_msg *)p->payload;
+ if (inet_chksum(igmp, p->len)) {
+ pbuf_free(p);
+ IGMP_STATS_INC(igmp.chkerr);
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: checksum error\n"));
+ return;
+ }
+
+ /* Packet is ok so find an existing group */
+ group = igmp_lookfor_group(inp, dest); /* use the destination IP address of incoming packet */
+
+ /* If group can be found or create... */
+ if (!group) {
+ pbuf_free(p);
+ IGMP_STATS_INC(igmp.drop);
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP frame not for us\n"));
+ return;
+ }
+
+ /* NOW ACT ON THE INCOMING MESSAGE TYPE... */
+ switch (igmp->igmp_msgtype) {
+ case IGMP_MEMB_QUERY:
+ /* IGMP_MEMB_QUERY to the "all systems" address ? */
+ if ((ip4_addr_cmp(dest, &allsystems)) && ip4_addr_isany(&igmp->igmp_group_address)) {
+ /* THIS IS THE GENERAL QUERY */
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: General IGMP_MEMB_QUERY on \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
+
+ if (igmp->igmp_maxresp == 0) {
+ IGMP_STATS_INC(igmp.rx_v1);
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: got an all hosts query with time== 0 - this is V1 and not implemented - treat as v2\n"));
+ igmp->igmp_maxresp = IGMP_V1_DELAYING_MEMBER_TMR;
+ } else {
+ IGMP_STATS_INC(igmp.rx_general);
+ }
+
+ groupref = netif_igmp_data(inp);
+
+ /* Do not send messages on the all systems group address! */
+ /* Skip the first group in the list, it is always the allsystems group added in igmp_start() */
+ if (groupref != NULL) {
+ groupref = groupref->next;
+ }
+
+ while (groupref) {
+ igmp_delaying_member(groupref, igmp->igmp_maxresp);
+ groupref = groupref->next;
+ }
+ } else {
+ /* IGMP_MEMB_QUERY to a specific group ? */
+ if (!ip4_addr_isany(&igmp->igmp_group_address)) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_MEMB_QUERY to a specific group "));
+ ip4_addr_debug_print_val(IGMP_DEBUG, igmp->igmp_group_address);
+ if (ip4_addr_cmp(dest, &allsystems)) {
+ ip4_addr_t groupaddr;
+ LWIP_DEBUGF(IGMP_DEBUG, (" using \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
+ /* we first need to re-look for the group since we used dest last time */
+ ip4_addr_copy(groupaddr, igmp->igmp_group_address);
+ group = igmp_lookfor_group(inp, &groupaddr);
+ } else {
+ LWIP_DEBUGF(IGMP_DEBUG, (" with the group address as destination [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
+ }
+
+ if (group != NULL) {
+ IGMP_STATS_INC(igmp.rx_group);
+ igmp_delaying_member(group, igmp->igmp_maxresp);
+ } else {
+ IGMP_STATS_INC(igmp.drop);
+ }
+ } else {
+ IGMP_STATS_INC(igmp.proterr);
+ }
+ }
+ break;
+ case IGMP_V2_MEMB_REPORT:
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_V2_MEMB_REPORT\n"));
+ IGMP_STATS_INC(igmp.rx_report);
+ if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) {
+ /* This is on a specific group we have already looked up */
+ group->timer = 0; /* stopped */
+ group->group_state = IGMP_GROUP_IDLE_MEMBER;
+ group->last_reporter_flag = 0;
+ }
+ break;
+ default:
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: unexpected msg %d in state %d on group %p on if %p\n",
+ igmp->igmp_msgtype, group->group_state, (void *)&group, (void *)inp));
+ IGMP_STATS_INC(igmp.proterr);
+ break;
+ }
+
+ pbuf_free(p);
+ return;
+}
+
+/**
+ * @ingroup igmp
+ * Join a group on one network interface.
+ *
+ * @param ifaddr ip address of the network interface which should join a new group
+ * @param groupaddr the ip address of the group which to join
+ * @return ERR_OK if group was joined on the netif(s), an err_t otherwise
+ */
+err_t
+igmp_joingroup(const ip4_addr_t *ifaddr, const ip4_addr_t *groupaddr)
+{
+ err_t err = ERR_VAL; /* no matching interface */
+ struct netif *netif;
+
+ LWIP_ASSERT_CORE_LOCKED();
+
+ /* make sure it is multicast address */
+ LWIP_ERROR("igmp_joingroup: attempt to join non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;);
+ LWIP_ERROR("igmp_joingroup: attempt to join allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
+
+ /* loop through netif's */
+ NETIF_FOREACH(netif) {
+ /* Should we join this interface ? */
+ if ((netif->flags & NETIF_FLAG_IGMP) && ((ip4_addr_isany(ifaddr) || ip4_addr_cmp(netif_ip4_addr(netif), ifaddr)))) {
+ err = igmp_joingroup_netif(netif, groupaddr);
+ if (err != ERR_OK) {
+ /* Return an error even if some network interfaces are joined */
+ /** @todo undo any other netif already joined */
+ return err;
+ }
+ }
+ }
+
+ return err;
+}
+
+/**
+ * @ingroup igmp
+ * Join a group on one network interface.
+ *
+ * @param netif the network interface which should join a new group
+ * @param groupaddr the ip address of the group which to join
+ * @return ERR_OK if group was joined on the netif, an err_t otherwise
+ */
+err_t
+igmp_joingroup_netif(struct netif *netif, const ip4_addr_t *groupaddr)
+{
+ struct igmp_group *group;
+
+ LWIP_ASSERT_CORE_LOCKED();
+
+ /* make sure it is multicast address */
+ LWIP_ERROR("igmp_joingroup_netif: attempt to join non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;);
+ LWIP_ERROR("igmp_joingroup_netif: attempt to join allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
+
+ /* make sure it is an igmp-enabled netif */
+ LWIP_ERROR("igmp_joingroup_netif: attempt to join on non-IGMP netif", netif->flags & NETIF_FLAG_IGMP, return ERR_VAL;);
+
+ /* find group or create a new one if not found */
+ group = igmp_lookup_group(netif, groupaddr);
+
+ if (group != NULL) {
+ /* This should create a new group, check the state to make sure */
+ if (group->group_state != IGMP_GROUP_NON_MEMBER) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: join to group not in state IGMP_GROUP_NON_MEMBER\n"));
+ } else {
+ /* OK - it was new group */
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: join to new group: "));
+ ip4_addr_debug_print(IGMP_DEBUG, groupaddr);
+ LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
+
+ /* If first use of the group, allow the group at the MAC level */
+ if ((group->use == 0) && (netif->igmp_mac_filter != NULL)) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: igmp_mac_filter(ADD "));
+ ip4_addr_debug_print(IGMP_DEBUG, groupaddr);
+ LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void *)netif));
+ netif->igmp_mac_filter(netif, groupaddr, NETIF_ADD_MAC_FILTER);
+ }
+
+ IGMP_STATS_INC(igmp.tx_join);
+ igmp_send(netif, group, IGMP_V2_MEMB_REPORT);
+
+ igmp_start_timer(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
+
+ /* Need to work out where this timer comes from */
+ group->group_state = IGMP_GROUP_DELAYING_MEMBER;
+ }
+ /* Increment group use */
+ group->use++;
+ /* Join on this interface */
+ return ERR_OK;
+ } else {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: Not enough memory to join to group\n"));
+ return ERR_MEM;
+ }
+}
+
+/**
+ * @ingroup igmp
+ * Leave a group on one network interface.
+ *
+ * @param ifaddr ip address of the network interface which should leave a group
+ * @param groupaddr the ip address of the group which to leave
+ * @return ERR_OK if group was left on the netif(s), an err_t otherwise
+ */
+err_t
+igmp_leavegroup(const ip4_addr_t *ifaddr, const ip4_addr_t *groupaddr)
+{
+ err_t err = ERR_VAL; /* no matching interface */
+ struct netif *netif;
+
+ LWIP_ASSERT_CORE_LOCKED();
+
+ /* make sure it is multicast address */
+ LWIP_ERROR("igmp_leavegroup: attempt to leave non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;);
+ LWIP_ERROR("igmp_leavegroup: attempt to leave allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
+
+ /* loop through netif's */
+ NETIF_FOREACH(netif) {
+ /* Should we leave this interface ? */
+ if ((netif->flags & NETIF_FLAG_IGMP) && ((ip4_addr_isany(ifaddr) || ip4_addr_cmp(netif_ip4_addr(netif), ifaddr)))) {
+ err_t res = igmp_leavegroup_netif(netif, groupaddr);
+ if (err != ERR_OK) {
+ /* Store this result if we have not yet gotten a success */
+ err = res;
+ }
+ }
+ }
+
+ return err;
+}
+
+/**
+ * @ingroup igmp
+ * Leave a group on one network interface.
+ *
+ * @param netif the network interface which should leave a group
+ * @param groupaddr the ip address of the group which to leave
+ * @return ERR_OK if group was left on the netif, an err_t otherwise
+ */
+err_t
+igmp_leavegroup_netif(struct netif *netif, const ip4_addr_t *groupaddr)
+{
+ struct igmp_group *group;
+
+ LWIP_ASSERT_CORE_LOCKED();
+
+ /* make sure it is multicast address */
+ LWIP_ERROR("igmp_leavegroup_netif: attempt to leave non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;);
+ LWIP_ERROR("igmp_leavegroup_netif: attempt to leave allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
+
+ /* make sure it is an igmp-enabled netif */
+ LWIP_ERROR("igmp_leavegroup_netif: attempt to leave on non-IGMP netif", netif->flags & NETIF_FLAG_IGMP, return ERR_VAL;);
+
+ /* find group */
+ group = igmp_lookfor_group(netif, groupaddr);
+
+ if (group != NULL) {
+ /* Only send a leave if the flag is set according to the state diagram */
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: Leaving group: "));
+ ip4_addr_debug_print(IGMP_DEBUG, groupaddr);
+ LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
+
+ /* If there is no other use of the group */
+ if (group->use <= 1) {
+ /* Remove the group from the list */
+ igmp_remove_group(netif, group);
+
+ /* If we are the last reporter for this group */
+ if (group->last_reporter_flag) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: sending leaving group\n"));
+ IGMP_STATS_INC(igmp.tx_leave);
+ igmp_send(netif, group, IGMP_LEAVE_GROUP);
+ }
+
+ /* Disable the group at the MAC level */
+ if (netif->igmp_mac_filter != NULL) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: igmp_mac_filter(DEL "));
+ ip4_addr_debug_print(IGMP_DEBUG, groupaddr);
+ LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void *)netif));
+ netif->igmp_mac_filter(netif, groupaddr, NETIF_DEL_MAC_FILTER);
+ }
+
+ /* Free group struct */
+ memp_free(MEMP_IGMP_GROUP, group);
+ } else {
+ /* Decrement group use */
+ group->use--;
+ }
+ return ERR_OK;
+ } else {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: not member of group\n"));
+ return ERR_VAL;
+ }
+}
+
+/**
+ * The igmp timer function (both for NO_SYS=1 and =0)
+ * Should be called every IGMP_TMR_INTERVAL milliseconds (100 ms is default).
+ */
+void
+igmp_tmr(void)
+{
+ struct netif *netif;
+
+ NETIF_FOREACH(netif) {
+ struct igmp_group *group = netif_igmp_data(netif);
+
+ while (group != NULL) {
+ if (group->timer > 0) {
+ group->timer--;
+ if (group->timer == 0) {
+ igmp_timeout(netif, group);
+ }
+ }
+ group = group->next;
+ }
+ }
+}
+
+/**
+ * Called if a timeout for one group is reached.
+ * Sends a report for this group.
+ *
+ * @param group an igmp_group for which a timeout is reached
+ */
+static void
+igmp_timeout(struct netif *netif, struct igmp_group *group)
+{
+ /* If the state is IGMP_GROUP_DELAYING_MEMBER then we send a report for this group
+ (unless it is the allsystems group) */
+ if ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) &&
+ (!(ip4_addr_cmp(&(group->group_address), &allsystems)))) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_timeout: report membership for group with address "));
+ ip4_addr_debug_print_val(IGMP_DEBUG, group->group_address);
+ LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", (void *)netif));
+
+ group->group_state = IGMP_GROUP_IDLE_MEMBER;
+
+ IGMP_STATS_INC(igmp.tx_report);
+ igmp_send(netif, group, IGMP_V2_MEMB_REPORT);
+ }
+}
+
+/**
+ * Start a timer for an igmp group
+ *
+ * @param group the igmp_group for which to start a timer
+ * @param max_time the time in multiples of IGMP_TMR_INTERVAL (decrease with
+ * every call to igmp_tmr())
+ */
+static void
+igmp_start_timer(struct igmp_group *group, u8_t max_time)
+{
+#ifdef LWIP_RAND
+ group->timer = (u16_t)(max_time > 2 ? (LWIP_RAND() % max_time) : 1);
+#else /* LWIP_RAND */
+ /* ATTENTION: use this only if absolutely necessary! */
+ group->timer = max_time / 2;
+#endif /* LWIP_RAND */
+
+ if (group->timer == 0) {
+ group->timer = 1;
+ }
+}
+
+/**
+ * Delaying membership report for a group if necessary
+ *
+ * @param group the igmp_group for which "delaying" membership report
+ * @param maxresp query delay
+ */
+static void
+igmp_delaying_member(struct igmp_group *group, u8_t maxresp)
+{
+ if ((group->group_state == IGMP_GROUP_IDLE_MEMBER) ||
+ ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) &&
+ ((group->timer == 0) || (maxresp < group->timer)))) {
+ igmp_start_timer(group, maxresp);
+ group->group_state = IGMP_GROUP_DELAYING_MEMBER;
+ }
+}
+
+
+/**
+ * Sends an IP packet on a network interface. This function constructs the IP header
+ * and calculates the IP header checksum. If the source IP address is NULL,
+ * the IP address of the outgoing network interface is filled in as source address.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+ protocol header; if dest == LWIP_IP_HDRINCL, p already includes an
+ IP header and p->payload points to that IP header)
+ * @param src the source IP address to send from (if src == IP4_ADDR_ANY, the
+ * IP address of the netif used to send is used as source address)
+ * @param dest the destination IP address to send the packet to
+ * @param netif the netif on which to send this packet
+ * @return ERR_OK if the packet was sent OK
+ * ERR_BUF if p doesn't have enough space for IP/LINK headers
+ * returns errors returned by netif->output
+ */
+static err_t
+igmp_ip_output_if(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, struct netif *netif)
+{
+ /* This is the "router alert" option */
+ u16_t ra[2];
+ ra[0] = PP_HTONS(ROUTER_ALERT);
+ ra[1] = 0x0000; /* Router shall examine packet */
+ IGMP_STATS_INC(igmp.xmit);
+ return ip4_output_if_opt(p, src, dest, IGMP_TTL, 0, IP_PROTO_IGMP, netif, ra, ROUTER_ALERTLEN);
+}
+
+/**
+ * Send an igmp packet to a specific group.
+ *
+ * @param group the group to which to send the packet
+ * @param type the type of igmp packet to send
+ */
+static void
+igmp_send(struct netif *netif, struct igmp_group *group, u8_t type)
+{
+ struct pbuf *p = NULL;
+ struct igmp_msg *igmp = NULL;
+ ip4_addr_t src = *IP4_ADDR_ANY4;
+ ip4_addr_t *dest = NULL;
+
+ /* IP header + "router alert" option + IGMP header */
+ p = pbuf_alloc(PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM);
+
+ if (p) {
+ igmp = (struct igmp_msg *)p->payload;
+ LWIP_ASSERT("igmp_send: check that first pbuf can hold struct igmp_msg",
+ (p->len >= sizeof(struct igmp_msg)));
+ ip4_addr_copy(src, *netif_ip4_addr(netif));
+
+ if (type == IGMP_V2_MEMB_REPORT) {
+ dest = &(group->group_address);
+ ip4_addr_copy(igmp->igmp_group_address, group->group_address);
+ group->last_reporter_flag = 1; /* Remember we were the last to report */
+ } else {
+ if (type == IGMP_LEAVE_GROUP) {
+ dest = &allrouters;
+ ip4_addr_copy(igmp->igmp_group_address, group->group_address);
+ }
+ }
+
+ if ((type == IGMP_V2_MEMB_REPORT) || (type == IGMP_LEAVE_GROUP)) {
+ igmp->igmp_msgtype = type;
+ igmp->igmp_maxresp = 0;
+ igmp->igmp_checksum = 0;
+ igmp->igmp_checksum = inet_chksum(igmp, IGMP_MINLEN);
+
+ igmp_ip_output_if(p, &src, dest, netif);
+ }
+
+ pbuf_free(p);
+ } else {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_send: not enough memory for igmp_send\n"));
+ IGMP_STATS_INC(igmp.memerr);
+ }
+}
+
+#endif /* LWIP_IPV4 && LWIP_IGMP */
diff --git a/resources/ethernet/LwIP/core/ipv4/ip4.c b/resources/ethernet/LwIP/core/ipv4/ip4.c
new file mode 100644
index 00000000..7350186c
--- /dev/null
+++ b/resources/ethernet/LwIP/core/ipv4/ip4.c
@@ -0,0 +1,1133 @@
+/**
+ * @file
+ * This is the IPv4 layer implementation for incoming and outgoing IP traffic.
+ *
+ * @see ip_frag.c
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV4
+
+#include "lwip/ip.h"
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/ip4_frag.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/netif.h"
+#include "lwip/icmp.h"
+#include "lwip/igmp.h"
+#include "lwip/priv/raw_priv.h"
+#include "lwip/udp.h"
+#include "lwip/priv/tcp_priv.h"
+#include "lwip/autoip.h"
+#include "lwip/stats.h"
+#include "lwip/prot/iana.h"
+
+#include
+#include
+
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+
+/** Set this to 0 in the rare case of wanting to call an extra function to
+ * generate the IP checksum (in contrast to calculating it on-the-fly). */
+#ifndef LWIP_INLINE_IP_CHKSUM
+#if LWIP_CHECKSUM_CTRL_PER_NETIF
+#define LWIP_INLINE_IP_CHKSUM 0
+#else /* LWIP_CHECKSUM_CTRL_PER_NETIF */
+#define LWIP_INLINE_IP_CHKSUM 1
+#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF */
+#endif
+
+#if LWIP_INLINE_IP_CHKSUM && CHECKSUM_GEN_IP
+#define CHECKSUM_GEN_IP_INLINE 1
+#else
+#define CHECKSUM_GEN_IP_INLINE 0
+#endif
+
+#if LWIP_DHCP || defined(LWIP_IP_ACCEPT_UDP_PORT)
+#define IP_ACCEPT_LINK_LAYER_ADDRESSING 1
+
+/** Some defines for DHCP to let link-layer-addressed packets through while the
+ * netif is down.
+ * To use this in your own application/protocol, define LWIP_IP_ACCEPT_UDP_PORT(port)
+ * to return 1 if the port is accepted and 0 if the port is not accepted.
+ */
+#if LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT)
+/* accept DHCP client port and custom port */
+#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) (((port) == PP_NTOHS(LWIP_IANA_PORT_DHCP_CLIENT)) \
+ || (LWIP_IP_ACCEPT_UDP_PORT(port)))
+#elif defined(LWIP_IP_ACCEPT_UDP_PORT) /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */
+/* accept custom port only */
+#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) (LWIP_IP_ACCEPT_UDP_PORT(port))
+#else /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */
+/* accept DHCP client port only */
+#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) ((port) == PP_NTOHS(LWIP_IANA_PORT_DHCP_CLIENT))
+#endif /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */
+
+#else /* LWIP_DHCP */
+#define IP_ACCEPT_LINK_LAYER_ADDRESSING 0
+#endif /* LWIP_DHCP */
+
+/** The IP header ID of the next outgoing IP packet */
+static u16_t ip_id;
+
+#if LWIP_MULTICAST_TX_OPTIONS
+/** The default netif used for multicast */
+static struct netif *ip4_default_multicast_netif;
+
+/**
+ * @ingroup ip4
+ * Set a default netif for IPv4 multicast. */
+void
+ip4_set_default_multicast_netif(struct netif *default_multicast_netif)
+{
+ ip4_default_multicast_netif = default_multicast_netif;
+}
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+
+#ifdef LWIP_HOOK_IP4_ROUTE_SRC
+/**
+ * Source based IPv4 routing must be fully implemented in
+ * LWIP_HOOK_IP4_ROUTE_SRC(). This function only provides the parameters.
+ */
+struct netif *
+ip4_route_src(const ip4_addr_t *src, const ip4_addr_t *dest)
+{
+ if (src != NULL) {
+ /* when src==NULL, the hook is called from ip4_route(dest) */
+ struct netif *netif = LWIP_HOOK_IP4_ROUTE_SRC(src, dest);
+ if (netif != NULL) {
+ return netif;
+ }
+ }
+ return ip4_route(dest);
+}
+#endif /* LWIP_HOOK_IP4_ROUTE_SRC */
+
+/**
+ * Finds the appropriate network interface for a given IP address. It
+ * searches the list of network interfaces linearly. A match is found
+ * if the masked IP address of the network interface equals the masked
+ * IP address given to the function.
+ *
+ * @param dest the destination IP address for which to find the route
+ * @return the netif on which to send to reach dest
+ */
+struct netif *
+ip4_route(const ip4_addr_t *dest)
+{
+#if !LWIP_SINGLE_NETIF
+ struct netif *netif;
+
+ LWIP_ASSERT_CORE_LOCKED();
+
+#if LWIP_MULTICAST_TX_OPTIONS
+ /* Use administratively selected interface for multicast by default */
+ if (ip4_addr_ismulticast(dest) && ip4_default_multicast_netif) {
+ return ip4_default_multicast_netif;
+ }
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+
+ /* bug #54569: in case LWIP_SINGLE_NETIF=1 and LWIP_DEBUGF() disabled, the following loop is optimized away */
+ LWIP_UNUSED_ARG(dest);
+
+ /* iterate through netifs */
+ NETIF_FOREACH(netif) {
+ /* is the netif up, does it have a link and a valid address? */
+ if (netif_is_up(netif) && netif_is_link_up(netif) && !ip4_addr_isany_val(*netif_ip4_addr(netif))) {
+ /* network mask matches? */
+ if (ip4_addr_netcmp(dest, netif_ip4_addr(netif), netif_ip4_netmask(netif))) {
+ /* return netif on which to forward IP packet */
+ return netif;
+ }
+ /* gateway matches on a non broadcast interface? (i.e. peer in a point to point interface) */
+ if (((netif->flags & NETIF_FLAG_BROADCAST) == 0) && ip4_addr_cmp(dest, netif_ip4_gw(netif))) {
+ /* return netif on which to forward IP packet */
+ return netif;
+ }
+ }
+ }
+
+#if LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF
+ /* loopif is disabled, looopback traffic is passed through any netif */
+ if (ip4_addr_isloopback(dest)) {
+ /* don't check for link on loopback traffic */
+ if (netif_default != NULL && netif_is_up(netif_default)) {
+ return netif_default;
+ }
+ /* default netif is not up, just use any netif for loopback traffic */
+ NETIF_FOREACH(netif) {
+ if (netif_is_up(netif)) {
+ return netif;
+ }
+ }
+ return NULL;
+ }
+#endif /* LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF */
+
+#ifdef LWIP_HOOK_IP4_ROUTE_SRC
+ netif = LWIP_HOOK_IP4_ROUTE_SRC(NULL, dest);
+ if (netif != NULL) {
+ return netif;
+ }
+#elif defined(LWIP_HOOK_IP4_ROUTE)
+ netif = LWIP_HOOK_IP4_ROUTE(dest);
+ if (netif != NULL) {
+ return netif;
+ }
+#endif
+#endif /* !LWIP_SINGLE_NETIF */
+
+ if ((netif_default == NULL) || !netif_is_up(netif_default) || !netif_is_link_up(netif_default) ||
+ ip4_addr_isany_val(*netif_ip4_addr(netif_default)) || ip4_addr_isloopback(dest)) {
+ /* No matching netif found and default netif is not usable.
+ If this is not good enough for you, use LWIP_HOOK_IP4_ROUTE() */
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_route: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));
+ IP_STATS_INC(ip.rterr);
+ MIB2_STATS_INC(mib2.ipoutnoroutes);
+ return NULL;
+ }
+
+ return netif_default;
+}
+
+#if IP_FORWARD
+/**
+ * Determine whether an IP address is in a reserved set of addresses
+ * that may not be forwarded, or whether datagrams to that destination
+ * may be forwarded.
+ * @param p the packet to forward
+ * @return 1: can forward 0: discard
+ */
+static int
+ip4_canforward(struct pbuf *p)
+{
+ u32_t addr = lwip_htonl(ip4_addr_get_u32(ip4_current_dest_addr()));
+
+#ifdef LWIP_HOOK_IP4_CANFORWARD
+ int ret = LWIP_HOOK_IP4_CANFORWARD(p, addr);
+ if (ret >= 0) {
+ return ret;
+ }
+#endif /* LWIP_HOOK_IP4_CANFORWARD */
+
+ if (p->flags & PBUF_FLAG_LLBCAST) {
+ /* don't route link-layer broadcasts */
+ return 0;
+ }
+ if ((p->flags & PBUF_FLAG_LLMCAST) || IP_MULTICAST(addr)) {
+ /* don't route link-layer multicasts (use LWIP_HOOK_IP4_CANFORWARD instead) */
+ return 0;
+ }
+ if (IP_EXPERIMENTAL(addr)) {
+ return 0;
+ }
+ if (IP_CLASSA(addr)) {
+ u32_t net = addr & IP_CLASSA_NET;
+ if ((net == 0) || (net == ((u32_t)IP_LOOPBACKNET << IP_CLASSA_NSHIFT))) {
+ /* don't route loopback packets */
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/**
+ * Forwards an IP packet. It finds an appropriate route for the
+ * packet, decrements the TTL value of the packet, adjusts the
+ * checksum and outputs the packet on the appropriate interface.
+ *
+ * @param p the packet to forward (p->payload points to IP header)
+ * @param iphdr the IP header of the input packet
+ * @param inp the netif on which this packet was received
+ */
+static void
+ip4_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp)
+{
+ struct netif *netif;
+
+ PERF_START;
+ LWIP_UNUSED_ARG(inp);
+
+ if (!ip4_canforward(p)) {
+ goto return_noroute;
+ }
+
+ /* RFC3927 2.7: do not forward link-local addresses */
+ if (ip4_addr_islinklocal(ip4_current_dest_addr())) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip4_forward: not forwarding LLA %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(ip4_current_dest_addr()), ip4_addr2_16(ip4_current_dest_addr()),
+ ip4_addr3_16(ip4_current_dest_addr()), ip4_addr4_16(ip4_current_dest_addr())));
+ goto return_noroute;
+ }
+
+ /* Find network interface where to forward this IP packet to. */
+ netif = ip4_route_src(ip4_current_src_addr(), ip4_current_dest_addr());
+ if (netif == NULL) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip4_forward: no forwarding route for %"U16_F".%"U16_F".%"U16_F".%"U16_F" found\n",
+ ip4_addr1_16(ip4_current_dest_addr()), ip4_addr2_16(ip4_current_dest_addr()),
+ ip4_addr3_16(ip4_current_dest_addr()), ip4_addr4_16(ip4_current_dest_addr())));
+ /* @todo: send ICMP_DUR_NET? */
+ goto return_noroute;
+ }
+#if !IP_FORWARD_ALLOW_TX_ON_RX_NETIF
+ /* Do not forward packets onto the same network interface on which
+ * they arrived. */
+ if (netif == inp) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip4_forward: not bouncing packets back on incoming interface.\n"));
+ goto return_noroute;
+ }
+#endif /* IP_FORWARD_ALLOW_TX_ON_RX_NETIF */
+
+ /* decrement TTL */
+ IPH_TTL_SET(iphdr, IPH_TTL(iphdr) - 1);
+ /* send ICMP if TTL == 0 */
+ if (IPH_TTL(iphdr) == 0) {
+ MIB2_STATS_INC(mib2.ipinhdrerrors);
+#if LWIP_ICMP
+ /* Don't send ICMP messages in response to ICMP messages */
+ if (IPH_PROTO(iphdr) != IP_PROTO_ICMP) {
+ icmp_time_exceeded(p, ICMP_TE_TTL);
+ }
+#endif /* LWIP_ICMP */
+ return;
+ }
+
+ /* Incrementally update the IP checksum. */
+ if (IPH_CHKSUM(iphdr) >= PP_HTONS(0xffffU - 0x100)) {
+ IPH_CHKSUM_SET(iphdr, (u16_t)(IPH_CHKSUM(iphdr) + PP_HTONS(0x100) + 1));
+ } else {
+ IPH_CHKSUM_SET(iphdr, (u16_t)(IPH_CHKSUM(iphdr) + PP_HTONS(0x100)));
+ }
+
+ LWIP_DEBUGF(IP_DEBUG, ("ip4_forward: forwarding packet to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(ip4_current_dest_addr()), ip4_addr2_16(ip4_current_dest_addr()),
+ ip4_addr3_16(ip4_current_dest_addr()), ip4_addr4_16(ip4_current_dest_addr())));
+
+ IP_STATS_INC(ip.fw);
+ MIB2_STATS_INC(mib2.ipforwdatagrams);
+ IP_STATS_INC(ip.xmit);
+
+ PERF_STOP("ip4_forward");
+ /* don't fragment if interface has mtu set to 0 [loopif] */
+ if (netif->mtu && (p->tot_len > netif->mtu)) {
+ if ((IPH_OFFSET(iphdr) & PP_NTOHS(IP_DF)) == 0) {
+#if IP_FRAG
+ ip4_frag(p, netif, ip4_current_dest_addr());
+#else /* IP_FRAG */
+ /* @todo: send ICMP Destination Unreachable code 13 "Communication administratively prohibited"? */
+#endif /* IP_FRAG */
+ } else {
+#if LWIP_ICMP
+ /* send ICMP Destination Unreachable code 4: "Fragmentation Needed and DF Set" */
+ icmp_dest_unreach(p, ICMP_DUR_FRAG);
+#endif /* LWIP_ICMP */
+ }
+ return;
+ }
+ /* transmit pbuf on chosen interface */
+ netif->output(netif, p, ip4_current_dest_addr());
+ return;
+return_noroute:
+ MIB2_STATS_INC(mib2.ipoutnoroutes);
+}
+#endif /* IP_FORWARD */
+
+/** Return true if the current input packet should be accepted on this netif */
+static int
+ip4_input_accept(struct netif *netif)
+{
+ LWIP_DEBUGF(IP_DEBUG, ("ip_input: iphdr->dest 0x%"X32_F" netif->ip_addr 0x%"X32_F" (0x%"X32_F", 0x%"X32_F", 0x%"X32_F")\n",
+ ip4_addr_get_u32(ip4_current_dest_addr()), ip4_addr_get_u32(netif_ip4_addr(netif)),
+ ip4_addr_get_u32(ip4_current_dest_addr()) & ip4_addr_get_u32(netif_ip4_netmask(netif)),
+ ip4_addr_get_u32(netif_ip4_addr(netif)) & ip4_addr_get_u32(netif_ip4_netmask(netif)),
+ ip4_addr_get_u32(ip4_current_dest_addr()) & ~ip4_addr_get_u32(netif_ip4_netmask(netif))));
+
+ /* interface is up and configured? */
+ if ((netif_is_up(netif)) && (!ip4_addr_isany_val(*netif_ip4_addr(netif)))) {
+ /* unicast to this interface address? */
+ if (ip4_addr_cmp(ip4_current_dest_addr(), netif_ip4_addr(netif)) ||
+ /* or broadcast on this interface network address? */
+ ip4_addr_isbroadcast(ip4_current_dest_addr(), netif)
+#if LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF
+ || (ip4_addr_get_u32(ip4_current_dest_addr()) == PP_HTONL(IPADDR_LOOPBACK))
+#endif /* LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF */
+ ) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip4_input: packet accepted on interface %c%c\n",
+ netif->name[0], netif->name[1]));
+ /* accept on this netif */
+ return 1;
+ }
+#if LWIP_AUTOIP
+ /* connections to link-local addresses must persist after changing
+ the netif's address (RFC3927 ch. 1.9) */
+ if (autoip_accept_packet(netif, ip4_current_dest_addr())) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip4_input: LLA packet accepted on interface %c%c\n",
+ netif->name[0], netif->name[1]));
+ /* accept on this netif */
+ return 1;
+ }
+#endif /* LWIP_AUTOIP */
+ }
+ return 0;
+}
+
+/**
+ * This function is called by the network interface device driver when
+ * an IP packet is received. The function does the basic checks of the
+ * IP header such as packet size being at least larger than the header
+ * size etc. If the packet was not destined for us, the packet is
+ * forwarded (using ip_forward). The IP checksum is always checked.
+ *
+ * Finally, the packet is sent to the upper layer protocol input function.
+ *
+ * @param p the received IP packet (p->payload points to IP header)
+ * @param inp the netif on which this packet was received
+ * @return ERR_OK if the packet was processed (could return ERR_* if it wasn't
+ * processed, but currently always returns ERR_OK)
+ */
+err_t
+ip4_input(struct pbuf *p, struct netif *inp)
+{
+ const struct ip_hdr *iphdr;
+ struct netif *netif;
+ u16_t iphdr_hlen;
+ u16_t iphdr_len;
+#if IP_ACCEPT_LINK_LAYER_ADDRESSING || LWIP_IGMP
+ int check_ip_src = 1;
+#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING || LWIP_IGMP */
+#if LWIP_RAW
+ raw_input_state_t raw_status;
+#endif /* LWIP_RAW */
+
+ LWIP_ASSERT_CORE_LOCKED();
+
+ IP_STATS_INC(ip.recv);
+ MIB2_STATS_INC(mib2.ipinreceives);
+
+ /* identify the IP header */
+ iphdr = (struct ip_hdr *)p->payload;
+ if (IPH_V(iphdr) != 4) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_WARNING, ("IP packet dropped due to bad version number %"U16_F"\n", (u16_t)IPH_V(iphdr)));
+ ip4_debug_print(p);
+ pbuf_free(p);
+ IP_STATS_INC(ip.err);
+ IP_STATS_INC(ip.drop);
+ MIB2_STATS_INC(mib2.ipinhdrerrors);
+ return ERR_OK;
+ }
+
+#ifdef LWIP_HOOK_IP4_INPUT
+ if (LWIP_HOOK_IP4_INPUT(p, inp)) {
+ /* the packet has been eaten */
+ return ERR_OK;
+ }
+#endif
+
+ /* obtain IP header length in bytes */
+ iphdr_hlen = IPH_HL_BYTES(iphdr);
+ /* obtain ip length in bytes */
+ iphdr_len = lwip_ntohs(IPH_LEN(iphdr));
+
+ /* Trim pbuf. This is especially required for packets < 60 bytes. */
+ if (iphdr_len < p->tot_len) {
+ pbuf_realloc(p, iphdr_len);
+ }
+
+ /* header length exceeds first pbuf length, or ip length exceeds total pbuf length? */
+ if ((iphdr_hlen > p->len) || (iphdr_len > p->tot_len) || (iphdr_hlen < IP_HLEN)) {
+ if (iphdr_hlen < IP_HLEN) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("ip4_input: short IP header (%"U16_F" bytes) received, IP packet dropped\n", iphdr_hlen));
+ }
+ if (iphdr_hlen > p->len) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("IP header (len %"U16_F") does not fit in first pbuf (len %"U16_F"), IP packet dropped.\n",
+ iphdr_hlen, p->len));
+ }
+ if (iphdr_len > p->tot_len) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("IP (len %"U16_F") is longer than pbuf (len %"U16_F"), IP packet dropped.\n",
+ iphdr_len, p->tot_len));
+ }
+ /* free (drop) packet pbufs */
+ pbuf_free(p);
+ IP_STATS_INC(ip.lenerr);
+ IP_STATS_INC(ip.drop);
+ MIB2_STATS_INC(mib2.ipindiscards);
+ return ERR_OK;
+ }
+
+ /* verify checksum */
+#if CHECKSUM_CHECK_IP
+ IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_IP) {
+ if (inet_chksum(iphdr, iphdr_hlen) != 0) {
+
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("Checksum (0x%"X16_F") failed, IP packet dropped.\n", inet_chksum(iphdr, iphdr_hlen)));
+ ip4_debug_print(p);
+ pbuf_free(p);
+ IP_STATS_INC(ip.chkerr);
+ IP_STATS_INC(ip.drop);
+ MIB2_STATS_INC(mib2.ipinhdrerrors);
+ return ERR_OK;
+ }
+ }
+#endif
+ /* copy IP addresses to aligned ip_addr_t */
+ ip_addr_copy_from_ip4(ip_data.current_iphdr_dest, iphdr->dest);
+ ip_addr_copy_from_ip4(ip_data.current_iphdr_src, iphdr->src);
+
+ /* match packet against an interface, i.e. is this packet for us? */
+ if (ip4_addr_ismulticast(ip4_current_dest_addr())) {
+#if LWIP_IGMP
+ if ((inp->flags & NETIF_FLAG_IGMP) && (igmp_lookfor_group(inp, ip4_current_dest_addr()))) {
+ /* IGMP snooping switches need 0.0.0.0 to be allowed as source address (RFC 4541) */
+ ip4_addr_t allsystems;
+ IP4_ADDR(&allsystems, 224, 0, 0, 1);
+ if (ip4_addr_cmp(ip4_current_dest_addr(), &allsystems) &&
+ ip4_addr_isany(ip4_current_src_addr())) {
+ check_ip_src = 0;
+ }
+ netif = inp;
+ } else {
+ netif = NULL;
+ }
+#else /* LWIP_IGMP */
+ if ((netif_is_up(inp)) && (!ip4_addr_isany_val(*netif_ip4_addr(inp)))) {
+ netif = inp;
+ } else {
+ netif = NULL;
+ }
+#endif /* LWIP_IGMP */
+ } else {
+ /* start trying with inp. if that's not acceptable, start walking the
+ list of configured netifs. */
+ if (ip4_input_accept(inp)) {
+ netif = inp;
+ } else {
+ netif = NULL;
+#if !LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF
+ /* Packets sent to the loopback address must not be accepted on an
+ * interface that does not have the loopback address assigned to it,
+ * unless a non-loopback interface is used for loopback traffic. */
+ if (!ip4_addr_isloopback(ip4_current_dest_addr()))
+#endif /* !LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF */
+ {
+#if !LWIP_SINGLE_NETIF
+ NETIF_FOREACH(netif) {
+ if (netif == inp) {
+ /* we checked that before already */
+ continue;
+ }
+ if (ip4_input_accept(netif)) {
+ break;
+ }
+ }
+#endif /* !LWIP_SINGLE_NETIF */
+ }
+ }
+ }
+
+#if IP_ACCEPT_LINK_LAYER_ADDRESSING
+ /* Pass DHCP messages regardless of destination address. DHCP traffic is addressed
+ * using link layer addressing (such as Ethernet MAC) so we must not filter on IP.
+ * According to RFC 1542 section 3.1.1, referred by RFC 2131).
+ *
+ * If you want to accept private broadcast communication while a netif is down,
+ * define LWIP_IP_ACCEPT_UDP_PORT(dst_port), e.g.:
+ *
+ * #define LWIP_IP_ACCEPT_UDP_PORT(dst_port) ((dst_port) == PP_NTOHS(12345))
+ */
+ if (netif == NULL) {
+ /* remote port is DHCP server? */
+ if (IPH_PROTO(iphdr) == IP_PROTO_UDP) {
+ const struct udp_hdr *udphdr = (const struct udp_hdr *)((const u8_t *)iphdr + iphdr_hlen);
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip4_input: UDP packet to DHCP client port %"U16_F"\n",
+ lwip_ntohs(udphdr->dest)));
+ if (IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(udphdr->dest)) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip4_input: DHCP packet accepted.\n"));
+ netif = inp;
+ check_ip_src = 0;
+ }
+ }
+ }
+#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */
+
+ /* broadcast or multicast packet source address? Compliant with RFC 1122: 3.2.1.3 */
+#if LWIP_IGMP || IP_ACCEPT_LINK_LAYER_ADDRESSING
+ if (check_ip_src
+#if IP_ACCEPT_LINK_LAYER_ADDRESSING
+ /* DHCP servers need 0.0.0.0 to be allowed as source address (RFC 1.1.2.2: 3.2.1.3/a) */
+ && !ip4_addr_isany_val(*ip4_current_src_addr())
+#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */
+ )
+#endif /* LWIP_IGMP || IP_ACCEPT_LINK_LAYER_ADDRESSING */
+ {
+ if ((ip4_addr_isbroadcast(ip4_current_src_addr(), inp)) ||
+ (ip4_addr_ismulticast(ip4_current_src_addr()))) {
+ /* packet source is not valid */
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("ip4_input: packet source is not valid.\n"));
+ /* free (drop) packet pbufs */
+ pbuf_free(p);
+ IP_STATS_INC(ip.drop);
+ MIB2_STATS_INC(mib2.ipinaddrerrors);
+ MIB2_STATS_INC(mib2.ipindiscards);
+ return ERR_OK;
+ }
+ }
+
+ /* packet not for us? */
+ if (netif == NULL) {
+ /* packet not for us, route or discard */
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip4_input: packet not for us.\n"));
+#if IP_FORWARD
+ /* non-broadcast packet? */
+ if (!ip4_addr_isbroadcast(ip4_current_dest_addr(), inp)) {
+ /* try to forward IP packet on (other) interfaces */
+ ip4_forward(p, (struct ip_hdr *)p->payload, inp);
+ } else
+#endif /* IP_FORWARD */
+ {
+ IP_STATS_INC(ip.drop);
+ MIB2_STATS_INC(mib2.ipinaddrerrors);
+ MIB2_STATS_INC(mib2.ipindiscards);
+ }
+ pbuf_free(p);
+ return ERR_OK;
+ }
+
+ /* packet consists of multiple fragments? */
+ if ((IPH_OFFSET(iphdr) & PP_HTONS(IP_OFFMASK | IP_MF)) != 0) {
+#if IP_REASSEMBLY /* packet fragment reassembly code present? */
+ LWIP_DEBUGF(IP_DEBUG, ("IP packet is a fragment (id=0x%04"X16_F" tot_len=%"U16_F" len=%"U16_F" MF=%"U16_F" offset=%"U16_F"), calling ip4_reass()\n",
+ lwip_ntohs(IPH_ID(iphdr)), p->tot_len, lwip_ntohs(IPH_LEN(iphdr)), (u16_t)!!(IPH_OFFSET(iphdr) & PP_HTONS(IP_MF)), (u16_t)((lwip_ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK) * 8)));
+ /* reassemble the packet*/
+ p = ip4_reass(p);
+ /* packet not fully reassembled yet? */
+ if (p == NULL) {
+ return ERR_OK;
+ }
+ iphdr = (const struct ip_hdr *)p->payload;
+#else /* IP_REASSEMBLY == 0, no packet fragment reassembly code present */
+ pbuf_free(p);
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IP packet dropped since it was fragmented (0x%"X16_F") (while IP_REASSEMBLY == 0).\n",
+ lwip_ntohs(IPH_OFFSET(iphdr))));
+ IP_STATS_INC(ip.opterr);
+ IP_STATS_INC(ip.drop);
+ /* unsupported protocol feature */
+ MIB2_STATS_INC(mib2.ipinunknownprotos);
+ return ERR_OK;
+#endif /* IP_REASSEMBLY */
+ }
+
+#if IP_OPTIONS_ALLOWED == 0 /* no support for IP options in the IP header? */
+
+#if LWIP_IGMP
+ /* there is an extra "router alert" option in IGMP messages which we allow for but do not police */
+ if ((iphdr_hlen > IP_HLEN) && (IPH_PROTO(iphdr) != IP_PROTO_IGMP)) {
+#else
+ if (iphdr_hlen > IP_HLEN) {
+#endif /* LWIP_IGMP */
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IP packet dropped since there were IP options (while IP_OPTIONS_ALLOWED == 0).\n"));
+ pbuf_free(p);
+ IP_STATS_INC(ip.opterr);
+ IP_STATS_INC(ip.drop);
+ /* unsupported protocol feature */
+ MIB2_STATS_INC(mib2.ipinunknownprotos);
+ return ERR_OK;
+ }
+#endif /* IP_OPTIONS_ALLOWED == 0 */
+
+ /* send to upper layers */
+ LWIP_DEBUGF(IP_DEBUG, ("ip4_input: \n"));
+ ip4_debug_print(p);
+ LWIP_DEBUGF(IP_DEBUG, ("ip4_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len));
+
+ ip_data.current_netif = netif;
+ ip_data.current_input_netif = inp;
+ ip_data.current_ip4_header = iphdr;
+ ip_data.current_ip_header_tot_len = IPH_HL_BYTES(iphdr);
+
+#if LWIP_RAW
+ /* raw input did not eat the packet? */
+ raw_status = raw_input(p, inp);
+ if (raw_status != RAW_INPUT_EATEN)
+#endif /* LWIP_RAW */
+ {
+ pbuf_remove_header(p, iphdr_hlen); /* Move to payload, no check necessary. */
+
+ switch (IPH_PROTO(iphdr)) {
+#if LWIP_UDP
+ case IP_PROTO_UDP:
+#if LWIP_UDPLITE
+ case IP_PROTO_UDPLITE:
+#endif /* LWIP_UDPLITE */
+ MIB2_STATS_INC(mib2.ipindelivers);
+ udp_input(p, inp);
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case IP_PROTO_TCP:
+ MIB2_STATS_INC(mib2.ipindelivers);
+ tcp_input(p, inp);
+ break;
+#endif /* LWIP_TCP */
+#if LWIP_ICMP
+ case IP_PROTO_ICMP:
+ MIB2_STATS_INC(mib2.ipindelivers);
+ icmp_input(p, inp);
+ break;
+#endif /* LWIP_ICMP */
+#if LWIP_IGMP
+ case IP_PROTO_IGMP:
+ igmp_input(p, inp, ip4_current_dest_addr());
+ break;
+#endif /* LWIP_IGMP */
+ default:
+#if LWIP_RAW
+ if (raw_status == RAW_INPUT_DELIVERED) {
+ MIB2_STATS_INC(mib2.ipindelivers);
+ } else
+#endif /* LWIP_RAW */
+ {
+#if LWIP_ICMP
+ /* send ICMP destination protocol unreachable unless is was a broadcast */
+ if (!ip4_addr_isbroadcast(ip4_current_dest_addr(), netif) &&
+ !ip4_addr_ismulticast(ip4_current_dest_addr())) {
+ pbuf_header_force(p, (s16_t)iphdr_hlen); /* Move to ip header, no check necessary. */
+ icmp_dest_unreach(p, ICMP_DUR_PROTO);
+ }
+#endif /* LWIP_ICMP */
+
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("Unsupported transport protocol %"U16_F"\n", (u16_t)IPH_PROTO(iphdr)));
+
+ IP_STATS_INC(ip.proterr);
+ IP_STATS_INC(ip.drop);
+ MIB2_STATS_INC(mib2.ipinunknownprotos);
+ }
+ pbuf_free(p);
+ break;
+ }
+ }
+
+ /* @todo: this is not really necessary... */
+ ip_data.current_netif = NULL;
+ ip_data.current_input_netif = NULL;
+ ip_data.current_ip4_header = NULL;
+ ip_data.current_ip_header_tot_len = 0;
+ ip4_addr_set_any(ip4_current_src_addr());
+ ip4_addr_set_any(ip4_current_dest_addr());
+
+ return ERR_OK;
+}
+
+/**
+ * Sends an IP packet on a network interface. This function constructs
+ * the IP header and calculates the IP header checksum. If the source
+ * IP address is NULL, the IP address of the outgoing network
+ * interface is filled in as source address.
+ * If the destination IP address is LWIP_IP_HDRINCL, p is assumed to already
+ * include an IP header and p->payload points to it instead of the data.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+ protocol header; if dest == LWIP_IP_HDRINCL, p already includes an
+ IP header and p->payload points to that IP header)
+ * @param src the source IP address to send from (if src == IP4_ADDR_ANY, the
+ * IP address of the netif used to send is used as source address)
+ * @param dest the destination IP address to send the packet to
+ * @param ttl the TTL value to be set in the IP header
+ * @param tos the TOS value to be set in the IP header
+ * @param proto the PROTOCOL to be set in the IP header
+ * @param netif the netif on which to send this packet
+ * @return ERR_OK if the packet was sent OK
+ * ERR_BUF if p doesn't have enough space for IP/LINK headers
+ * returns errors returned by netif->output
+ *
+ * @note ip_id: RFC791 "some host may be able to simply use
+ * unique identifiers independent of destination"
+ */
+err_t
+ip4_output_if(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
+ u8_t ttl, u8_t tos,
+ u8_t proto, struct netif *netif)
+{
+#if IP_OPTIONS_SEND
+ return ip4_output_if_opt(p, src, dest, ttl, tos, proto, netif, NULL, 0);
+}
+
+/**
+ * Same as ip_output_if() but with the possibility to include IP options:
+ *
+ * @ param ip_options pointer to the IP options, copied into the IP header
+ * @ param optlen length of ip_options
+ */
+err_t
+ip4_output_if_opt(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
+ u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options,
+ u16_t optlen)
+{
+#endif /* IP_OPTIONS_SEND */
+ const ip4_addr_t *src_used = src;
+ if (dest != LWIP_IP_HDRINCL) {
+ if (ip4_addr_isany(src)) {
+ src_used = netif_ip4_addr(netif);
+ }
+ }
+
+#if IP_OPTIONS_SEND
+ return ip4_output_if_opt_src(p, src_used, dest, ttl, tos, proto, netif,
+ ip_options, optlen);
+#else /* IP_OPTIONS_SEND */
+ return ip4_output_if_src(p, src_used, dest, ttl, tos, proto, netif);
+#endif /* IP_OPTIONS_SEND */
+}
+
+/**
+ * Same as ip_output_if() but 'src' address is not replaced by netif address
+ * when it is 'any'.
+ */
+err_t
+ip4_output_if_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
+ u8_t ttl, u8_t tos,
+ u8_t proto, struct netif *netif)
+{
+#if IP_OPTIONS_SEND
+ return ip4_output_if_opt_src(p, src, dest, ttl, tos, proto, netif, NULL, 0);
+}
+
+/**
+ * Same as ip_output_if_opt() but 'src' address is not replaced by netif address
+ * when it is 'any'.
+ */
+err_t
+ip4_output_if_opt_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
+ u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options,
+ u16_t optlen)
+{
+#endif /* IP_OPTIONS_SEND */
+ struct ip_hdr *iphdr;
+ ip4_addr_t dest_addr;
+#if CHECKSUM_GEN_IP_INLINE
+ u32_t chk_sum = 0;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+
+ LWIP_ASSERT_CORE_LOCKED();
+ LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p);
+
+ MIB2_STATS_INC(mib2.ipoutrequests);
+
+ /* Should the IP header be generated or is it already included in p? */
+ if (dest != LWIP_IP_HDRINCL) {
+ u16_t ip_hlen = IP_HLEN;
+#if IP_OPTIONS_SEND
+ u16_t optlen_aligned = 0;
+ if (optlen != 0) {
+#if CHECKSUM_GEN_IP_INLINE
+ int i;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+ if (optlen > (IP_HLEN_MAX - IP_HLEN)) {
+ /* optlen too long */
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_output_if_opt: optlen too long\n"));
+ IP_STATS_INC(ip.err);
+ MIB2_STATS_INC(mib2.ipoutdiscards);
+ return ERR_VAL;
+ }
+ /* round up to a multiple of 4 */
+ optlen_aligned = (u16_t)((optlen + 3) & ~3);
+ ip_hlen = (u16_t)(ip_hlen + optlen_aligned);
+ /* First write in the IP options */
+ if (pbuf_add_header(p, optlen_aligned)) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_output_if_opt: not enough room for IP options in pbuf\n"));
+ IP_STATS_INC(ip.err);
+ MIB2_STATS_INC(mib2.ipoutdiscards);
+ return ERR_BUF;
+ }
+ MEMCPY(p->payload, ip_options, optlen);
+ if (optlen < optlen_aligned) {
+ /* zero the remaining bytes */
+ memset(((char *)p->payload) + optlen, 0, (size_t)(optlen_aligned - optlen));
+ }
+#if CHECKSUM_GEN_IP_INLINE
+ for (i = 0; i < optlen_aligned / 2; i++) {
+ chk_sum += ((u16_t *)p->payload)[i];
+ }
+#endif /* CHECKSUM_GEN_IP_INLINE */
+ }
+#endif /* IP_OPTIONS_SEND */
+ /* generate IP header */
+ if (pbuf_add_header(p, IP_HLEN)) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_output: not enough room for IP header in pbuf\n"));
+
+ IP_STATS_INC(ip.err);
+ MIB2_STATS_INC(mib2.ipoutdiscards);
+ return ERR_BUF;
+ }
+
+ iphdr = (struct ip_hdr *)p->payload;
+ LWIP_ASSERT("check that first pbuf can hold struct ip_hdr",
+ (p->len >= sizeof(struct ip_hdr)));
+
+ IPH_TTL_SET(iphdr, ttl);
+ IPH_PROTO_SET(iphdr, proto);
+#if CHECKSUM_GEN_IP_INLINE
+ chk_sum += PP_NTOHS(proto | (ttl << 8));
+#endif /* CHECKSUM_GEN_IP_INLINE */
+
+ /* dest cannot be NULL here */
+ ip4_addr_copy(iphdr->dest, *dest);
+#if CHECKSUM_GEN_IP_INLINE
+ chk_sum += ip4_addr_get_u32(&iphdr->dest) & 0xFFFF;
+ chk_sum += ip4_addr_get_u32(&iphdr->dest) >> 16;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+
+ IPH_VHL_SET(iphdr, 4, ip_hlen / 4);
+ IPH_TOS_SET(iphdr, tos);
+#if CHECKSUM_GEN_IP_INLINE
+ chk_sum += PP_NTOHS(tos | (iphdr->_v_hl << 8));
+#endif /* CHECKSUM_GEN_IP_INLINE */
+ IPH_LEN_SET(iphdr, lwip_htons(p->tot_len));
+#if CHECKSUM_GEN_IP_INLINE
+ chk_sum += iphdr->_len;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+ IPH_OFFSET_SET(iphdr, 0);
+ IPH_ID_SET(iphdr, lwip_htons(ip_id));
+#if CHECKSUM_GEN_IP_INLINE
+ chk_sum += iphdr->_id;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+ ++ip_id;
+
+ if (src == NULL) {
+ ip4_addr_copy(iphdr->src, *IP4_ADDR_ANY4);
+ } else {
+ /* src cannot be NULL here */
+ ip4_addr_copy(iphdr->src, *src);
+ }
+
+#if CHECKSUM_GEN_IP_INLINE
+ chk_sum += ip4_addr_get_u32(&iphdr->src) & 0xFFFF;
+ chk_sum += ip4_addr_get_u32(&iphdr->src) >> 16;
+ chk_sum = (chk_sum >> 16) + (chk_sum & 0xFFFF);
+ chk_sum = (chk_sum >> 16) + chk_sum;
+ chk_sum = ~chk_sum;
+ IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_IP) {
+ iphdr->_chksum = (u16_t)chk_sum; /* network order */
+ }
+#if LWIP_CHECKSUM_CTRL_PER_NETIF
+ else {
+ IPH_CHKSUM_SET(iphdr, 0);
+ }
+#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF*/
+#else /* CHECKSUM_GEN_IP_INLINE */
+ IPH_CHKSUM_SET(iphdr, 0);
+#if CHECKSUM_GEN_IP
+ IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_IP) {
+ IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, ip_hlen));
+ }
+#endif /* CHECKSUM_GEN_IP */
+#endif /* CHECKSUM_GEN_IP_INLINE */
+ } else {
+ /* IP header already included in p */
+ if (p->len < IP_HLEN) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_output: LWIP_IP_HDRINCL but pbuf is too short\n"));
+ IP_STATS_INC(ip.err);
+ MIB2_STATS_INC(mib2.ipoutdiscards);
+ return ERR_BUF;
+ }
+ iphdr = (struct ip_hdr *)p->payload;
+ ip4_addr_copy(dest_addr, iphdr->dest);
+ dest = &dest_addr;
+ }
+
+ IP_STATS_INC(ip.xmit);
+
+ LWIP_DEBUGF(IP_DEBUG, ("ip4_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], (u16_t)netif->num));
+ ip4_debug_print(p);
+
+#if ENABLE_LOOPBACK
+ if (ip4_addr_cmp(dest, netif_ip4_addr(netif))
+#if !LWIP_HAVE_LOOPIF
+ || ip4_addr_isloopback(dest)
+#endif /* !LWIP_HAVE_LOOPIF */
+ ) {
+ /* Packet to self, enqueue it for loopback */
+ LWIP_DEBUGF(IP_DEBUG, ("netif_loop_output()"));
+ return netif_loop_output(netif, p);
+ }
+#if LWIP_MULTICAST_TX_OPTIONS
+ if ((p->flags & PBUF_FLAG_MCASTLOOP) != 0) {
+ netif_loop_output(netif, p);
+ }
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+#endif /* ENABLE_LOOPBACK */
+#if IP_FRAG
+ /* don't fragment if interface has mtu set to 0 [loopif] */
+ if (netif->mtu && (p->tot_len > netif->mtu)) {
+ return ip4_frag(p, netif, dest);
+ }
+#endif /* IP_FRAG */
+
+ LWIP_DEBUGF(IP_DEBUG, ("ip4_output_if: call netif->output()\n"));
+ return netif->output(netif, p, dest);
+}
+
+/**
+ * Simple interface to ip_output_if. It finds the outgoing network
+ * interface and calls upon ip_output_if to do the actual work.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+ protocol header; if dest == LWIP_IP_HDRINCL, p already includes an
+ IP header and p->payload points to that IP header)
+ * @param src the source IP address to send from (if src == IP4_ADDR_ANY, the
+ * IP address of the netif used to send is used as source address)
+ * @param dest the destination IP address to send the packet to
+ * @param ttl the TTL value to be set in the IP header
+ * @param tos the TOS value to be set in the IP header
+ * @param proto the PROTOCOL to be set in the IP header
+ *
+ * @return ERR_RTE if no route is found
+ * see ip_output_if() for more return values
+ */
+err_t
+ip4_output(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
+ u8_t ttl, u8_t tos, u8_t proto)
+{
+ struct netif *netif;
+
+ LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p);
+
+ if ((netif = ip4_route_src(src, dest)) == NULL) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip4_output: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));
+ IP_STATS_INC(ip.rterr);
+ return ERR_RTE;
+ }
+
+ return ip4_output_if(p, src, dest, ttl, tos, proto, netif);
+}
+
+#if LWIP_NETIF_USE_HINTS
+/** Like ip_output, but takes and addr_hint pointer that is passed on to netif->addr_hint
+ * before calling ip_output_if.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+ protocol header; if dest == LWIP_IP_HDRINCL, p already includes an
+ IP header and p->payload points to that IP header)
+ * @param src the source IP address to send from (if src == IP4_ADDR_ANY, the
+ * IP address of the netif used to send is used as source address)
+ * @param dest the destination IP address to send the packet to
+ * @param ttl the TTL value to be set in the IP header
+ * @param tos the TOS value to be set in the IP header
+ * @param proto the PROTOCOL to be set in the IP header
+ * @param netif_hint netif output hint pointer set to netif->hint before
+ * calling ip_output_if()
+ *
+ * @return ERR_RTE if no route is found
+ * see ip_output_if() for more return values
+ */
+err_t
+ip4_output_hinted(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
+ u8_t ttl, u8_t tos, u8_t proto, struct netif_hint *netif_hint)
+{
+ struct netif *netif;
+ err_t err;
+
+ LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p);
+
+ if ((netif = ip4_route_src(src, dest)) == NULL) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip4_output: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));
+ IP_STATS_INC(ip.rterr);
+ return ERR_RTE;
+ }
+
+ NETIF_SET_HINTS(netif, netif_hint);
+ err = ip4_output_if(p, src, dest, ttl, tos, proto, netif);
+ NETIF_RESET_HINTS(netif);
+
+ return err;
+}
+#endif /* LWIP_NETIF_USE_HINTS*/
+
+#if IP_DEBUG
+/* Print an IP header by using LWIP_DEBUGF
+ * @param p an IP packet, p->payload pointing to the IP header
+ */
+void
+ip4_debug_print(struct pbuf *p)
+{
+ struct ip_hdr *iphdr = (struct ip_hdr *)p->payload;
+
+ LWIP_DEBUGF(IP_DEBUG, ("IP header:\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("|%2"S16_F" |%2"S16_F" | 0x%02"X16_F" | %5"U16_F" | (v, hl, tos, len)\n",
+ (u16_t)IPH_V(iphdr),
+ (u16_t)IPH_HL(iphdr),
+ (u16_t)IPH_TOS(iphdr),
+ lwip_ntohs(IPH_LEN(iphdr))));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("| %5"U16_F" |%"U16_F"%"U16_F"%"U16_F"| %4"U16_F" | (id, flags, offset)\n",
+ lwip_ntohs(IPH_ID(iphdr)),
+ (u16_t)(lwip_ntohs(IPH_OFFSET(iphdr)) >> 15 & 1),
+ (u16_t)(lwip_ntohs(IPH_OFFSET(iphdr)) >> 14 & 1),
+ (u16_t)(lwip_ntohs(IPH_OFFSET(iphdr)) >> 13 & 1),
+ (u16_t)(lwip_ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK)));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | 0x%04"X16_F" | (ttl, proto, chksum)\n",
+ (u16_t)IPH_TTL(iphdr),
+ (u16_t)IPH_PROTO(iphdr),
+ lwip_ntohs(IPH_CHKSUM(iphdr))));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | %3"U16_F" | %3"U16_F" | (src)\n",
+ ip4_addr1_16_val(iphdr->src),
+ ip4_addr2_16_val(iphdr->src),
+ ip4_addr3_16_val(iphdr->src),
+ ip4_addr4_16_val(iphdr->src)));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | %3"U16_F" | %3"U16_F" | (dest)\n",
+ ip4_addr1_16_val(iphdr->dest),
+ ip4_addr2_16_val(iphdr->dest),
+ ip4_addr3_16_val(iphdr->dest),
+ ip4_addr4_16_val(iphdr->dest)));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+}
+#endif /* IP_DEBUG */
+
+#endif /* LWIP_IPV4 */
diff --git a/resources/ethernet/LwIP/core/ipv4/ip4_addr.c b/resources/ethernet/LwIP/core/ipv4/ip4_addr.c
new file mode 100644
index 00000000..92077c20
--- /dev/null
+++ b/resources/ethernet/LwIP/core/ipv4/ip4_addr.c
@@ -0,0 +1,321 @@
+/**
+ * @file
+ * This is the IPv4 address tools implementation.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV4
+
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+
+/* used by IP4_ADDR_ANY and IP_ADDR_BROADCAST in ip_addr.h */
+const ip_addr_t ip_addr_any = IPADDR4_INIT(IPADDR_ANY);
+const ip_addr_t ip_addr_broadcast = IPADDR4_INIT(IPADDR_BROADCAST);
+
+/**
+ * Determine if an address is a broadcast address on a network interface
+ *
+ * @param addr address to be checked
+ * @param netif the network interface against which the address is checked
+ * @return returns non-zero if the address is a broadcast address
+ */
+u8_t
+ip4_addr_isbroadcast_u32(u32_t addr, const struct netif *netif)
+{
+ ip4_addr_t ipaddr;
+ ip4_addr_set_u32(&ipaddr, addr);
+
+ /* all ones (broadcast) or all zeroes (old skool broadcast) */
+ if ((~addr == IPADDR_ANY) ||
+ (addr == IPADDR_ANY)) {
+ return 1;
+ /* no broadcast support on this network interface? */
+ } else if ((netif->flags & NETIF_FLAG_BROADCAST) == 0) {
+ /* the given address cannot be a broadcast address
+ * nor can we check against any broadcast addresses */
+ return 0;
+ /* address matches network interface address exactly? => no broadcast */
+ } else if (addr == ip4_addr_get_u32(netif_ip4_addr(netif))) {
+ return 0;
+ /* on the same (sub) network... */
+ } else if (ip4_addr_netcmp(&ipaddr, netif_ip4_addr(netif), netif_ip4_netmask(netif))
+ /* ...and host identifier bits are all ones? =>... */
+ && ((addr & ~ip4_addr_get_u32(netif_ip4_netmask(netif))) ==
+ (IPADDR_BROADCAST & ~ip4_addr_get_u32(netif_ip4_netmask(netif))))) {
+ /* => network broadcast address */
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/** Checks if a netmask is valid (starting with ones, then only zeros)
+ *
+ * @param netmask the IPv4 netmask to check (in network byte order!)
+ * @return 1 if the netmask is valid, 0 if it is not
+ */
+u8_t
+ip4_addr_netmask_valid(u32_t netmask)
+{
+ u32_t mask;
+ u32_t nm_hostorder = lwip_htonl(netmask);
+
+ /* first, check for the first zero */
+ for (mask = 1UL << 31 ; mask != 0; mask >>= 1) {
+ if ((nm_hostorder & mask) == 0) {
+ break;
+ }
+ }
+ /* then check that there is no one */
+ for (; mask != 0; mask >>= 1) {
+ if ((nm_hostorder & mask) != 0) {
+ /* there is a one after the first zero -> invalid */
+ return 0;
+ }
+ }
+ /* no one after the first zero -> valid */
+ return 1;
+}
+
+/**
+ * Ascii internet address interpretation routine.
+ * The value returned is in network order.
+ *
+ * @param cp IP address in ascii representation (e.g. "127.0.0.1")
+ * @return ip address in network order
+ */
+u32_t
+ipaddr_addr(const char *cp)
+{
+ ip4_addr_t val;
+
+ if (ip4addr_aton(cp, &val)) {
+ return ip4_addr_get_u32(&val);
+ }
+ return (IPADDR_NONE);
+}
+
+/**
+ * Check whether "cp" is a valid ascii representation
+ * of an Internet address and convert to a binary address.
+ * Returns 1 if the address is valid, 0 if not.
+ * This replaces inet_addr, the return value from which
+ * cannot distinguish between failure and a local broadcast address.
+ *
+ * @param cp IP address in ascii representation (e.g. "127.0.0.1")
+ * @param addr pointer to which to save the ip address in network order
+ * @return 1 if cp could be converted to addr, 0 on failure
+ */
+int
+ip4addr_aton(const char *cp, ip4_addr_t *addr)
+{
+ u32_t val;
+ u8_t base;
+ char c;
+ u32_t parts[4];
+ u32_t *pp = parts;
+
+ c = *cp;
+ for (;;) {
+ /*
+ * Collect number up to ``.''.
+ * Values are specified as for C:
+ * 0x=hex, 0=octal, 1-9=decimal.
+ */
+ if (!lwip_isdigit(c)) {
+ return 0;
+ }
+ val = 0;
+ base = 10;
+ if (c == '0') {
+ c = *++cp;
+ if (c == 'x' || c == 'X') {
+ base = 16;
+ c = *++cp;
+ } else {
+ base = 8;
+ }
+ }
+ for (;;) {
+ if (lwip_isdigit(c)) {
+ val = (val * base) + (u32_t)(c - '0');
+ c = *++cp;
+ } else if (base == 16 && lwip_isxdigit(c)) {
+ val = (val << 4) | (u32_t)(c + 10 - (lwip_islower(c) ? 'a' : 'A'));
+ c = *++cp;
+ } else {
+ break;
+ }
+ }
+ if (c == '.') {
+ /*
+ * Internet format:
+ * a.b.c.d
+ * a.b.c (with c treated as 16 bits)
+ * a.b (with b treated as 24 bits)
+ */
+ if (pp >= parts + 3) {
+ return 0;
+ }
+ *pp++ = val;
+ c = *++cp;
+ } else {
+ break;
+ }
+ }
+ /*
+ * Check for trailing characters.
+ */
+ if (c != '\0' && !lwip_isspace(c)) {
+ return 0;
+ }
+ /*
+ * Concoct the address according to
+ * the number of parts specified.
+ */
+ switch (pp - parts + 1) {
+
+ case 0:
+ return 0; /* initial nondigit */
+
+ case 1: /* a -- 32 bits */
+ break;
+
+ case 2: /* a.b -- 8.24 bits */
+ if (val > 0xffffffUL) {
+ return 0;
+ }
+ if (parts[0] > 0xff) {
+ return 0;
+ }
+ val |= parts[0] << 24;
+ break;
+
+ case 3: /* a.b.c -- 8.8.16 bits */
+ if (val > 0xffff) {
+ return 0;
+ }
+ if ((parts[0] > 0xff) || (parts[1] > 0xff)) {
+ return 0;
+ }
+ val |= (parts[0] << 24) | (parts[1] << 16);
+ break;
+
+ case 4: /* a.b.c.d -- 8.8.8.8 bits */
+ if (val > 0xff) {
+ return 0;
+ }
+ if ((parts[0] > 0xff) || (parts[1] > 0xff) || (parts[2] > 0xff)) {
+ return 0;
+ }
+ val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
+ break;
+ default:
+ LWIP_ASSERT("unhandled", 0);
+ break;
+ }
+ if (addr) {
+ ip4_addr_set_u32(addr, lwip_htonl(val));
+ }
+ return 1;
+}
+
+/**
+ * Convert numeric IP address into decimal dotted ASCII representation.
+ * returns ptr to static buffer; not reentrant!
+ *
+ * @param addr ip address in network order to convert
+ * @return pointer to a global static (!) buffer that holds the ASCII
+ * representation of addr
+ */
+char *
+ip4addr_ntoa(const ip4_addr_t *addr)
+{
+ static char str[IP4ADDR_STRLEN_MAX];
+ return ip4addr_ntoa_r(addr, str, IP4ADDR_STRLEN_MAX);
+}
+
+/**
+ * Same as ip4addr_ntoa, but reentrant since a user-supplied buffer is used.
+ *
+ * @param addr ip address in network order to convert
+ * @param buf target buffer where the string is stored
+ * @param buflen length of buf
+ * @return either pointer to buf which now holds the ASCII
+ * representation of addr or NULL if buf was too small
+ */
+char *
+ip4addr_ntoa_r(const ip4_addr_t *addr, char *buf, int buflen)
+{
+ u32_t s_addr;
+ char inv[3];
+ char *rp;
+ u8_t *ap;
+ u8_t rem;
+ u8_t n;
+ u8_t i;
+ int len = 0;
+
+ s_addr = ip4_addr_get_u32(addr);
+
+ rp = buf;
+ ap = (u8_t *)&s_addr;
+ for (n = 0; n < 4; n++) {
+ i = 0;
+ do {
+ rem = *ap % (u8_t)10;
+ *ap /= (u8_t)10;
+ inv[i++] = (char)('0' + rem);
+ } while (*ap);
+ while (i--) {
+ if (len++ >= buflen) {
+ return NULL;
+ }
+ *rp++ = inv[i];
+ }
+ if (len++ >= buflen) {
+ return NULL;
+ }
+ *rp++ = '.';
+ ap++;
+ }
+ *--rp = 0;
+ return buf;
+}
+
+#endif /* LWIP_IPV4 */
diff --git a/resources/ethernet/LwIP/core/ipv4/ip4_frag.c b/resources/ethernet/LwIP/core/ipv4/ip4_frag.c
new file mode 100644
index 00000000..b2fd14c5
--- /dev/null
+++ b/resources/ethernet/LwIP/core/ipv4/ip4_frag.c
@@ -0,0 +1,894 @@
+/**
+ * @file
+ * This is the IPv4 packet segmentation and reassembly implementation.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Jani Monoses
+ * Simon Goldschmidt
+ * original reassembly code by Adam Dunkels
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV4
+
+#include "lwip/ip4_frag.h"
+#include "lwip/def.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/netif.h"
+#include "lwip/stats.h"
+#include "lwip/icmp.h"
+
+#include
+
+#if IP_REASSEMBLY
+/**
+ * The IP reassembly code currently has the following limitations:
+ * - IP header options are not supported
+ * - fragments must not overlap (e.g. due to different routes),
+ * currently, overlapping or duplicate fragments are thrown away
+ * if IP_REASS_CHECK_OVERLAP=1 (the default)!
+ *
+ * @todo: work with IP header options
+ */
+
+/** Setting this to 0, you can turn off checking the fragments for overlapping
+ * regions. The code gets a little smaller. Only use this if you know that
+ * overlapping won't occur on your network! */
+#ifndef IP_REASS_CHECK_OVERLAP
+#define IP_REASS_CHECK_OVERLAP 1
+#endif /* IP_REASS_CHECK_OVERLAP */
+
+/** Set to 0 to prevent freeing the oldest datagram when the reassembly buffer is
+ * full (IP_REASS_MAX_PBUFS pbufs are enqueued). The code gets a little smaller.
+ * Datagrams will be freed by timeout only. Especially useful when MEMP_NUM_REASSDATA
+ * is set to 1, so one datagram can be reassembled at a time, only. */
+#ifndef IP_REASS_FREE_OLDEST
+#define IP_REASS_FREE_OLDEST 1
+#endif /* IP_REASS_FREE_OLDEST */
+
+#define IP_REASS_FLAG_LASTFRAG 0x01
+
+#define IP_REASS_VALIDATE_TELEGRAM_FINISHED 1
+#define IP_REASS_VALIDATE_PBUF_QUEUED 0
+#define IP_REASS_VALIDATE_PBUF_DROPPED -1
+
+/** This is a helper struct which holds the starting
+ * offset and the ending offset of this fragment to
+ * easily chain the fragments.
+ * It has the same packing requirements as the IP header, since it replaces
+ * the IP header in memory in incoming fragments (after copying it) to keep
+ * track of the various fragments. (-> If the IP header doesn't need packing,
+ * this struct doesn't need packing, too.)
+ */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip_reass_helper {
+ PACK_STRUCT_FIELD(struct pbuf *next_pbuf);
+ PACK_STRUCT_FIELD(u16_t start);
+ PACK_STRUCT_FIELD(u16_t end);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+#define IP_ADDRESSES_AND_ID_MATCH(iphdrA, iphdrB) \
+ (ip4_addr_cmp(&(iphdrA)->src, &(iphdrB)->src) && \
+ ip4_addr_cmp(&(iphdrA)->dest, &(iphdrB)->dest) && \
+ IPH_ID(iphdrA) == IPH_ID(iphdrB)) ? 1 : 0
+
+/* global variables */
+static struct ip_reassdata *reassdatagrams;
+static u16_t ip_reass_pbufcount;
+
+/* function prototypes */
+static void ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev);
+static int ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev);
+
+/**
+ * Reassembly timer base function
+ * for both NO_SYS == 0 and 1 (!).
+ *
+ * Should be called every 1000 msec (defined by IP_TMR_INTERVAL).
+ */
+void
+ip_reass_tmr(void)
+{
+ struct ip_reassdata *r, *prev = NULL;
+
+ r = reassdatagrams;
+ while (r != NULL) {
+ /* Decrement the timer. Once it reaches 0,
+ * clean up the incomplete fragment assembly */
+ if (r->timer > 0) {
+ r->timer--;
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer dec %"U16_F"\n", (u16_t)r->timer));
+ prev = r;
+ r = r->next;
+ } else {
+ /* reassembly timed out */
+ struct ip_reassdata *tmp;
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer timed out\n"));
+ tmp = r;
+ /* get the next pointer before freeing */
+ r = r->next;
+ /* free the helper struct and all enqueued pbufs */
+ ip_reass_free_complete_datagram(tmp, prev);
+ }
+ }
+}
+
+/**
+ * Free a datagram (struct ip_reassdata) and all its pbufs.
+ * Updates the total count of enqueued pbufs (ip_reass_pbufcount),
+ * SNMP counters and sends an ICMP time exceeded packet.
+ *
+ * @param ipr datagram to free
+ * @param prev the previous datagram in the linked list
+ * @return the number of pbufs freed
+ */
+static int
+ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
+{
+ u16_t pbufs_freed = 0;
+ u16_t clen;
+ struct pbuf *p;
+ struct ip_reass_helper *iprh;
+
+ LWIP_ASSERT("prev != ipr", prev != ipr);
+ if (prev != NULL) {
+ LWIP_ASSERT("prev->next == ipr", prev->next == ipr);
+ }
+
+ MIB2_STATS_INC(mib2.ipreasmfails);
+#if LWIP_ICMP
+ iprh = (struct ip_reass_helper *)ipr->p->payload;
+ if (iprh->start == 0) {
+ /* The first fragment was received, send ICMP time exceeded. */
+ /* First, de-queue the first pbuf from r->p. */
+ p = ipr->p;
+ ipr->p = iprh->next_pbuf;
+ /* Then, copy the original header into it. */
+ SMEMCPY(p->payload, &ipr->iphdr, IP_HLEN);
+ icmp_time_exceeded(p, ICMP_TE_FRAG);
+ clen = pbuf_clen(p);
+ LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
+ pbufs_freed = (u16_t)(pbufs_freed + clen);
+ pbuf_free(p);
+ }
+#endif /* LWIP_ICMP */
+
+ /* First, free all received pbufs. The individual pbufs need to be released
+ separately as they have not yet been chained */
+ p = ipr->p;
+ while (p != NULL) {
+ struct pbuf *pcur;
+ iprh = (struct ip_reass_helper *)p->payload;
+ pcur = p;
+ /* get the next pointer before freeing */
+ p = iprh->next_pbuf;
+ clen = pbuf_clen(pcur);
+ LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
+ pbufs_freed = (u16_t)(pbufs_freed + clen);
+ pbuf_free(pcur);
+ }
+ /* Then, unchain the struct ip_reassdata from the list and free it. */
+ ip_reass_dequeue_datagram(ipr, prev);
+ LWIP_ASSERT("ip_reass_pbufcount >= pbufs_freed", ip_reass_pbufcount >= pbufs_freed);
+ ip_reass_pbufcount = (u16_t)(ip_reass_pbufcount - pbufs_freed);
+
+ return pbufs_freed;
+}
+
+#if IP_REASS_FREE_OLDEST
+/**
+ * Free the oldest datagram to make room for enqueueing new fragments.
+ * The datagram 'fraghdr' belongs to is not freed!
+ *
+ * @param fraghdr IP header of the current fragment
+ * @param pbufs_needed number of pbufs needed to enqueue
+ * (used for freeing other datagrams if not enough space)
+ * @return the number of pbufs freed
+ */
+static int
+ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed)
+{
+ /* @todo Can't we simply remove the last datagram in the
+ * linked list behind reassdatagrams?
+ */
+ struct ip_reassdata *r, *oldest, *prev, *oldest_prev;
+ int pbufs_freed = 0, pbufs_freed_current;
+ int other_datagrams;
+
+ /* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs,
+ * but don't free the datagram that 'fraghdr' belongs to! */
+ do {
+ oldest = NULL;
+ prev = NULL;
+ oldest_prev = NULL;
+ other_datagrams = 0;
+ r = reassdatagrams;
+ while (r != NULL) {
+ if (!IP_ADDRESSES_AND_ID_MATCH(&r->iphdr, fraghdr)) {
+ /* Not the same datagram as fraghdr */
+ other_datagrams++;
+ if (oldest == NULL) {
+ oldest = r;
+ oldest_prev = prev;
+ } else if (r->timer <= oldest->timer) {
+ /* older than the previous oldest */
+ oldest = r;
+ oldest_prev = prev;
+ }
+ }
+ if (r->next != NULL) {
+ prev = r;
+ }
+ r = r->next;
+ }
+ if (oldest != NULL) {
+ pbufs_freed_current = ip_reass_free_complete_datagram(oldest, oldest_prev);
+ pbufs_freed += pbufs_freed_current;
+ }
+ } while ((pbufs_freed < pbufs_needed) && (other_datagrams > 1));
+ return pbufs_freed;
+}
+#endif /* IP_REASS_FREE_OLDEST */
+
+/**
+ * Enqueues a new fragment into the fragment queue
+ * @param fraghdr points to the new fragments IP hdr
+ * @param clen number of pbufs needed to enqueue (used for freeing other datagrams if not enough space)
+ * @return A pointer to the queue location into which the fragment was enqueued
+ */
+static struct ip_reassdata *
+ip_reass_enqueue_new_datagram(struct ip_hdr *fraghdr, int clen)
+{
+ struct ip_reassdata *ipr;
+#if ! IP_REASS_FREE_OLDEST
+ LWIP_UNUSED_ARG(clen);
+#endif
+
+ /* No matching previous fragment found, allocate a new reassdata struct */
+ ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA);
+ if (ipr == NULL) {
+#if IP_REASS_FREE_OLDEST
+ if (ip_reass_remove_oldest_datagram(fraghdr, clen) >= clen) {
+ ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA);
+ }
+ if (ipr == NULL)
+#endif /* IP_REASS_FREE_OLDEST */
+ {
+ IPFRAG_STATS_INC(ip_frag.memerr);
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("Failed to alloc reassdata struct\n"));
+ return NULL;
+ }
+ }
+ memset(ipr, 0, sizeof(struct ip_reassdata));
+ ipr->timer = IP_REASS_MAXAGE;
+
+ /* enqueue the new structure to the front of the list */
+ ipr->next = reassdatagrams;
+ reassdatagrams = ipr;
+ /* copy the ip header for later tests and input */
+ /* @todo: no ip options supported? */
+ SMEMCPY(&(ipr->iphdr), fraghdr, IP_HLEN);
+ return ipr;
+}
+
+/**
+ * Dequeues a datagram from the datagram queue. Doesn't deallocate the pbufs.
+ * @param ipr points to the queue entry to dequeue
+ */
+static void
+ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
+{
+ /* dequeue the reass struct */
+ if (reassdatagrams == ipr) {
+ /* it was the first in the list */
+ reassdatagrams = ipr->next;
+ } else {
+ /* it wasn't the first, so it must have a valid 'prev' */
+ LWIP_ASSERT("sanity check linked list", prev != NULL);
+ prev->next = ipr->next;
+ }
+
+ /* now we can free the ip_reassdata struct */
+ memp_free(MEMP_REASSDATA, ipr);
+}
+
+/**
+ * Chain a new pbuf into the pbuf list that composes the datagram. The pbuf list
+ * will grow over time as new pbufs are rx.
+ * Also checks that the datagram passes basic continuity checks (if the last
+ * fragment was received at least once).
+ * @param ipr points to the reassembly state
+ * @param new_p points to the pbuf for the current fragment
+ * @param is_last is 1 if this pbuf has MF==0 (ipr->flags not updated yet)
+ * @return see IP_REASS_VALIDATE_* defines
+ */
+static int
+ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct pbuf *new_p, int is_last)
+{
+ struct ip_reass_helper *iprh, *iprh_tmp, *iprh_prev = NULL;
+ struct pbuf *q;
+ u16_t offset, len;
+ u8_t hlen;
+ struct ip_hdr *fraghdr;
+ int valid = 1;
+
+ /* Extract length and fragment offset from current fragment */
+ fraghdr = (struct ip_hdr *)new_p->payload;
+ len = lwip_ntohs(IPH_LEN(fraghdr));
+ hlen = IPH_HL_BYTES(fraghdr);
+ if (hlen > len) {
+ /* invalid datagram */
+ return IP_REASS_VALIDATE_PBUF_DROPPED;
+ }
+ len = (u16_t)(len - hlen);
+ offset = IPH_OFFSET_BYTES(fraghdr);
+
+ /* overwrite the fragment's ip header from the pbuf with our helper struct,
+ * and setup the embedded helper structure. */
+ /* make sure the struct ip_reass_helper fits into the IP header */
+ LWIP_ASSERT("sizeof(struct ip_reass_helper) <= IP_HLEN",
+ sizeof(struct ip_reass_helper) <= IP_HLEN);
+ iprh = (struct ip_reass_helper *)new_p->payload;
+ iprh->next_pbuf = NULL;
+ iprh->start = offset;
+ iprh->end = (u16_t)(offset + len);
+ if (iprh->end < offset) {
+ /* u16_t overflow, cannot handle this */
+ return IP_REASS_VALIDATE_PBUF_DROPPED;
+ }
+
+ /* Iterate through until we either get to the end of the list (append),
+ * or we find one with a larger offset (insert). */
+ for (q = ipr->p; q != NULL;) {
+ iprh_tmp = (struct ip_reass_helper *)q->payload;
+ if (iprh->start < iprh_tmp->start) {
+ /* the new pbuf should be inserted before this */
+ iprh->next_pbuf = q;
+ if (iprh_prev != NULL) {
+ /* not the fragment with the lowest offset */
+#if IP_REASS_CHECK_OVERLAP
+ if ((iprh->start < iprh_prev->end) || (iprh->end > iprh_tmp->start)) {
+ /* fragment overlaps with previous or following, throw away */
+ return IP_REASS_VALIDATE_PBUF_DROPPED;
+ }
+#endif /* IP_REASS_CHECK_OVERLAP */
+ iprh_prev->next_pbuf = new_p;
+ if (iprh_prev->end != iprh->start) {
+ /* There is a fragment missing between the current
+ * and the previous fragment */
+ valid = 0;
+ }
+ } else {
+#if IP_REASS_CHECK_OVERLAP
+ if (iprh->end > iprh_tmp->start) {
+ /* fragment overlaps with following, throw away */
+ return IP_REASS_VALIDATE_PBUF_DROPPED;
+ }
+#endif /* IP_REASS_CHECK_OVERLAP */
+ /* fragment with the lowest offset */
+ ipr->p = new_p;
+ }
+ break;
+ } else if (iprh->start == iprh_tmp->start) {
+ /* received the same datagram twice: no need to keep the datagram */
+ return IP_REASS_VALIDATE_PBUF_DROPPED;
+#if IP_REASS_CHECK_OVERLAP
+ } else if (iprh->start < iprh_tmp->end) {
+ /* overlap: no need to keep the new datagram */
+ return IP_REASS_VALIDATE_PBUF_DROPPED;
+#endif /* IP_REASS_CHECK_OVERLAP */
+ } else {
+ /* Check if the fragments received so far have no holes. */
+ if (iprh_prev != NULL) {
+ if (iprh_prev->end != iprh_tmp->start) {
+ /* There is a fragment missing between the current
+ * and the previous fragment */
+ valid = 0;
+ }
+ }
+ }
+ q = iprh_tmp->next_pbuf;
+ iprh_prev = iprh_tmp;
+ }
+
+ /* If q is NULL, then we made it to the end of the list. Determine what to do now */
+ if (q == NULL) {
+ if (iprh_prev != NULL) {
+ /* this is (for now), the fragment with the highest offset:
+ * chain it to the last fragment */
+#if IP_REASS_CHECK_OVERLAP
+ LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start);
+#endif /* IP_REASS_CHECK_OVERLAP */
+ iprh_prev->next_pbuf = new_p;
+ if (iprh_prev->end != iprh->start) {
+ valid = 0;
+ }
+ } else {
+#if IP_REASS_CHECK_OVERLAP
+ LWIP_ASSERT("no previous fragment, this must be the first fragment!",
+ ipr->p == NULL);
+#endif /* IP_REASS_CHECK_OVERLAP */
+ /* this is the first fragment we ever received for this ip datagram */
+ ipr->p = new_p;
+ }
+ }
+
+ /* At this point, the validation part begins: */
+ /* If we already received the last fragment */
+ if (is_last || ((ipr->flags & IP_REASS_FLAG_LASTFRAG) != 0)) {
+ /* and had no holes so far */
+ if (valid) {
+ /* then check if the rest of the fragments is here */
+ /* Check if the queue starts with the first datagram */
+ if ((ipr->p == NULL) || (((struct ip_reass_helper *)ipr->p->payload)->start != 0)) {
+ valid = 0;
+ } else {
+ /* and check that there are no holes after this datagram */
+ iprh_prev = iprh;
+ q = iprh->next_pbuf;
+ while (q != NULL) {
+ iprh = (struct ip_reass_helper *)q->payload;
+ if (iprh_prev->end != iprh->start) {
+ valid = 0;
+ break;
+ }
+ iprh_prev = iprh;
+ q = iprh->next_pbuf;
+ }
+ /* if still valid, all fragments are received
+ * (because to the MF==0 already arrived */
+ if (valid) {
+ LWIP_ASSERT("sanity check", ipr->p != NULL);
+ LWIP_ASSERT("sanity check",
+ ((struct ip_reass_helper *)ipr->p->payload) != iprh);
+ LWIP_ASSERT("validate_datagram:next_pbuf!=NULL",
+ iprh->next_pbuf == NULL);
+ }
+ }
+ }
+ /* If valid is 0 here, there are some fragments missing in the middle
+ * (since MF == 0 has already arrived). Such datagrams simply time out if
+ * no more fragments are received... */
+ return valid ? IP_REASS_VALIDATE_TELEGRAM_FINISHED : IP_REASS_VALIDATE_PBUF_QUEUED;
+ }
+ /* If we come here, not all fragments were received, yet! */
+ return IP_REASS_VALIDATE_PBUF_QUEUED; /* not yet valid! */
+}
+
+/**
+ * Reassembles incoming IP fragments into an IP datagram.
+ *
+ * @param p points to a pbuf chain of the fragment
+ * @return NULL if reassembly is incomplete, ? otherwise
+ */
+struct pbuf *
+ip4_reass(struct pbuf *p)
+{
+ struct pbuf *r;
+ struct ip_hdr *fraghdr;
+ struct ip_reassdata *ipr;
+ struct ip_reass_helper *iprh;
+ u16_t offset, len, clen;
+ u8_t hlen;
+ int valid;
+ int is_last;
+
+ IPFRAG_STATS_INC(ip_frag.recv);
+ MIB2_STATS_INC(mib2.ipreasmreqds);
+
+ fraghdr = (struct ip_hdr *)p->payload;
+
+ if (IPH_HL_BYTES(fraghdr) != IP_HLEN) {
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("ip4_reass: IP options currently not supported!\n"));
+ IPFRAG_STATS_INC(ip_frag.err);
+ goto nullreturn;
+ }
+
+ offset = IPH_OFFSET_BYTES(fraghdr);
+ len = lwip_ntohs(IPH_LEN(fraghdr));
+ hlen = IPH_HL_BYTES(fraghdr);
+ if (hlen > len) {
+ /* invalid datagram */
+ goto nullreturn;
+ }
+ len = (u16_t)(len - hlen);
+
+ /* Check if we are allowed to enqueue more datagrams. */
+ clen = pbuf_clen(p);
+ if ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) {
+#if IP_REASS_FREE_OLDEST
+ if (!ip_reass_remove_oldest_datagram(fraghdr, clen) ||
+ ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS))
+#endif /* IP_REASS_FREE_OLDEST */
+ {
+ /* No datagram could be freed and still too many pbufs enqueued */
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("ip4_reass: Overflow condition: pbufct=%d, clen=%d, MAX=%d\n",
+ ip_reass_pbufcount, clen, IP_REASS_MAX_PBUFS));
+ IPFRAG_STATS_INC(ip_frag.memerr);
+ /* @todo: send ICMP time exceeded here? */
+ /* drop this pbuf */
+ goto nullreturn;
+ }
+ }
+
+ /* Look for the datagram the fragment belongs to in the current datagram queue,
+ * remembering the previous in the queue for later dequeueing. */
+ for (ipr = reassdatagrams; ipr != NULL; ipr = ipr->next) {
+ /* Check if the incoming fragment matches the one currently present
+ in the reassembly buffer. If so, we proceed with copying the
+ fragment into the buffer. */
+ if (IP_ADDRESSES_AND_ID_MATCH(&ipr->iphdr, fraghdr)) {
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("ip4_reass: matching previous fragment ID=%"X16_F"\n",
+ lwip_ntohs(IPH_ID(fraghdr))));
+ IPFRAG_STATS_INC(ip_frag.cachehit);
+ break;
+ }
+ }
+
+ if (ipr == NULL) {
+ /* Enqueue a new datagram into the datagram queue */
+ ipr = ip_reass_enqueue_new_datagram(fraghdr, clen);
+ /* Bail if unable to enqueue */
+ if (ipr == NULL) {
+ goto nullreturn;
+ }
+ } else {
+ if (((lwip_ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) == 0) &&
+ ((lwip_ntohs(IPH_OFFSET(&ipr->iphdr)) & IP_OFFMASK) != 0)) {
+ /* ipr->iphdr is not the header from the first fragment, but fraghdr is
+ * -> copy fraghdr into ipr->iphdr since we want to have the header
+ * of the first fragment (for ICMP time exceeded and later, for copying
+ * all options, if supported)*/
+ SMEMCPY(&ipr->iphdr, fraghdr, IP_HLEN);
+ }
+ }
+
+ /* At this point, we have either created a new entry or pointing
+ * to an existing one */
+
+ /* check for 'no more fragments', and update queue entry*/
+ is_last = (IPH_OFFSET(fraghdr) & PP_NTOHS(IP_MF)) == 0;
+ if (is_last) {
+ u16_t datagram_len = (u16_t)(offset + len);
+ if ((datagram_len < offset) || (datagram_len > (0xFFFF - IP_HLEN))) {
+ /* u16_t overflow, cannot handle this */
+ goto nullreturn_ipr;
+ }
+ }
+ /* find the right place to insert this pbuf */
+ /* @todo: trim pbufs if fragments are overlapping */
+ valid = ip_reass_chain_frag_into_datagram_and_validate(ipr, p, is_last);
+ if (valid == IP_REASS_VALIDATE_PBUF_DROPPED) {
+ goto nullreturn_ipr;
+ }
+ /* if we come here, the pbuf has been enqueued */
+
+ /* Track the current number of pbufs current 'in-flight', in order to limit
+ the number of fragments that may be enqueued at any one time
+ (overflow checked by testing against IP_REASS_MAX_PBUFS) */
+ ip_reass_pbufcount = (u16_t)(ip_reass_pbufcount + clen);
+ if (is_last) {
+ u16_t datagram_len = (u16_t)(offset + len);
+ ipr->datagram_len = datagram_len;
+ ipr->flags |= IP_REASS_FLAG_LASTFRAG;
+ LWIP_DEBUGF(IP_REASS_DEBUG,
+ ("ip4_reass: last fragment seen, total len %"S16_F"\n",
+ ipr->datagram_len));
+ }
+
+ if (valid == IP_REASS_VALIDATE_TELEGRAM_FINISHED) {
+ struct ip_reassdata *ipr_prev;
+ /* the totally last fragment (flag more fragments = 0) was received at least
+ * once AND all fragments are received */
+ u16_t datagram_len = (u16_t)(ipr->datagram_len + IP_HLEN);
+
+ /* save the second pbuf before copying the header over the pointer */
+ r = ((struct ip_reass_helper *)ipr->p->payload)->next_pbuf;
+
+ /* copy the original ip header back to the first pbuf */
+ fraghdr = (struct ip_hdr *)(ipr->p->payload);
+ SMEMCPY(fraghdr, &ipr->iphdr, IP_HLEN);
+ IPH_LEN_SET(fraghdr, lwip_htons(datagram_len));
+ IPH_OFFSET_SET(fraghdr, 0);
+ IPH_CHKSUM_SET(fraghdr, 0);
+ /* @todo: do we need to set/calculate the correct checksum? */
+#if CHECKSUM_GEN_IP
+ IF__NETIF_CHECKSUM_ENABLED(ip_current_input_netif(), NETIF_CHECKSUM_GEN_IP) {
+ IPH_CHKSUM_SET(fraghdr, inet_chksum(fraghdr, IP_HLEN));
+ }
+#endif /* CHECKSUM_GEN_IP */
+
+ p = ipr->p;
+
+ /* chain together the pbufs contained within the reass_data list. */
+ while (r != NULL) {
+ iprh = (struct ip_reass_helper *)r->payload;
+
+ /* hide the ip header for every succeeding fragment */
+ pbuf_remove_header(r, IP_HLEN);
+ pbuf_cat(p, r);
+ r = iprh->next_pbuf;
+ }
+
+ /* find the previous entry in the linked list */
+ if (ipr == reassdatagrams) {
+ ipr_prev = NULL;
+ } else {
+ for (ipr_prev = reassdatagrams; ipr_prev != NULL; ipr_prev = ipr_prev->next) {
+ if (ipr_prev->next == ipr) {
+ break;
+ }
+ }
+ }
+
+ /* release the sources allocate for the fragment queue entry */
+ ip_reass_dequeue_datagram(ipr, ipr_prev);
+
+ /* and adjust the number of pbufs currently queued for reassembly. */
+ clen = pbuf_clen(p);
+ LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= clen);
+ ip_reass_pbufcount = (u16_t)(ip_reass_pbufcount - clen);
+
+ MIB2_STATS_INC(mib2.ipreasmoks);
+
+ /* Return the pbuf chain */
+ return p;
+ }
+ /* the datagram is not (yet?) reassembled completely */
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_pbufcount: %d out\n", ip_reass_pbufcount));
+ return NULL;
+
+nullreturn_ipr:
+ LWIP_ASSERT("ipr != NULL", ipr != NULL);
+ if (ipr->p == NULL) {
+ /* dropped pbuf after creating a new datagram entry: remove the entry, too */
+ LWIP_ASSERT("not firstalthough just enqueued", ipr == reassdatagrams);
+ ip_reass_dequeue_datagram(ipr, NULL);
+ }
+
+nullreturn:
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("ip4_reass: nullreturn\n"));
+ IPFRAG_STATS_INC(ip_frag.drop);
+ pbuf_free(p);
+ return NULL;
+}
+#endif /* IP_REASSEMBLY */
+
+#if IP_FRAG
+#if !LWIP_NETIF_TX_SINGLE_PBUF
+/** Allocate a new struct pbuf_custom_ref */
+static struct pbuf_custom_ref *
+ip_frag_alloc_pbuf_custom_ref(void)
+{
+ return (struct pbuf_custom_ref *)memp_malloc(MEMP_FRAG_PBUF);
+}
+
+/** Free a struct pbuf_custom_ref */
+static void
+ip_frag_free_pbuf_custom_ref(struct pbuf_custom_ref *p)
+{
+ LWIP_ASSERT("p != NULL", p != NULL);
+ memp_free(MEMP_FRAG_PBUF, p);
+}
+
+/** Free-callback function to free a 'struct pbuf_custom_ref', called by
+ * pbuf_free. */
+static void
+ipfrag_free_pbuf_custom(struct pbuf *p)
+{
+ struct pbuf_custom_ref *pcr = (struct pbuf_custom_ref *)p;
+ LWIP_ASSERT("pcr != NULL", pcr != NULL);
+ LWIP_ASSERT("pcr == p", (void *)pcr == (void *)p);
+ if (pcr->original != NULL) {
+ pbuf_free(pcr->original);
+ }
+ ip_frag_free_pbuf_custom_ref(pcr);
+}
+#endif /* !LWIP_NETIF_TX_SINGLE_PBUF */
+
+/**
+ * Fragment an IP datagram if too large for the netif.
+ *
+ * Chop the datagram in MTU sized chunks and send them in order
+ * by pointing PBUF_REFs into p.
+ *
+ * @param p ip packet to send
+ * @param netif the netif on which to send
+ * @param dest destination ip address to which to send
+ *
+ * @return ERR_OK if sent successfully, err_t otherwise
+ */
+err_t
+ip4_frag(struct pbuf *p, struct netif *netif, const ip4_addr_t *dest)
+{
+ struct pbuf *rambuf;
+#if !LWIP_NETIF_TX_SINGLE_PBUF
+ struct pbuf *newpbuf;
+ u16_t newpbuflen = 0;
+ u16_t left_to_copy;
+#endif
+ struct ip_hdr *original_iphdr;
+ struct ip_hdr *iphdr;
+ const u16_t nfb = (u16_t)((netif->mtu - IP_HLEN) / 8);
+ u16_t left, fragsize;
+ u16_t ofo;
+ int last;
+ u16_t poff = IP_HLEN;
+ u16_t tmp;
+ int mf_set;
+
+ original_iphdr = (struct ip_hdr *)p->payload;
+ iphdr = original_iphdr;
+ if (IPH_HL_BYTES(iphdr) != IP_HLEN) {
+ /* ip4_frag() does not support IP options */
+ return ERR_VAL;
+ }
+ LWIP_ERROR("ip4_frag(): pbuf too short", p->len >= IP_HLEN, return ERR_VAL);
+
+ /* Save original offset */
+ tmp = lwip_ntohs(IPH_OFFSET(iphdr));
+ ofo = tmp & IP_OFFMASK;
+ /* already fragmented? if so, the last fragment we create must have MF, too */
+ mf_set = tmp & IP_MF;
+
+ left = (u16_t)(p->tot_len - IP_HLEN);
+
+ while (left) {
+ /* Fill this fragment */
+ fragsize = LWIP_MIN(left, (u16_t)(nfb * 8));
+
+#if LWIP_NETIF_TX_SINGLE_PBUF
+ rambuf = pbuf_alloc(PBUF_IP, fragsize, PBUF_RAM);
+ if (rambuf == NULL) {
+ goto memerr;
+ }
+ LWIP_ASSERT("this needs a pbuf in one piece!",
+ (rambuf->len == rambuf->tot_len) && (rambuf->next == NULL));
+ poff += pbuf_copy_partial(p, rambuf->payload, fragsize, poff);
+ /* make room for the IP header */
+ if (pbuf_add_header(rambuf, IP_HLEN)) {
+ pbuf_free(rambuf);
+ goto memerr;
+ }
+ /* fill in the IP header */
+ SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN);
+ iphdr = (struct ip_hdr *)rambuf->payload;
+#else /* LWIP_NETIF_TX_SINGLE_PBUF */
+ /* When not using a static buffer, create a chain of pbufs.
+ * The first will be a PBUF_RAM holding the link and IP header.
+ * The rest will be PBUF_REFs mirroring the pbuf chain to be fragged,
+ * but limited to the size of an mtu.
+ */
+ rambuf = pbuf_alloc(PBUF_LINK, IP_HLEN, PBUF_RAM);
+ if (rambuf == NULL) {
+ goto memerr;
+ }
+ LWIP_ASSERT("this needs a pbuf in one piece!",
+ (rambuf->len >= (IP_HLEN)));
+ SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN);
+ iphdr = (struct ip_hdr *)rambuf->payload;
+
+ left_to_copy = fragsize;
+ while (left_to_copy) {
+ struct pbuf_custom_ref *pcr;
+ u16_t plen = (u16_t)(p->len - poff);
+ LWIP_ASSERT("p->len >= poff", p->len >= poff);
+ newpbuflen = LWIP_MIN(left_to_copy, plen);
+ /* Is this pbuf already empty? */
+ if (!newpbuflen) {
+ poff = 0;
+ p = p->next;
+ continue;
+ }
+ pcr = ip_frag_alloc_pbuf_custom_ref();
+ if (pcr == NULL) {
+ pbuf_free(rambuf);
+ goto memerr;
+ }
+ /* Mirror this pbuf, although we might not need all of it. */
+ newpbuf = pbuf_alloced_custom(PBUF_RAW, newpbuflen, PBUF_REF, &pcr->pc,
+ (u8_t *)p->payload + poff, newpbuflen);
+ if (newpbuf == NULL) {
+ ip_frag_free_pbuf_custom_ref(pcr);
+ pbuf_free(rambuf);
+ goto memerr;
+ }
+ pbuf_ref(p);
+ pcr->original = p;
+ pcr->pc.custom_free_function = ipfrag_free_pbuf_custom;
+
+ /* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain
+ * so that it is removed when pbuf_dechain is later called on rambuf.
+ */
+ pbuf_cat(rambuf, newpbuf);
+ left_to_copy = (u16_t)(left_to_copy - newpbuflen);
+ if (left_to_copy) {
+ poff = 0;
+ p = p->next;
+ }
+ }
+ poff = (u16_t)(poff + newpbuflen);
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+
+ /* Correct header */
+ last = (left <= netif->mtu - IP_HLEN);
+
+ /* Set new offset and MF flag */
+ tmp = (IP_OFFMASK & (ofo));
+ if (!last || mf_set) {
+ /* the last fragment has MF set if the input frame had it */
+ tmp = tmp | IP_MF;
+ }
+ IPH_OFFSET_SET(iphdr, lwip_htons(tmp));
+ IPH_LEN_SET(iphdr, lwip_htons((u16_t)(fragsize + IP_HLEN)));
+ IPH_CHKSUM_SET(iphdr, 0);
+#if CHECKSUM_GEN_IP
+ IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_IP) {
+ IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
+ }
+#endif /* CHECKSUM_GEN_IP */
+
+ /* No need for separate header pbuf - we allowed room for it in rambuf
+ * when allocated.
+ */
+ netif->output(netif, rambuf, dest);
+ IPFRAG_STATS_INC(ip_frag.xmit);
+
+ /* Unfortunately we can't reuse rambuf - the hardware may still be
+ * using the buffer. Instead we free it (and the ensuing chain) and
+ * recreate it next time round the loop. If we're lucky the hardware
+ * will have already sent the packet, the free will really free, and
+ * there will be zero memory penalty.
+ */
+
+ pbuf_free(rambuf);
+ left = (u16_t)(left - fragsize);
+ ofo = (u16_t)(ofo + nfb);
+ }
+ MIB2_STATS_INC(mib2.ipfragoks);
+ return ERR_OK;
+memerr:
+ MIB2_STATS_INC(mib2.ipfragfails);
+ return ERR_MEM;
+}
+#endif /* IP_FRAG */
+
+#endif /* LWIP_IPV4 */
diff --git a/resources/ethernet/LwIP/core/ipv6/dhcp6.c b/resources/ethernet/LwIP/core/ipv6/dhcp6.c
new file mode 100644
index 00000000..1ae8742f
--- /dev/null
+++ b/resources/ethernet/LwIP/core/ipv6/dhcp6.c
@@ -0,0 +1,812 @@
+/**
+ * @file
+ *
+ * @defgroup dhcp6 DHCPv6
+ * @ingroup ip6
+ * DHCPv6 client: IPv6 address autoconfiguration as per
+ * RFC 3315 (stateful DHCPv6) and
+ * RFC 3736 (stateless DHCPv6).
+ *
+ * For now, only stateless DHCPv6 is implemented!
+ *
+ * TODO:
+ * - enable/disable API to not always start when RA is received
+ * - stateful DHCPv6 (for now, only stateless DHCPv6 for DNS and NTP servers works)
+ * - create Client Identifier?
+ * - only start requests if a valid local address is available on the netif
+ * - only start information requests if required (not for every RA)
+ *
+ * dhcp6_enable_stateful() enables stateful DHCPv6 for a netif (stateless disabled)\n
+ * dhcp6_enable_stateless() enables stateless DHCPv6 for a netif (stateful disabled)\n
+ * dhcp6_disable() disable DHCPv6 for a netif
+ *
+ * When enabled, requests are only issued after receipt of RA with the
+ * corresponding bits set.
+ */
+
+/*
+ * Copyright (c) 2018 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6 && LWIP_IPV6_DHCP6 /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/dhcp6.h"
+#include "lwip/prot/dhcp6.h"
+#include "lwip/def.h"
+#include "lwip/udp.h"
+#include "lwip/dns.h"
+
+#include
+
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+#ifndef LWIP_HOOK_DHCP6_APPEND_OPTIONS
+#define LWIP_HOOK_DHCP6_APPEND_OPTIONS(netif, dhcp6, state, msg, msg_type, options_len_ptr, max_len)
+#endif
+#ifndef LWIP_HOOK_DHCP6_PARSE_OPTION
+#define LWIP_HOOK_DHCP6_PARSE_OPTION(netif, dhcp6, state, msg, msg_type, option, len, pbuf, offset) do { LWIP_UNUSED_ARG(msg); } while(0)
+#endif
+
+#if LWIP_DNS && LWIP_DHCP6_MAX_DNS_SERVERS
+#if DNS_MAX_SERVERS > LWIP_DHCP6_MAX_DNS_SERVERS
+#define LWIP_DHCP6_PROVIDE_DNS_SERVERS LWIP_DHCP6_MAX_DNS_SERVERS
+#else
+#define LWIP_DHCP6_PROVIDE_DNS_SERVERS DNS_MAX_SERVERS
+#endif
+#else
+#define LWIP_DHCP6_PROVIDE_DNS_SERVERS 0
+#endif
+
+
+/** Option handling: options are parsed in dhcp6_parse_reply
+ * and saved in an array where other functions can load them from.
+ * This might be moved into the struct dhcp6 (not necessarily since
+ * lwIP is single-threaded and the array is only used while in recv
+ * callback). */
+enum dhcp6_option_idx {
+ DHCP6_OPTION_IDX_CLI_ID = 0,
+ DHCP6_OPTION_IDX_SERVER_ID,
+#if LWIP_DHCP6_PROVIDE_DNS_SERVERS
+ DHCP6_OPTION_IDX_DNS_SERVER,
+ DHCP6_OPTION_IDX_DOMAIN_LIST,
+#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS */
+#if LWIP_DHCP6_GET_NTP_SRV
+ DHCP6_OPTION_IDX_NTP_SERVER,
+#endif /* LWIP_DHCP_GET_NTP_SRV */
+ DHCP6_OPTION_IDX_MAX
+};
+
+struct dhcp6_option_info {
+ u8_t option_given;
+ u16_t val_start;
+ u16_t val_length;
+};
+
+/** Holds the decoded option info, only valid while in dhcp6_recv. */
+struct dhcp6_option_info dhcp6_rx_options[DHCP6_OPTION_IDX_MAX];
+
+#define dhcp6_option_given(dhcp6, idx) (dhcp6_rx_options[idx].option_given != 0)
+#define dhcp6_got_option(dhcp6, idx) (dhcp6_rx_options[idx].option_given = 1)
+#define dhcp6_clear_option(dhcp6, idx) (dhcp6_rx_options[idx].option_given = 0)
+#define dhcp6_clear_all_options(dhcp6) (memset(dhcp6_rx_options, 0, sizeof(dhcp6_rx_options)))
+#define dhcp6_get_option_start(dhcp6, idx) (dhcp6_rx_options[idx].val_start)
+#define dhcp6_get_option_length(dhcp6, idx) (dhcp6_rx_options[idx].val_length)
+#define dhcp6_set_option(dhcp6, idx, start, len) do { dhcp6_rx_options[idx].val_start = (start); dhcp6_rx_options[idx].val_length = (len); }while(0)
+
+
+const ip_addr_t dhcp6_All_DHCP6_Relay_Agents_and_Servers = IPADDR6_INIT_HOST(0xFF020000, 0, 0, 0x00010002);
+const ip_addr_t dhcp6_All_DHCP6_Servers = IPADDR6_INIT_HOST(0xFF020000, 0, 0, 0x00010003);
+
+static struct udp_pcb *dhcp6_pcb;
+static u8_t dhcp6_pcb_refcount;
+
+
+/* receive, unfold, parse and free incoming messages */
+static void dhcp6_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port);
+
+/** Ensure DHCP PCB is allocated and bound */
+static err_t
+dhcp6_inc_pcb_refcount(void)
+{
+ if (dhcp6_pcb_refcount == 0) {
+ LWIP_ASSERT("dhcp6_inc_pcb_refcount(): memory leak", dhcp6_pcb == NULL);
+
+ /* allocate UDP PCB */
+ dhcp6_pcb = udp_new_ip6();
+
+ if (dhcp6_pcb == NULL) {
+ return ERR_MEM;
+ }
+
+ ip_set_option(dhcp6_pcb, SOF_BROADCAST);
+
+ /* set up local and remote port for the pcb -> listen on all interfaces on all src/dest IPs */
+ udp_bind(dhcp6_pcb, IP6_ADDR_ANY, DHCP6_CLIENT_PORT);
+ udp_recv(dhcp6_pcb, dhcp6_recv, NULL);
+ }
+
+ dhcp6_pcb_refcount++;
+
+ return ERR_OK;
+}
+
+/** Free DHCP PCB if the last netif stops using it */
+static void
+dhcp6_dec_pcb_refcount(void)
+{
+ LWIP_ASSERT("dhcp6_pcb_refcount(): refcount error", (dhcp6_pcb_refcount > 0));
+ dhcp6_pcb_refcount--;
+
+ if (dhcp6_pcb_refcount == 0) {
+ udp_remove(dhcp6_pcb);
+ dhcp6_pcb = NULL;
+ }
+}
+
+/**
+ * @ingroup dhcp6
+ * Set a statically allocated struct dhcp6 to work with.
+ * Using this prevents dhcp6_start to allocate it using mem_malloc.
+ *
+ * @param netif the netif for which to set the struct dhcp
+ * @param dhcp6 (uninitialised) dhcp6 struct allocated by the application
+ */
+void
+dhcp6_set_struct(struct netif *netif, struct dhcp6 *dhcp6)
+{
+ LWIP_ASSERT("netif != NULL", netif != NULL);
+ LWIP_ASSERT("dhcp6 != NULL", dhcp6 != NULL);
+ LWIP_ASSERT("netif already has a struct dhcp6 set", netif_dhcp6_data(netif) == NULL);
+
+ /* clear data structure */
+ memset(dhcp6, 0, sizeof(struct dhcp6));
+ /* dhcp6_set_state(&dhcp, DHCP6_STATE_OFF); */
+ netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP6, dhcp6);
+}
+
+/**
+ * @ingroup dhcp6
+ * Removes a struct dhcp6 from a netif.
+ *
+ * ATTENTION: Only use this when not using dhcp6_set_struct() to allocate the
+ * struct dhcp6 since the memory is passed back to the heap.
+ *
+ * @param netif the netif from which to remove the struct dhcp
+ */
+void dhcp6_cleanup(struct netif *netif)
+{
+ LWIP_ASSERT("netif != NULL", netif != NULL);
+
+ if (netif_dhcp6_data(netif) != NULL) {
+ mem_free(netif_dhcp6_data(netif));
+ netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP6, NULL);
+ }
+}
+
+static struct dhcp6*
+dhcp6_get_struct(struct netif *netif, const char *dbg_requester)
+{
+ struct dhcp6 *dhcp6 = netif_dhcp6_data(netif);
+ if (dhcp6 == NULL) {
+ LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("%s: mallocing new DHCPv6 client\n", dbg_requester));
+ dhcp6 = (struct dhcp6 *)mem_malloc(sizeof(struct dhcp6));
+ if (dhcp6 == NULL) {
+ LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("%s: could not allocate dhcp6\n", dbg_requester));
+ return NULL;
+ }
+
+ /* clear data structure, this implies DHCP6_STATE_OFF */
+ memset(dhcp6, 0, sizeof(struct dhcp6));
+ /* store this dhcp6 client in the netif */
+ netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP6, dhcp6);
+ } else {
+ /* already has DHCP6 client attached */
+ LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("%s: using existing DHCPv6 client\n", dbg_requester));
+ }
+
+ if (!dhcp6->pcb_allocated) {
+ if (dhcp6_inc_pcb_refcount() != ERR_OK) { /* ensure DHCP6 PCB is allocated */
+ mem_free(dhcp6);
+ netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP6, NULL);
+ return NULL;
+ }
+ LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("%s: allocated dhcp6", dbg_requester));
+ dhcp6->pcb_allocated = 1;
+ }
+ return dhcp6;
+}
+
+/*
+ * Set the DHCPv6 state
+ * If the state changed, reset the number of tries.
+ */
+static void
+dhcp6_set_state(struct dhcp6 *dhcp6, u8_t new_state, const char *dbg_caller)
+{
+ LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("DHCPv6 state: %d -> %d (%s)\n",
+ dhcp6->state, new_state, dbg_caller));
+ if (new_state != dhcp6->state) {
+ dhcp6->state = new_state;
+ dhcp6->tries = 0;
+ dhcp6->request_timeout = 0;
+ }
+}
+
+static int
+dhcp6_stateless_enabled(struct dhcp6 *dhcp6)
+{
+ if ((dhcp6->state == DHCP6_STATE_STATELESS_IDLE) ||
+ (dhcp6->state == DHCP6_STATE_REQUESTING_CONFIG)) {
+ return 1;
+ }
+ return 0;
+}
+
+/*static int
+dhcp6_stateful_enabled(struct dhcp6 *dhcp6)
+{
+ if (dhcp6->state == DHCP6_STATE_OFF) {
+ return 0;
+ }
+ if (dhcp6_stateless_enabled(dhcp6)) {
+ return 0;
+ }
+ return 1;
+}*/
+
+/**
+ * @ingroup dhcp6
+ * Enable stateful DHCPv6 on this netif
+ * Requests are sent on receipt of an RA message with the
+ * ND6_RA_FLAG_MANAGED_ADDR_CONFIG flag set.
+ *
+ * A struct dhcp6 will be allocated for this netif if not
+ * set via @ref dhcp6_set_struct before.
+ *
+ * @todo: stateful DHCPv6 not supported, yet
+ */
+err_t
+dhcp6_enable_stateful(struct netif *netif)
+{
+ LWIP_UNUSED_ARG(netif);
+ LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("stateful dhcp6 not implemented yet"));
+ return ERR_VAL;
+}
+
+/**
+ * @ingroup dhcp6
+ * Enable stateless DHCPv6 on this netif
+ * Requests are sent on receipt of an RA message with the
+ * ND6_RA_FLAG_OTHER_CONFIG flag set.
+ *
+ * A struct dhcp6 will be allocated for this netif if not
+ * set via @ref dhcp6_set_struct before.
+ */
+err_t
+dhcp6_enable_stateless(struct netif *netif)
+{
+ struct dhcp6 *dhcp6;
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp6_enable_stateless(netif=%p) %c%c%"U16_F"\n", (void *)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+
+ dhcp6 = dhcp6_get_struct(netif, "dhcp6_enable_stateless()");
+ if (dhcp6 == NULL) {
+ return ERR_MEM;
+ }
+ if (dhcp6_stateless_enabled(dhcp6)) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_enable_stateless(): stateless DHCPv6 already enabled"));
+ return ERR_OK;
+ } else if (dhcp6->state != DHCP6_STATE_OFF) {
+ /* stateful running */
+ /* @todo: stop stateful once it is implemented */
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_enable_stateless(): switching from stateful to stateless DHCPv6"));
+ }
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_enable_stateless(): stateless DHCPv6 enabled\n"));
+ dhcp6_set_state(dhcp6, DHCP6_STATE_STATELESS_IDLE, "dhcp6_enable_stateless");
+ return ERR_OK;
+}
+
+/**
+ * @ingroup dhcp6
+ * Disable stateful or stateless DHCPv6 on this netif
+ * Requests are sent on receipt of an RA message with the
+ * ND6_RA_FLAG_OTHER_CONFIG flag set.
+ */
+void
+dhcp6_disable(struct netif *netif)
+{
+ struct dhcp6 *dhcp6;
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp6_disable(netif=%p) %c%c%"U16_F"\n", (void *)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+
+ dhcp6 = netif_dhcp6_data(netif);
+ if (dhcp6 != NULL) {
+ if (dhcp6->state != DHCP6_STATE_OFF) {
+ LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_disable(): DHCPv6 disabled (old state: %s)\n",
+ (dhcp6_stateless_enabled(dhcp6) ? "stateless" : "stateful")));
+ dhcp6_set_state(dhcp6, DHCP6_STATE_OFF, "dhcp6_disable");
+ if (dhcp6->pcb_allocated != 0) {
+ dhcp6_dec_pcb_refcount(); /* free DHCPv6 PCB if not needed any more */
+ dhcp6->pcb_allocated = 0;
+ }
+ }
+ }
+}
+
+/**
+ * Create a DHCPv6 request, fill in common headers
+ *
+ * @param netif the netif under DHCPv6 control
+ * @param dhcp6 dhcp6 control struct
+ * @param message_type message type of the request
+ * @param opt_len_alloc option length to allocate
+ * @param options_out_len option length on exit
+ * @return a pbuf for the message
+ */
+static struct pbuf *
+dhcp6_create_msg(struct netif *netif, struct dhcp6 *dhcp6, u8_t message_type,
+ u16_t opt_len_alloc, u16_t *options_out_len)
+{
+ struct pbuf *p_out;
+ struct dhcp6_msg *msg_out;
+
+ LWIP_ERROR("dhcp6_create_msg: netif != NULL", (netif != NULL), return NULL;);
+ LWIP_ERROR("dhcp6_create_msg: dhcp6 != NULL", (dhcp6 != NULL), return NULL;);
+ p_out = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcp6_msg) + opt_len_alloc, PBUF_RAM);
+ if (p_out == NULL) {
+ LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("dhcp6_create_msg(): could not allocate pbuf\n"));
+ return NULL;
+ }
+ LWIP_ASSERT("dhcp6_create_msg: check that first pbuf can hold struct dhcp6_msg",
+ (p_out->len >= sizeof(struct dhcp6_msg) + opt_len_alloc));
+
+ /* @todo: limit new xid for certain message types? */
+ /* reuse transaction identifier in retransmissions */
+ if (dhcp6->tries == 0) {
+ dhcp6->xid = LWIP_RAND() & 0xFFFFFF;
+ }
+
+ LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE,
+ ("transaction id xid(%"X32_F")\n", dhcp6->xid));
+
+ msg_out = (struct dhcp6_msg *)p_out->payload;
+ memset(msg_out, 0, sizeof(struct dhcp6_msg) + opt_len_alloc);
+
+ msg_out->msgtype = message_type;
+ msg_out->transaction_id[0] = (u8_t)(dhcp6->xid >> 16);
+ msg_out->transaction_id[1] = (u8_t)(dhcp6->xid >> 8);
+ msg_out->transaction_id[2] = (u8_t)dhcp6->xid;
+ *options_out_len = 0;
+ return p_out;
+}
+
+static u16_t
+dhcp6_option_short(u16_t options_out_len, u8_t *options, u16_t value)
+{
+ options[options_out_len++] = (u8_t)((value & 0xff00U) >> 8);
+ options[options_out_len++] = (u8_t) (value & 0x00ffU);
+ return options_out_len;
+}
+
+static u16_t
+dhcp6_option_optionrequest(u16_t options_out_len, u8_t *options, const u16_t *req_options,
+ u16_t num_req_options, u16_t max_len)
+{
+ size_t i;
+ u16_t ret;
+
+ LWIP_ASSERT("dhcp6_option_optionrequest: options_out_len + sizeof(struct dhcp6_msg) + addlen <= max_len",
+ sizeof(struct dhcp6_msg) + options_out_len + 4U + (2U * num_req_options) <= max_len);
+ LWIP_UNUSED_ARG(max_len);
+
+ ret = dhcp6_option_short(options_out_len, options, DHCP6_OPTION_ORO);
+ ret = dhcp6_option_short(ret, options, 2 * num_req_options);
+ for (i = 0; i < num_req_options; i++) {
+ ret = dhcp6_option_short(ret, options, req_options[i]);
+ }
+ return ret;
+}
+
+/* All options are added, shrink the pbuf to the required size */
+static void
+dhcp6_msg_finalize(u16_t options_out_len, struct pbuf *p_out)
+{
+ /* shrink the pbuf to the actual content length */
+ pbuf_realloc(p_out, (u16_t)(sizeof(struct dhcp6_msg) + options_out_len));
+}
+
+
+#if LWIP_IPV6_DHCP6_STATELESS
+static void
+dhcp6_information_request(struct netif *netif, struct dhcp6 *dhcp6)
+{
+ const u16_t requested_options[] = {DHCP6_OPTION_DNS_SERVERS, DHCP6_OPTION_DOMAIN_LIST, DHCP6_OPTION_SNTP_SERVERS};
+ u16_t msecs;
+ struct pbuf *p_out;
+ u16_t options_out_len;
+ LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_information_request()\n"));
+ /* create and initialize the DHCP message header */
+ p_out = dhcp6_create_msg(netif, dhcp6, DHCP6_INFOREQUEST, 4 + sizeof(requested_options), &options_out_len);
+ if (p_out != NULL) {
+ err_t err;
+ struct dhcp6_msg *msg_out = (struct dhcp6_msg *)p_out->payload;
+ u8_t *options = (u8_t *)(msg_out + 1);
+ LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_information_request: making request\n"));
+
+ options_out_len = dhcp6_option_optionrequest(options_out_len, options, requested_options,
+ LWIP_ARRAYSIZE(requested_options), p_out->len);
+ LWIP_HOOK_DHCP6_APPEND_OPTIONS(netif, dhcp6, DHCP6_STATE_REQUESTING_CONFIG, msg_out,
+ DHCP6_INFOREQUEST, options_out_len, p_out->len);
+ dhcp6_msg_finalize(options_out_len, p_out);
+
+ err = udp_sendto_if(dhcp6_pcb, p_out, &dhcp6_All_DHCP6_Relay_Agents_and_Servers, DHCP6_SERVER_PORT, netif);
+ pbuf_free(p_out);
+ LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp6_information_request: INFOREQUESTING -> %d\n", (int)err));
+ LWIP_UNUSED_ARG(err);
+ } else {
+ LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp6_information_request: could not allocate DHCP6 request\n"));
+ }
+ dhcp6_set_state(dhcp6, DHCP6_STATE_REQUESTING_CONFIG, "dhcp6_information_request");
+ if (dhcp6->tries < 255) {
+ dhcp6->tries++;
+ }
+ msecs = (u16_t)((dhcp6->tries < 6 ? 1 << dhcp6->tries : 60) * 1000);
+ dhcp6->request_timeout = (u16_t)((msecs + DHCP6_TIMER_MSECS - 1) / DHCP6_TIMER_MSECS);
+ LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp6_information_request(): set request timeout %"U16_F" msecs\n", msecs));
+}
+
+static err_t
+dhcp6_request_config(struct netif *netif, struct dhcp6 *dhcp6)
+{
+ /* stateless mode enabled and no request running? */
+ if (dhcp6->state == DHCP6_STATE_STATELESS_IDLE) {
+ /* send Information-request and wait for answer; setup receive timeout */
+ dhcp6_information_request(netif, dhcp6);
+ }
+
+ return ERR_OK;
+}
+
+static void
+dhcp6_abort_config_request(struct dhcp6 *dhcp6)
+{
+ if (dhcp6->state == DHCP6_STATE_REQUESTING_CONFIG) {
+ /* abort running request */
+ dhcp6_set_state(dhcp6, DHCP6_STATE_STATELESS_IDLE, "dhcp6_abort_config_request");
+ }
+}
+
+/* Handle a REPLY to INFOREQUEST
+ * This parses DNS and NTP server addresses from the reply.
+ */
+static void
+dhcp6_handle_config_reply(struct netif *netif, struct pbuf *p_msg_in)
+{
+ struct dhcp6 *dhcp6 = netif_dhcp6_data(netif);
+
+ LWIP_UNUSED_ARG(dhcp6);
+ LWIP_UNUSED_ARG(p_msg_in);
+
+#if LWIP_DHCP6_PROVIDE_DNS_SERVERS
+ if (dhcp6_option_given(dhcp6, DHCP6_OPTION_IDX_DNS_SERVER)) {
+ ip_addr_t dns_addr;
+ ip6_addr_t *dns_addr6;
+ u16_t op_start = dhcp6_get_option_start(dhcp6, DHCP6_OPTION_IDX_DNS_SERVER);
+ u16_t op_len = dhcp6_get_option_length(dhcp6, DHCP6_OPTION_IDX_DNS_SERVER);
+ u16_t idx;
+ u8_t n;
+
+ memset(&dns_addr, 0, sizeof(dns_addr));
+ dns_addr6 = ip_2_ip6(&dns_addr);
+ for (n = 0, idx = op_start; (idx < op_start + op_len) && (n < LWIP_DHCP6_PROVIDE_DNS_SERVERS);
+ n++, idx += sizeof(struct ip6_addr_packed)) {
+ u16_t copied = pbuf_copy_partial(p_msg_in, dns_addr6, sizeof(struct ip6_addr_packed), idx);
+ if (copied != sizeof(struct ip6_addr_packed)) {
+ /* pbuf length mismatch */
+ return;
+ }
+ ip6_addr_assign_zone(dns_addr6, IP6_UNKNOWN, netif);
+ /* @todo: do we need a different offset than DHCP(v4)? */
+ dns_setserver(n, &dns_addr);
+ }
+ }
+ /* @ todo: parse and set Domain Search List */
+#endif /* LWIP_DHCP6_PROVIDE_DNS_SERVERS */
+
+#if LWIP_DHCP6_GET_NTP_SRV
+ if (dhcp6_option_given(dhcp6, DHCP6_OPTION_IDX_NTP_SERVER)) {
+ ip_addr_t ntp_server_addrs[LWIP_DHCP6_MAX_NTP_SERVERS];
+ u16_t op_start = dhcp6_get_option_start(dhcp6, DHCP6_OPTION_IDX_NTP_SERVER);
+ u16_t op_len = dhcp6_get_option_length(dhcp6, DHCP6_OPTION_IDX_NTP_SERVER);
+ u16_t idx;
+ u8_t n;
+
+ for (n = 0, idx = op_start; (idx < op_start + op_len) && (n < LWIP_DHCP6_MAX_NTP_SERVERS);
+ n++, idx += sizeof(struct ip6_addr_packed)) {
+ u16_t copied;
+ ip6_addr_t *ntp_addr6 = ip_2_ip6(&ntp_server_addrs[n]);
+ ip_addr_set_zero_ip6(&ntp_server_addrs[n]);
+ copied = pbuf_copy_partial(p_msg_in, ntp_addr6, sizeof(struct ip6_addr_packed), idx);
+ if (copied != sizeof(struct ip6_addr_packed)) {
+ /* pbuf length mismatch */
+ return;
+ }
+ ip6_addr_assign_zone(ntp_addr6, IP6_UNKNOWN, netif);
+ }
+ dhcp6_set_ntp_servers(n, ntp_server_addrs);
+ }
+#endif /* LWIP_DHCP6_GET_NTP_SRV */
+}
+#endif /* LWIP_IPV6_DHCP6_STATELESS */
+
+/** This function is called from nd6 module when an RA messsage is received
+ * It triggers DHCPv6 requests (if enabled).
+ */
+void
+dhcp6_nd6_ra_trigger(struct netif *netif, u8_t managed_addr_config, u8_t other_config)
+{
+ struct dhcp6 *dhcp6;
+
+ LWIP_ASSERT("netif != NULL", netif != NULL);
+ dhcp6 = netif_dhcp6_data(netif);
+
+ LWIP_UNUSED_ARG(managed_addr_config);
+ LWIP_UNUSED_ARG(other_config);
+ LWIP_UNUSED_ARG(dhcp6);
+
+#if LWIP_IPV6_DHCP6_STATELESS
+ if (dhcp6 != NULL) {
+ if (dhcp6_stateless_enabled(dhcp6)) {
+ if (other_config) {
+ dhcp6_request_config(netif, dhcp6);
+ } else {
+ dhcp6_abort_config_request(dhcp6);
+ }
+ }
+ }
+#endif /* LWIP_IPV6_DHCP6_STATELESS */
+}
+
+/**
+ * Parse the DHCPv6 message and extract the DHCPv6 options.
+ *
+ * Extract the DHCPv6 options (offset + length) so that we can later easily
+ * check for them or extract the contents.
+ */
+static err_t
+dhcp6_parse_reply(struct pbuf *p, struct dhcp6 *dhcp6)
+{
+ u16_t offset;
+ u16_t offset_max;
+ u16_t options_idx;
+ struct dhcp6_msg *msg_in;
+
+ LWIP_UNUSED_ARG(dhcp6);
+
+ /* clear received options */
+ dhcp6_clear_all_options(dhcp6);
+ msg_in = (struct dhcp6_msg *)p->payload;
+
+ /* parse options */
+
+ options_idx = sizeof(struct dhcp6_msg);
+ /* parse options to the end of the received packet */
+ offset_max = p->tot_len;
+
+ offset = options_idx;
+ /* at least 4 byte to read? */
+ while ((offset + 4 <= offset_max)) {
+ u8_t op_len_buf[4];
+ u8_t *op_len;
+ u16_t op;
+ u16_t len;
+ u16_t val_offset = (u16_t)(offset + 4);
+ if (val_offset < offset) {
+ /* overflow */
+ return ERR_BUF;
+ }
+ /* copy option + length, might be split accross pbufs */
+ op_len = (u8_t *)pbuf_get_contiguous(p, op_len_buf, 4, 4, offset);
+ if (op_len == NULL) {
+ /* failed to get option and length */
+ return ERR_VAL;
+ }
+ op = (op_len[0] << 8) | op_len[1];
+ len = (op_len[2] << 8) | op_len[3];
+ offset = val_offset + len;
+ if (offset < val_offset) {
+ /* overflow */
+ return ERR_BUF;
+ }
+
+ switch (op) {
+ case (DHCP6_OPTION_CLIENTID):
+ dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_CLI_ID);
+ dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_CLI_ID, val_offset, len);
+ break;
+ case (DHCP6_OPTION_SERVERID):
+ dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_SERVER_ID);
+ dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_SERVER_ID, val_offset, len);
+ break;
+#if LWIP_DHCP6_PROVIDE_DNS_SERVERS
+ case (DHCP6_OPTION_DNS_SERVERS):
+ dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_DNS_SERVER);
+ dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_DNS_SERVER, val_offset, len);
+ break;
+ case (DHCP6_OPTION_DOMAIN_LIST):
+ dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_DOMAIN_LIST);
+ dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_DOMAIN_LIST, val_offset, len);
+ break;
+#endif /* LWIP_DHCP6_PROVIDE_DNS_SERVERS */
+#if LWIP_DHCP6_GET_NTP_SRV
+ case (DHCP6_OPTION_SNTP_SERVERS):
+ dhcp6_got_option(dhcp6, DHCP6_OPTION_IDX_NTP_SERVER);
+ dhcp6_set_option(dhcp6, DHCP6_OPTION_IDX_NTP_SERVER, val_offset, len);
+ break;
+#endif /* LWIP_DHCP6_GET_NTP_SRV*/
+ default:
+ LWIP_DEBUGF(DHCP6_DEBUG, ("skipping option %"U16_F" in options\n", op));
+ LWIP_HOOK_DHCP6_PARSE_OPTION(ip_current_netif(), dhcp6, dhcp6->state, msg_in,
+ msg_in->msgtype, op, len, q, val_offset);
+ break;
+ }
+ }
+ return ERR_OK;
+}
+
+static void
+dhcp6_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
+{
+ struct netif *netif = ip_current_input_netif();
+ struct dhcp6 *dhcp6 = netif_dhcp6_data(netif);
+ struct dhcp6_msg *reply_msg = (struct dhcp6_msg *)p->payload;
+ u8_t msg_type;
+ u32_t xid;
+
+ LWIP_UNUSED_ARG(arg);
+
+ /* Caught DHCPv6 message from netif that does not have DHCPv6 enabled? -> not interested */
+ if ((dhcp6 == NULL) || (dhcp6->pcb_allocated == 0)) {
+ goto free_pbuf_and_return;
+ }
+
+ LWIP_ERROR("invalid server address type", IP_IS_V6(addr), goto free_pbuf_and_return;);
+
+ LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_recv(pbuf = %p) from DHCPv6 server %s port %"U16_F"\n", (void *)p,
+ ipaddr_ntoa(addr), port));
+ LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("pbuf->len = %"U16_F"\n", p->len));
+ LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("pbuf->tot_len = %"U16_F"\n", p->tot_len));
+ /* prevent warnings about unused arguments */
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_UNUSED_ARG(addr);
+ LWIP_UNUSED_ARG(port);
+
+ if (p->len < sizeof(struct dhcp6_msg)) {
+ LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCPv6 reply message or pbuf too short\n"));
+ goto free_pbuf_and_return;
+ }
+
+ /* match transaction ID against what we expected */
+ xid = reply_msg->transaction_id[0] << 16;
+ xid |= reply_msg->transaction_id[1] << 8;
+ xid |= reply_msg->transaction_id[2];
+ if (xid != dhcp6->xid) {
+ LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("transaction id mismatch reply_msg->xid(%"X32_F")!= dhcp6->xid(%"X32_F")\n", xid, dhcp6->xid));
+ goto free_pbuf_and_return;
+ }
+ /* option fields could be unfold? */
+ if (dhcp6_parse_reply(p, dhcp6) != ERR_OK) {
+ LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("problem unfolding DHCPv6 message - too short on memory?\n"));
+ goto free_pbuf_and_return;
+ }
+
+ /* read DHCP message type */
+ msg_type = reply_msg->msgtype;
+ /* message type is DHCP6 REPLY? */
+ if (msg_type == DHCP6_REPLY) {
+ LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("DHCP6_REPLY received\n"));
+#if LWIP_IPV6_DHCP6_STATELESS
+ /* in info-requesting state? */
+ if (dhcp6->state == DHCP6_STATE_REQUESTING_CONFIG) {
+ dhcp6_set_state(dhcp6, DHCP6_STATE_STATELESS_IDLE, "dhcp6_recv");
+ dhcp6_handle_config_reply(netif, p);
+ } else
+#endif /* LWIP_IPV6_DHCP6_STATELESS */
+ {
+ /* @todo: handle reply in other states? */
+ }
+ } else {
+ /* @todo: handle other message types */
+ }
+
+free_pbuf_and_return:
+ pbuf_free(p);
+}
+
+/**
+ * A DHCPv6 request has timed out.
+ *
+ * The timer that was started with the DHCPv6 request has
+ * timed out, indicating no response was received in time.
+ */
+static void
+dhcp6_timeout(struct netif *netif, struct dhcp6 *dhcp6)
+{
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp6_timeout()\n"));
+
+ LWIP_UNUSED_ARG(netif);
+ LWIP_UNUSED_ARG(dhcp6);
+
+#if LWIP_IPV6_DHCP6_STATELESS
+ /* back-off period has passed, or server selection timed out */
+ if (dhcp6->state == DHCP6_STATE_REQUESTING_CONFIG) {
+ LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("dhcp6_timeout(): retrying information request\n"));
+ dhcp6_information_request(netif, dhcp6);
+ }
+#endif /* LWIP_IPV6_DHCP6_STATELESS */
+}
+
+/**
+ * DHCPv6 timeout handling (this function must be called every 500ms,
+ * see @ref DHCP6_TIMER_MSECS).
+ *
+ * A DHCPv6 server is expected to respond within a short period of time.
+ * This timer checks whether an outstanding DHCPv6 request is timed out.
+ */
+void
+dhcp6_tmr(void)
+{
+ struct netif *netif;
+ /* loop through netif's */
+ NETIF_FOREACH(netif) {
+ struct dhcp6 *dhcp6 = netif_dhcp6_data(netif);
+ /* only act on DHCPv6 configured interfaces */
+ if (dhcp6 != NULL) {
+ /* timer is active (non zero), and is about to trigger now */
+ if (dhcp6->request_timeout > 1) {
+ dhcp6->request_timeout--;
+ } else if (dhcp6->request_timeout == 1) {
+ dhcp6->request_timeout--;
+ /* { dhcp6->request_timeout == 0 } */
+ LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp6_tmr(): request timeout\n"));
+ /* this client's request timeout triggered */
+ dhcp6_timeout(netif, dhcp6);
+ }
+ }
+ }
+}
+
+#endif /* LWIP_IPV6 && LWIP_IPV6_DHCP6 */
diff --git a/resources/ethernet/LwIP/core/ipv6/ethip6.c b/resources/ethernet/LwIP/core/ipv6/ethip6.c
new file mode 100644
index 00000000..0a616b6c
--- /dev/null
+++ b/resources/ethernet/LwIP/core/ipv6/ethip6.c
@@ -0,0 +1,123 @@
+/**
+ * @file
+ *
+ * Ethernet output for IPv6. Uses ND tables for link-layer addressing.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6 && LWIP_ETHERNET
+
+#include "lwip/ethip6.h"
+#include "lwip/nd6.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/netif.h"
+#include "lwip/icmp6.h"
+#include "lwip/prot/ethernet.h"
+#include "netif/ethernet.h"
+
+#include
+
+/**
+ * Resolve and fill-in Ethernet address header for outgoing IPv6 packet.
+ *
+ * For IPv6 multicast, corresponding Ethernet addresses
+ * are selected and the packet is transmitted on the link.
+ *
+ * For unicast addresses, ask the ND6 module what to do. It will either let us
+ * send the the packet right away, or queue the packet for later itself, unless
+ * an error occurs.
+ *
+ * @todo anycast addresses
+ *
+ * @param netif The lwIP network interface which the IP packet will be sent on.
+ * @param q The pbuf(s) containing the IP packet to be sent.
+ * @param ip6addr The IP address of the packet destination.
+ *
+ * @return
+ * - ERR_OK or the return value of @ref nd6_get_next_hop_addr_or_queue.
+ */
+err_t
+ethip6_output(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr)
+{
+ struct eth_addr dest;
+ const u8_t *hwaddr;
+ err_t result;
+
+ LWIP_ASSERT_CORE_LOCKED();
+
+ /* The destination IP address must be properly zoned from here on down. */
+ IP6_ADDR_ZONECHECK_NETIF(ip6addr, netif);
+
+ /* multicast destination IP address? */
+ if (ip6_addr_ismulticast(ip6addr)) {
+ /* Hash IP multicast address to MAC address.*/
+ dest.addr[0] = 0x33;
+ dest.addr[1] = 0x33;
+ dest.addr[2] = ((const u8_t *)(&(ip6addr->addr[3])))[0];
+ dest.addr[3] = ((const u8_t *)(&(ip6addr->addr[3])))[1];
+ dest.addr[4] = ((const u8_t *)(&(ip6addr->addr[3])))[2];
+ dest.addr[5] = ((const u8_t *)(&(ip6addr->addr[3])))[3];
+
+ /* Send out. */
+ return ethernet_output(netif, q, (const struct eth_addr*)(netif->hwaddr), &dest, ETHTYPE_IPV6);
+ }
+
+ /* We have a unicast destination IP address */
+ /* @todo anycast? */
+
+ /* Ask ND6 what to do with the packet. */
+ result = nd6_get_next_hop_addr_or_queue(netif, q, ip6addr, &hwaddr);
+ if (result != ERR_OK) {
+ return result;
+ }
+
+ /* If no hardware address is returned, nd6 has queued the packet for later. */
+ if (hwaddr == NULL) {
+ return ERR_OK;
+ }
+
+ /* Send out the packet using the returned hardware address. */
+ SMEMCPY(dest.addr, hwaddr, 6);
+ return ethernet_output(netif, q, (const struct eth_addr*)(netif->hwaddr), &dest, ETHTYPE_IPV6);
+}
+
+#endif /* LWIP_IPV6 && LWIP_ETHERNET */
diff --git a/resources/ethernet/LwIP/core/ipv6/icmp6.c b/resources/ethernet/LwIP/core/ipv6/icmp6.c
new file mode 100644
index 00000000..7c936686
--- /dev/null
+++ b/resources/ethernet/LwIP/core/ipv6/icmp6.c
@@ -0,0 +1,425 @@
+/**
+ * @file
+ *
+ * IPv6 version of ICMP, as per RFC 4443.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_ICMP6 && LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/icmp6.h"
+#include "lwip/prot/icmp6.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/pbuf.h"
+#include "lwip/netif.h"
+#include "lwip/nd6.h"
+#include "lwip/mld6.h"
+#include "lwip/ip.h"
+#include "lwip/stats.h"
+
+#include