39 Commits

Author SHA1 Message Date
gaoro-xiao 58361589d8 fix: defer ch390 exti until rtos is ready 2026-04-29 06:06:26 +08:00
gaoro-xiao 0681b8bbe4 fix: set tcp client reconnect interval to three seconds 2026-04-29 04:55:16 +08:00
gaoro-xiao 3cb49fc4f8 docs: document rtos firmware versioning 2026-04-29 04:45:43 +08:00
gaoro-xiao db714471b8 docs: document ch390 pbuf leak fix 2026-04-29 04:36:47 +08:00
gaoro-xiao 8c204aad77 fix: stabilize lwip ethernet recovery 2026-04-29 04:36:29 +08:00
gaoro-xiao a6040e7d68 fix: harden ch390 rx protocol handling 2026-04-29 04:36:17 +08:00
gaoro-xiao 60d2af0a27 fix: stop network tasks cleanly on restart 2026-04-29 04:36:01 +08:00
gaoro-xiao ac0c464910 feat: add delayed uart and runtime mac configuration 2026-04-29 04:35:48 +08:00
gaoro-xiao c9ece65182 fix: serialize ch390 recovery in ethernet poll path 2026-04-23 18:06:03 +08:00
gaoro-xiao c519f90149 fix: harden ch390 recovery primitives 2026-04-23 18:06:03 +08:00
gaoro-xiao d6a1565503 fix: fail fast on app route backpressure 2026-04-23 18:06:02 +08:00
gaoro-xiao ab3b6bfc9a fix: make uart tx enqueue all-or-nothing 2026-04-23 18:06:02 +08:00
gaoro-xiao c1a0822227 fix: add explicit route send failure results 2026-04-23 18:06:02 +08:00
gaoro-xiao 22bc6a7fef fix: abort client close to avoid time-wait reconnect block 2026-04-23 02:55:38 +08:00
gaoro-xiao fe03bee588 docs: document client abort close tradeoff 2026-04-23 02:55:38 +08:00
gaoro-xiao 00be10f134 chore: ignore generated build artifacts and add shared keil config 2026-04-23 00:01:01 +08:00
gaoro-xiao ccd69a523e fix: tune FreeRTOS and RTT diagnostics for runtime stability 2026-04-23 00:00:31 +08:00
gaoro-xiao eeccfb84a0 fix: start only enabled links and move config buffers off stack 2026-04-23 00:00:06 +08:00
gaoro-xiao e3450fd0ad fix: harden tcp tasks around link readiness and connect retries 2026-04-22 23:59:46 +08:00
gaoro-xiao afd90d357c fix: add bounded lwip connect waits and link status helper 2026-04-22 23:59:19 +08:00
gaoro-xiao c8f27e21f1 fix: improve boot fault visibility and defer watchdog start 2026-04-20 08:12:06 +08:00
gaoro-xiao 77da670f5c fix: align rd target memory and startup settings 2026-04-20 08:11:49 +08:00
gaoro-xiao 3f91481d00 docs: add log and project config 2026-04-18 17:12:55 +08:00
gaoro-xiao deecc8f1c8 docs: add debug handoff package 2026-04-18 05:29:47 +08:00
gaoro-xiao 70e78a6ef6 fix: capture current ch390 and lwip debug changes 2026-04-18 05:29:32 +08:00
gaoro-xiao cd48a8af68 fix: preserve current runtime debug state 2026-04-18 05:29:11 +08:00
gaoro-xiao 6aba77df9a feat: 保存已验证的CH390网络打通基线 2026-04-17 07:09:55 +08:00
gaoro-xiao 59eecf428f fix: 忽略Keil临时输出和调试日志 2026-04-17 07:07:53 +08:00
gaoro-xiao 229b961f8e docs: 固化ARP与ICMP联调经验 2026-04-17 07:07:43 +08:00
gaoro-xiao 4970f17254 docs: 明确DefaultTask不可删除(LED+IWDG),更新编码提示词 2026-04-15 19:34:41 +08:00
gaoro-xiao 5150a77b3e docs: 添加编码任务提示词,供后续coding agent使用 2026-04-15 19:29:17 +08:00
gaoro-xiao c81bd93205 refactor: 确定路径A架构(NO_SYS=0+netconn+多TCP任务),精确化任务设计与内存预算
- FreeRTOSConfig.h: 堆8->10KB, 优先级56->7, 添加lwIP sys_arch宏和任务优先级/栈大小定义
- lwipopts.h: LWIP_SOCKET=0节省RAM, LWIP_TCPIP_CORE_LOCKING=1, MEM_SIZE 8KB,
  PBUF_POOL 10, MEMP_NUM_NETCONN 8, TCP_SND_BUF/WND 8xMSS, 关闭DHCP/UDP,
  TCPIP_THREAD_STACKSIZE/PRIO明确指定
- 项目技术实现: 9+1任务架构, netconn阻塞模式每连接独立任务, 零拷贝route_msg_t,
  内存精确估算49KB(RCT6超1KB需优化或换RDT6), 模块重写/复用清单
- 项目需求说明: 明确netconn API路线, 添加RDT6备选, 更新任务列表9个任务
2026-04-15 19:23:48 +08:00
gaoro-xiao dc277b040b fix: 更新.gitignore以排除Keil构建日志和Wireshark日志文件 2026-04-13 15:50:16 +08:00
gaoro-xiao c21d85a9da refactor: 适配STM32F103RCT6 + FreeRTOS工程框架,同步baremetal-r8协议手册
- IOC: MCU切换为STM32F103RCTx,添加FREERTOS+TIM4中间件,HAL时间基准改为TIM4
- Keil uvprojx: 目标器件RC,Flash 256KB/48KB SRAM,宏xE,HD Flash算法,启动文件xe.s
- EWARM ewp: 宏xE,ICF/启动文件切换为xe版本
- 启动文件: Stack_Size 0x400→0x800适配FreeRTOS
- 重写项目需求说明/技术实现文档,描述FreeRTOS 5任务架构+lwIP NO_SYS=0
- 新增AT固件使用手册(MUX/NET/LINK协议)和工程调试指南(FreeRTOS专项)
2026-04-09 20:22:48 +08:00
gaoro-xiao 68c64959c7 refactor: 准备R8裸机迁移工程基线 2026-03-30 14:41:13 +08:00
gaoro-xiao 7ee96bc08d fix: 统一R8工程目标并修复MDK编译前置问题 2026-03-30 13:14:37 +08:00
gaoro-xiao 4996b451d9 feat: 完成TCP2UART透传核心集成
集成CH390驱动、LwIP协议栈和FreeRTOS多任务透传框架,确保TCP Server/Client与UART链路按配置稳定联动。
2026-03-30 11:39:40 +08:00
gaoro-xiao d5803ca7dd refactor: 基于EVT示例重构技术实现,采用LwIP协议栈架构
- 引入LwIP协议栈替代直接操作CH390D硬件Socket
- CH390D驱动层直接移植官方EVT代码(CH390.c/h + CH390_Interface.c/h)
- 重构FreeRTOS任务:LwIPTask统一处理协议栈,透传任务专注数据搬运
- 使用StreamBuffer替代Queue,更适合流式数据传输
- 更新中断优先级配置,符合FreeRTOS要求
- 添加内存使用估算(18KB/20KB)
- 完善SPI配置和LwIP配置说明
2026-03-29 17:51:47 +08:00
gaoro-xiao eb57a564ef fix: 将MCU型号从STM32F103RCT6修正为STM32F103R8T6
- 修改ioc文件中的芯片型号配置
- 调整HeapSize从10KB减至4KB以适应20KB RAM限制
- 更新项目技术实现文档中的MCU规格和内存配置
- 更新MDK-ARM工程文件的芯片定义(STM32F103xB)
- 添加新的启动文件startup_stm32f103xb.s
2026-03-29 17:42:25 +08:00
273 changed files with 88628 additions and 1595 deletions
+7
View File
@@ -0,0 +1,7 @@
{
"keil": {
"project": "MDK-ARM/TCP2UART.uvprojx",
"target": "TCP2UART",
"log_dir": ".embeddedskills/build"
}
}
+15
View File
@@ -26,6 +26,18 @@ Release/
*.uvguix.*
MDK-ARM/DebugConfig/
MDK-ARM/TCP2UART/
build_keil.log
MDK-ARM/build.log
MDK-ARM/build_capture.txt
MDK-ARM/build_output.txt
MDK-ARM/keil-build-viewer.log
MDK-ARM/keil-build-viewer-record.txt
MDK-ARM/keil-build-viewer.exe
MDK-ARM/EventRecorderStub.scvd
.embeddedskills/build/
.embeddedskills/state.json
build_current.log
uv4_stdout.txt
# OS
Thumbs.db
@@ -34,3 +46,6 @@ Desktop.ini
# 项目文档
项目计划.md
# Wireshark
WiresharkLog/
+12 -12
View File
File diff suppressed because one or more lines are too long
+402
View File
@@ -0,0 +1,402 @@
# TCP2UART AT 固件使用手册
## 1. 文档范围
本文档定义 `TCP2UART` 项目的最终 AT 外部协议。
本文档只描述最终协议模型,不保留任何历史展开式实例字段,不包含测试记录,不讨论旧版兼容命令。
适用对象:
- 上位机开发人员
- 联调与测试人员
- 固件接口实现人员
## 2. 设备与接口
- 主控:`STM32F103RCT6`256KB Flash / 48KB SRAM
- 以太网芯片:`CH390D`
- 配置口:`USART1`
- 数据口:`USART2``USART3`
### 2.1 固件版本线
- FreeRTOS + lwIP 版本线从 `V2.0.0` 开始。
- 裸机版本线从 `V1.0.0` 开始。
- 当前 FreeRTOS 固件基线 release`TCP2UART RTOS V2.0.0`
- 固件下载:`https://git.furtherverse.com/gaoro-xiao/TCP2UART/releases/tag/V2.0.0`
职责划分:
- `USART1`AT 配置口
- `USART2 / USART3`:业务数据口,可工作于普通透传或 MUX 透传模式
## 3. 最终协议模型
本项目最终控制协议由三部分组成:
1. `MUX`:全局数据承载模式开关
2. `NET`:全局静态网络配置记录
3. `LINK[ROLE]`:按角色名组织的链路配置记录(`S1/S2/C1/C2`
约束如下:
- 设备只有一张网卡,因此本地网络参数只配置一次
- DHCP 不属于最终协议范围
- 所有 AT 文本命令必须以 `\r\n` 结尾
-`DSTMASK=0x00` 时,MUX 数据口中的系统控制帧进入 AT 解析路径,其控制文本同样必须以 `\r\n` 结尾
## 4. MUX 帧格式
`MUX=1` 时,数据口使用如下帧格式:
```text
SYNC | LEN_H | LEN_L | SRCID | DSTMASK | PAYLOAD | TAIL
```
字段定义:
- `SYNC`:帧起始标记,建议固定为 `0x7E`
- `LEN_H / LEN_L``PAYLOAD` 长度,高字节在前
- `SRCID`:单字节源端点 ID
- `DSTMASK`:单字节目标端点位图
- `PAYLOAD`:负载数据
- `TAIL`:帧结束标记,建议固定为 `0x7F`
规则:
- `DSTMASK != 0x00`:业务数据帧
- `DSTMASK = 0x00`:系统控制帧
- 系统控制帧的 `PAYLOAD` 为 AT 文本,必须以 `\r\n` 结束
## 5. 统一端点编码
`UART``TCP` 逻辑实例统一进入同一套编码空间:
| 端点 | 编码 |
|------|------|
| `C1` | `0x01` |
| `C2` | `0x02` |
| `U0` | `0x04` |
| `U1` | `0x08` |
| `S1` | `0x10` |
| `S2` | `0x20` |
说明:
- `SRCID` 必须为单一端点值
- `DSTMASK` 可以是一个或多个端点编码按位或的结果
- `DSTMASK=0x00` 保留给系统控制帧
## 6. AT 命令总则
### 6.1 命令结尾
所有 AT 命令均必须以 `\r\n` 结尾。
例如:
```text
AT\r\n
AT+MUX?\r\n
AT+NET=192.168.31.100,255.255.255.0,192.168.31.1,00:00:00:00:00:00\r\n
```
### 6.2 持久化规则
参数设置成功后只写入当前运行配置,不会自动写入 Flash。
若要掉电保持,必须执行:
1. `AT+SAVE\r\n`
2. `AT+RESET\r\n`
### 6.3 响应风格
- 成功:`OK`
- 需要保存后生效时,允许追加提示文本
- 失败:`ERROR: <reason>`
## 7. 默认配置
### 7.1 MUX 默认值
```text
MUX = 0
```
### 7.2 NET 默认值
```text
NET = 192.168.31.100,255.255.255.0,192.168.31.1,00:00:00:00:00:00
```
默认 MAC 为全 0,表示 Flash 中不固化板卡 MAC;运行时使用 `CH390D` 内部 MAC。`AT+?``AT+NET?` 回显的是当前生效 MAC。
### 7.3 LINK 默认值
```text
LINK:S1 = 1,8080,0.0.0.0,0,U0
LINK:S2 = 0,8081,0.0.0.0,0,U1
LINK:C1 = 1,9001,192.168.1.200,9000,U1
LINK:C2 = 0,9002,192.168.1.201,9001,U0
```
说明:
- `S1/S2/C1/C2` 为对外可见角色名
- 内部索引映射由固件管理,不对外暴露
UART 记号约定:
- `U0 = USART2`
- `U1 = USART3`
## 8. AT 命令定义
### 8.1 测试设备在线
命令:
```text
AT\r\n
```
返回:
```text
OK
```
### 8.2 查询摘要
命令:
```text
AT+?\r\n
AT+QUERY\r\n
```
推荐返回格式:
```text
+NET:IP=192.168.31.100,MASK=255.255.255.0,GW=192.168.31.1,MAC=<当前生效MAC>
+LINK:S1,EN=1,LPORT=8080,RIP=0.0.0.0,RPORT=0,UART=U0
+LINK:S2,EN=0,LPORT=8081,RIP=0.0.0.0,RPORT=0,UART=U1
+LINK:C1,EN=1,LPORT=9001,RIP=192.168.1.200,RPORT=9000,UART=U1
+LINK:C2,EN=0,LPORT=9002,RIP=192.168.1.201,RPORT=9001,UART=U0
+MUX:0
+MAP:UART2=0x04,UART3=0x08,C1=0x01,C2=0x02,S1=0x10,S2=0x20
+BAUD:U0=115200,U1=115200
OK
```
### 8.3 MUX 类命令
#### 设置 MUX
```text
AT+MUX=1\r\n
```
参数:
- `0`:普通透传模式
- `1`MUX 透传模式
查询:
```text
AT+MUX?\r\n
```
返回示例:
```text
+MUX:1
OK
```
### 8.4 NET 类命令
#### 设置 NET
```text
AT+NET=192.168.31.100,255.255.255.0,192.168.31.1,00:00:00:00:00:00\r\n
```
字段顺序:
```text
IP,MASK,GW,MAC
```
查询:
```text
AT+NET?\r\n
```
返回示例:
```text
+NET:IP=192.168.31.100,MASK=255.255.255.0,GW=192.168.31.1,MAC=<当前生效MAC>
OK
```
**MAC 设置说明:**
当 MAC 设置为全 0 时,固件将使用 `CH390D` 内部 MAC 地址。此时 Flash 内仍保存全 0,不会把内部 MAC 写回 Flash;`AT+?``AT+NET?` 查询到的 MAC 地址为当前运行时生效的硬件 MAC 地址。
### 8.5 LINK 类命令
#### 设置单条 LINK 记录
```text
AT+LINK=S1,1,8080,0.0.0.0,0,U0\r\n
AT+LINK=C1,1,9001,192.168.1.200,9000,U1\r\n
```
字段顺序:
```text
ROLE,EN,LPORT,RIP,RPORT,UART
```
字段说明:
- `ROLE`:链路角色名,固定为 `S1/S2/C1/C2`
- `EN``0/1`
- `LPORT`:本地端口
- `RIP`:对端 IP
- `RPORT`:对端端口
- `UART``U0/U1`
说明:
- `Server``Client` 共用同一条 `LINK` 记录模型
- `Server``RIP/RPORT` 可作为允许接入的对端约束或预设对端信息
- `Client``RIP/RPORT` 表示远端目标地址与端口
- `Client` 侧当前保留固定 `LPORT` 语义,用于满足部分上位机或现场网络策略对固定源端口的依赖
- 为避免固定 `LPORT` 下频繁重连被 lwIP `TIME_WAIT` 长时间占用阻塞,当前固件对 `Client` 主动断开后的释放路径采用 abortive closeRST)而非优雅 `FIN/ACK` 关闭
- 因此 `Client` 重连场景下,对端可能观察到 `RST` 或“连接被重置”,这属于当前产品约束下的有意设计取舍,不影响 `AT+LINK``LPORT` 的配置语义
#### 查询单条 LINK
```text
AT+LINK=S1\r\n
```
返回示例:
```text
+LINK:S1,EN=1,LPORT=8080,RIP=0.0.0.0,RPORT=0,UART=U0
OK
```
#### 查询全部 LINK
```text
AT+LINK?\r\n
```
返回示例:
```text
+LINK:S1,EN=1,LPORT=8080,RIP=0.0.0.0,RPORT=0,UART=U0
+LINK:S2,EN=0,LPORT=8081,RIP=0.0.0.0,RPORT=0,UART=U1
+LINK:C1,EN=1,LPORT=9001,RIP=192.168.1.200,RPORT=9000,UART=U1
+LINK:C2,EN=0,LPORT=9002,RIP=192.168.1.201,RPORT=9001,UART=U0
OK
```
## 9. 保存与复位命令
### 9.1 保存配置
```text
AT+SAVE\r\n
```
成功返回:
```text
OK: Configuration saved
```
### 9.2 软件复位
```text
AT+RESET\r\n
```
返回:
```text
OK: Resetting...
```
### 9.3 恢复默认值
```text
AT+DEFAULT\r\n
```
返回:
```text
OK: Defaults restored
```
## 10. 常见错误返回
| 场景 | 返回 |
|------|------|
| 未知命令 | `ERROR: Unknown command` |
| 非法端口 | `ERROR: Invalid port` |
| 非法波特率 | `ERROR: Invalid baudrate` |
| 非法 IP 地址 | `ERROR: Invalid IP format` |
| 非法掩码 | `ERROR: Invalid mask format` |
| 非法网关 | `ERROR: Invalid gateway format` |
| 非法远端 IP | `ERROR: Invalid remote IP format` |
| 非法 MAC | `ERROR: Invalid MAC format` |
| 非法 `SRCID` / `DSTMASK` | `ERROR: Invalid route field` |
| Flash 保存失败 | `ERROR: Save failed` |
## 11. 推荐配置流程
```text
AT+NET=192.168.31.123,255.255.255.0,192.168.31.1,00:00:00:00:00:00\r\n
AT+LINK=S1,1,10001,0.0.0.0,0,U1\r\n
AT+LINK=S2,1,10003,0.0.0.0,0,U1\r\n
AT+LINK=C1,1,20001,192.168.1.201,10002,U0\r\n
AT+MUX=1\r\n
AT+SAVE\r\n
AT+RESET\r\n
```
## 12. 故障排查建议
### 12.1 发送 `AT` 没有返回
优先检查:
1. 是否连接到 `USART1`
2. 串口参数是否为 `115200 8N1`
3. 是否严格使用 `\r\n` 作为命令结尾
4. 接线是否正确
5. 设备是否正常上电运行
### 12.2 设置成功但重启后参数丢失
检查是否漏掉以下步骤:
1. `AT+SAVE\r\n`
2. `AT+RESET\r\n`
## 13. 相关文件
- AT 命令实现:`App/config.c`
- 配置结构与默认值:`App/config.h`
- FreeRTOS 任务定义:`Core/Src/freertos.c`
- 调试指导:`工程调试指南.md`
+44
View File
@@ -0,0 +1,44 @@
#ifndef APP_RUNTIME_H
#define APP_RUNTIME_H
#include <stdint.h>
#include "FreeRTOS.h"
#include "queue.h"
#include "semphr.h"
#include "task.h"
#include "config.h"
#ifdef __cplusplus
extern "C" {
#endif
extern QueueHandle_t xTcpRxQueue;
extern QueueHandle_t xConfigQueue;
extern QueueHandle_t xLinkTxQueues[CONFIG_LINK_COUNT];
extern SemaphoreHandle_t xNetSemaphore;
extern TaskHandle_t xUartRxTaskHandle;
extern TaskHandle_t xConfigTaskHandle;
extern volatile BaseType_t g_netif_ready;
extern volatile uint32_t g_netif_phase;
extern volatile int32_t g_netif_add_err;
extern volatile int32_t g_netif_set_default_err;
extern volatile int32_t g_netif_set_link_down_err;
extern volatile int32_t g_netif_set_up_err;
extern volatile int32_t g_netif_init_ok;
void app_start_network_tasks(void);
void app_request_network_task_stop(void);
void app_clear_network_task_stop(void);
BaseType_t app_network_task_stop_requested(void);
BaseType_t app_network_tasks_are_stopped(void);
void app_on_network_task_exit(TaskHandle_t task_handle);
void app_request_network_restart(void);
void app_clear_network_restart_request(void);
BaseType_t app_network_restart_requested(void);
#ifdef __cplusplus
}
#endif
#endif
+806
View File
@@ -0,0 +1,806 @@
#include "config.h"
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "flash_param.h"
#include "usart.h"
#include "route_msg.h"
#include "app_runtime.h"
#include "debug_log.h"
#include "ethernetif.h"
#include "uart_trans.h"
#define CONFIG_RX_BUFFER_SIZE 160u
#define CONFIG_TX_BUFFER_SIZE 512u
#define CONFIG_CMD_MAX_LEN 160u
static device_config_t g_config;
static volatile bool g_reset_requested;
static volatile bool g_uart1_tx_busy;
static volatile uint32_t g_config_rx_route_fail_count;
static volatile route_send_result_t g_config_rx_route_fail_reason;
static uint8_t g_uart1_rx_buffer[CONFIG_RX_BUFFER_SIZE];
static char g_config_cmd_buffer[CONFIG_CMD_MAX_LEN];
static char g_config_response_buffer[CONFIG_TX_BUFFER_SIZE];
static uint32_t config_calc_crc(const device_config_t *cfg)
{
return flash_param_crc32(cfg, offsetof(device_config_t, crc));
}
static const char *skip_whitespace(const char *str)
{
while (*str == ' ' || *str == '\t') {
++str;
}
return str;
}
static void trim_trailing(char *str)
{
int len = (int)strlen(str);
while (len > 0 && (str[len - 1] == ' ' || str[len - 1] == '\t' || str[len - 1] == '\r' || str[len - 1] == '\n')) {
str[--len] = '\0';
}
}
static bool equals_ignore_case(const char *a, const char *b)
{
while (*a != '\0' && *b != '\0') {
char c1 = *a++;
char c2 = *b++;
if (c1 >= 'a' && c1 <= 'z') {
c1 -= 32;
}
if (c2 >= 'a' && c2 <= 'z') {
c2 -= 32;
}
if (c1 != c2) {
return false;
}
}
return (*a == '\0' && *b == '\0');
}
static int prefix_equals_ignore_case(const char *str, const char *prefix)
{
while (*prefix != '\0') {
char c1 = *str++;
char c2 = *prefix++;
if (c1 >= 'a' && c1 <= 'z') {
c1 -= 32;
}
if (c2 >= 'a' && c2 <= 'z') {
c2 -= 32;
}
if (c1 != c2) {
return 0;
}
}
return 1;
}
static int parse_u32_value(const char *value, uint32_t min_value, uint32_t max_value, uint32_t *parsed_value)
{
char *endptr;
unsigned long parsed;
parsed = strtoul(value, &endptr, 10);
if (endptr == value || *skip_whitespace(endptr) != '\0') {
return -1;
}
if (parsed < min_value || parsed > max_value) {
return -1;
}
*parsed_value = (uint32_t)parsed;
return 0;
}
static int parse_link_uart(const char *value, uint8_t *uart)
{
if (equals_ignore_case(value, "U0")) {
*uart = LINK_UART_U0;
return 0;
}
if (equals_ignore_case(value, "U1")) {
*uart = LINK_UART_U1;
return 0;
}
return -1;
}
static int parse_uart_name(const char *value, uint8_t *uart_index)
{
if (equals_ignore_case(value, "U0") || equals_ignore_case(value, "UART2")) {
*uart_index = LINK_UART_U0;
return 0;
}
if (equals_ignore_case(value, "U1") || equals_ignore_case(value, "UART3")) {
*uart_index = LINK_UART_U1;
return 0;
}
return -1;
}
static const char *link_uart_to_str(uint8_t uart)
{
return (uart == LINK_UART_U1) ? "U1" : "U0";
}
static const char *link_index_to_name(uint32_t index)
{
switch (index) {
case CONFIG_LINK_S1:
return "S1";
case CONFIG_LINK_S2:
return "S2";
case CONFIG_LINK_C1:
return "C1";
case CONFIG_LINK_C2:
return "C2";
default:
return "?";
}
}
static void config_get_display_mac(uint8_t *mac)
{
if (ethernetif_get_effective_mac(mac) != 0u) {
return;
}
memcpy(mac, g_config.net.mac, sizeof(g_config.net.mac));
}
static int parse_link_name(const char *value, uint32_t *index)
{
if (equals_ignore_case(value, "S1")) {
*index = CONFIG_LINK_S1;
return 0;
}
if (equals_ignore_case(value, "S2")) {
*index = CONFIG_LINK_S2;
return 0;
}
if (equals_ignore_case(value, "C1")) {
*index = CONFIG_LINK_C1;
return 0;
}
if (equals_ignore_case(value, "C2")) {
*index = CONFIG_LINK_C2;
return 0;
}
return -1;
}
static bool parse_command_with_value(const char *cmd, const char *name, const char **value)
{
size_t name_len = strlen(name);
if (!prefix_equals_ignore_case(cmd, name) || cmd[name_len] != '=') {
return false;
}
*value = skip_whitespace(cmd + name_len + 1u);
return true;
}
static char *config_next_token(char **cursor)
{
char *start = *cursor;
char *end;
while (*start == ' ' || *start == '\t') {
++start;
}
if (*start == '\0') {
*cursor = NULL;
return NULL;
}
end = start;
while (*end != '\0' && *end != ',') {
++end;
}
if (*end == ',') {
*end = '\0';
*cursor = end + 1;
} else {
*cursor = NULL;
}
trim_trailing(start);
return start;
}
static void set_link_defaults(void)
{
static const uint8_t zero_ip[4] = {0u, 0u, 0u, 0u};
static const uint8_t c1_ip[4] = {192u, 168u, 1u, 200u};
static const uint8_t c2_ip[4] = {192u, 168u, 1u, 201u};
memset(g_config.links, 0, sizeof(g_config.links));
g_config.links[CONFIG_LINK_S1].enabled = 1u;
g_config.links[CONFIG_LINK_S1].uart = LINK_UART_U0;
g_config.links[CONFIG_LINK_S1].local_port = 8080u;
memcpy(g_config.links[CONFIG_LINK_S1].remote_ip, zero_ip, sizeof(zero_ip));
g_config.links[CONFIG_LINK_S2].enabled = 0u;
g_config.links[CONFIG_LINK_S2].uart = LINK_UART_U1;
g_config.links[CONFIG_LINK_S2].local_port = 8081u;
memcpy(g_config.links[CONFIG_LINK_S2].remote_ip, zero_ip, sizeof(zero_ip));
g_config.links[CONFIG_LINK_C1].enabled = 1u;
g_config.links[CONFIG_LINK_C1].uart = LINK_UART_U1;
g_config.links[CONFIG_LINK_C1].local_port = 9001u;
memcpy(g_config.links[CONFIG_LINK_C1].remote_ip, c1_ip, sizeof(c1_ip));
g_config.links[CONFIG_LINK_C1].remote_port = 9000u;
g_config.links[CONFIG_LINK_C2].enabled = 0u;
g_config.links[CONFIG_LINK_C2].uart = LINK_UART_U0;
g_config.links[CONFIG_LINK_C2].local_port = 9002u;
memcpy(g_config.links[CONFIG_LINK_C2].remote_ip, c2_ip, sizeof(c2_ip));
g_config.links[CONFIG_LINK_C2].remote_port = 9001u;
}
static at_result_t handle_summary_query(char *response, uint16_t max_len)
{
char ip_str[16];
char mask_str[16];
char gw_str[16];
char mac_str[18];
uint8_t display_mac[6];
char rip_str[CONFIG_LINK_COUNT][16];
uint32_t i;
config_ip_to_str(g_config.net.ip, ip_str);
config_ip_to_str(g_config.net.mask, mask_str);
config_ip_to_str(g_config.net.gw, gw_str);
config_get_display_mac(display_mac);
config_mac_to_str(display_mac, mac_str);
for (i = 0; i < CONFIG_LINK_COUNT; ++i) {
config_ip_to_str(g_config.links[i].remote_ip, rip_str[i]);
}
snprintf(response, max_len,
"+NET:IP=%s,MASK=%s,GW=%s,MAC=%s\r\n"
"+LINK:S1,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n"
"+LINK:S2,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n"
"+LINK:C1,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n"
"+LINK:C2,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n"
"+MUX:%u\r\n"
"+MAP:UART2=0x04,UART3=0x08,C1=0x01,C2=0x02,S1=0x10,S2=0x20\r\n"
"+BAUD:U0=%lu,U1=%lu\r\n"
"OK\r\n",
ip_str, mask_str, gw_str, mac_str,
g_config.links[0].enabled, g_config.links[0].local_port, rip_str[0], g_config.links[0].remote_port, link_uart_to_str(g_config.links[0].uart),
g_config.links[1].enabled, g_config.links[1].local_port, rip_str[1], g_config.links[1].remote_port, link_uart_to_str(g_config.links[1].uart),
g_config.links[2].enabled, g_config.links[2].local_port, rip_str[2], g_config.links[2].remote_port, link_uart_to_str(g_config.links[2].uart),
g_config.links[3].enabled, g_config.links[3].local_port, rip_str[3], g_config.links[3].remote_port, link_uart_to_str(g_config.links[3].uart),
g_config.mux_mode,
(unsigned long)g_config.uart_baudrate[0],
(unsigned long)g_config.uart_baudrate[1]);
return AT_OK;
}
int config_init(void)
{
flash_param_init();
return config_load();
}
int config_load(void)
{
if (flash_param_read(&g_config, sizeof(g_config)) == 0 &&
g_config.magic == CONFIG_MAGIC &&
g_config.version == CONFIG_VERSION &&
g_config.crc == config_calc_crc(&g_config)) {
return 0;
}
config_set_defaults();
return -1;
}
int config_save(void)
{
g_config.magic = CONFIG_MAGIC;
g_config.version = CONFIG_VERSION;
g_config.crc = config_calc_crc(&g_config);
return flash_param_write(&g_config, sizeof(g_config));
}
void config_set_defaults(void)
{
const uint8_t default_ip[] = DEFAULT_NET_IP;
const uint8_t default_mask[] = DEFAULT_NET_MASK;
const uint8_t default_gw[] = DEFAULT_NET_GW;
const uint8_t default_mac[] = DEFAULT_NET_MAC;
memset(&g_config, 0, sizeof(g_config));
g_config.magic = CONFIG_MAGIC;
g_config.version = CONFIG_VERSION;
g_config.mux_mode = MUX_MODE_RAW;
memcpy(g_config.net.ip, default_ip, sizeof(g_config.net.ip));
memcpy(g_config.net.mask, default_mask, sizeof(g_config.net.mask));
memcpy(g_config.net.gw, default_gw, sizeof(g_config.net.gw));
memcpy(g_config.net.mac, default_mac, sizeof(g_config.net.mac));
set_link_defaults();
g_config.uart_baudrate[0] = DEFAULT_UART_BAUDRATE;
g_config.uart_baudrate[1] = DEFAULT_UART_BAUDRATE;
g_config.reconnect_interval_ms = 3000u;
g_config.crc = config_calc_crc(&g_config);
}
const device_config_t *config_get(void)
{
return &g_config;
}
uint32_t config_get_uart_baudrate(uint8_t uart_index)
{
if (uart_index >= CONFIG_UART_COUNT) {
return DEFAULT_UART_BAUDRATE;
}
return g_config.uart_baudrate[uart_index];
}
device_config_t *config_get_mutable(void)
{
return &g_config;
}
at_result_t config_process_at_cmd(const char *cmd, char *response, uint16_t max_len)
{
char cmd_copy[CONFIG_CMD_MAX_LEN];
const char *value;
const char *p;
strncpy(cmd_copy, cmd, sizeof(cmd_copy) - 1u);
cmd_copy[sizeof(cmd_copy) - 1u] = '\0';
trim_trailing(cmd_copy);
p = skip_whitespace(cmd_copy);
if ((p[0] != 'A' && p[0] != 'a') || (p[1] != 'T' && p[1] != 't')) {
snprintf(response, max_len, "ERROR: Unknown command\r\n");
return AT_UNKNOWN_CMD;
}
if (p[2] == '\0') {
snprintf(response, max_len, "OK\r\n");
return AT_OK;
}
if (p[2] != '+') {
snprintf(response, max_len, "ERROR: Unknown command\r\n");
return AT_UNKNOWN_CMD;
}
p += 3;
if (equals_ignore_case(p, "?") || equals_ignore_case(p, "QUERY")) {
return handle_summary_query(response, max_len);
}
if (equals_ignore_case(p, "SAVE")) {
if (config_save() != 0) {
snprintf(response, max_len, "ERROR: Save failed\r\n");
return AT_SAVE_FAILED;
}
snprintf(response, max_len, "OK: Configuration saved\r\n");
return AT_OK;
}
if (equals_ignore_case(p, "RESET")) {
g_reset_requested = true;
snprintf(response, max_len, "OK: Resetting...\r\n");
return AT_OK;
}
if (equals_ignore_case(p, "DEFAULT")) {
config_set_defaults();
snprintf(response, max_len, "OK: Defaults restored\r\n");
return AT_OK;
}
if (equals_ignore_case(p, "MUX?")) {
snprintf(response, max_len, "+MUX:%u\r\nOK\r\n", g_config.mux_mode);
return AT_OK;
}
if (equals_ignore_case(p, "BAUD?")) {
snprintf(response, max_len,
"+BAUD:U0=%lu,U1=%lu\r\nOK\r\n",
(unsigned long)g_config.uart_baudrate[0],
(unsigned long)g_config.uart_baudrate[1]);
return AT_OK;
}
if (parse_command_with_value(p, "BAUD", &value)) {
char value_copy[48];
char *cursor;
char *token;
uint8_t uart_index;
uint32_t baudrate;
strncpy(value_copy, value, sizeof(value_copy) - 1u);
value_copy[sizeof(value_copy) - 1u] = '\0';
cursor = value_copy;
token = config_next_token(&cursor);
if (token == NULL || parse_uart_name(token, &uart_index) != 0) {
snprintf(response, max_len, "ERROR: Invalid UART\r\n");
return AT_INVALID_PARAM;
}
token = config_next_token(&cursor);
if (token == NULL || parse_u32_value(token, 1200u, 921600u, &baudrate) != 0) {
snprintf(response, max_len, "ERROR: Invalid baudrate\r\n");
return AT_INVALID_PARAM;
}
g_config.uart_baudrate[uart_index] = baudrate;
snprintf(response, max_len, "OK\r\n");
return AT_NEED_REBOOT;
}
if (parse_command_with_value(p, "MUX", &value)) {
uint32_t mux_value;
if (parse_u32_value(value, 0u, 1u, &mux_value) != 0) {
snprintf(response, max_len, "ERROR: Invalid value\r\n");
return AT_INVALID_PARAM;
}
g_config.mux_mode = (uint8_t)mux_value;
snprintf(response, max_len, "OK\r\n");
return AT_NEED_REBOOT;
}
if (equals_ignore_case(p, "NET?")) {
char ip_str[16];
char mask_str[16];
char gw_str[16];
char mac_str[18];
uint8_t display_mac[6];
config_ip_to_str(g_config.net.ip, ip_str);
config_ip_to_str(g_config.net.mask, mask_str);
config_ip_to_str(g_config.net.gw, gw_str);
config_get_display_mac(display_mac);
config_mac_to_str(display_mac, mac_str);
snprintf(response, max_len, "+NET:IP=%s,MASK=%s,GW=%s,MAC=%s\r\nOK\r\n", ip_str, mask_str, gw_str, mac_str);
return AT_OK;
}
if (parse_command_with_value(p, "NET", &value)) {
char value_copy[96];
char *cursor;
char *token;
uint8_t ip[4];
uint8_t mask[4];
uint8_t gw[4];
uint8_t mac[6];
strncpy(value_copy, value, sizeof(value_copy) - 1u);
value_copy[sizeof(value_copy) - 1u] = '\0';
cursor = value_copy;
token = config_next_token(&cursor);
if (token == NULL || config_str_to_ip(token, ip) != 0) {
snprintf(response, max_len, "ERROR: Invalid IP format\r\n");
return AT_INVALID_PARAM;
}
token = config_next_token(&cursor);
if (token == NULL || config_str_to_ip(token, mask) != 0) {
snprintf(response, max_len, "ERROR: Invalid mask format\r\n");
return AT_INVALID_PARAM;
}
token = config_next_token(&cursor);
if (token == NULL || config_str_to_ip(token, gw) != 0) {
snprintf(response, max_len, "ERROR: Invalid gateway format\r\n");
return AT_INVALID_PARAM;
}
token = config_next_token(&cursor);
if (token == NULL || config_str_to_mac(token, mac) != 0) {
snprintf(response, max_len, "ERROR: Invalid MAC format\r\n");
return AT_INVALID_PARAM;
}
memcpy(g_config.net.ip, ip, sizeof(ip));
memcpy(g_config.net.mask, mask, sizeof(mask));
memcpy(g_config.net.gw, gw, sizeof(gw));
memcpy(g_config.net.mac, mac, sizeof(mac));
snprintf(response, max_len, "OK\r\n");
return AT_NEED_REBOOT;
}
if (equals_ignore_case(p, "LINK?")) {
char rip_str[CONFIG_LINK_COUNT][16];
uint32_t i;
for (i = 0; i < CONFIG_LINK_COUNT; ++i) {
config_ip_to_str(g_config.links[i].remote_ip, rip_str[i]);
}
snprintf(response, max_len,
"+LINK:S1,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n"
"+LINK:S2,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n"
"+LINK:C1,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\n"
"+LINK:C2,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\nOK\r\n",
g_config.links[0].enabled, g_config.links[0].local_port, rip_str[0], g_config.links[0].remote_port, link_uart_to_str(g_config.links[0].uart),
g_config.links[1].enabled, g_config.links[1].local_port, rip_str[1], g_config.links[1].remote_port, link_uart_to_str(g_config.links[1].uart),
g_config.links[2].enabled, g_config.links[2].local_port, rip_str[2], g_config.links[2].remote_port, link_uart_to_str(g_config.links[2].uart),
g_config.links[3].enabled, g_config.links[3].local_port, rip_str[3], g_config.links[3].remote_port, link_uart_to_str(g_config.links[3].uart));
return AT_OK;
}
if (parse_command_with_value(p, "LINK", &value)) {
char value_copy[96];
char *cursor;
char *token;
uint32_t index;
uint32_t enabled;
uint32_t local_port;
uint32_t remote_port;
uint8_t rip[4];
uint8_t uart;
strncpy(value_copy, value, sizeof(value_copy) - 1u);
value_copy[sizeof(value_copy) - 1u] = '\0';
cursor = value_copy;
token = config_next_token(&cursor);
if (token == NULL || parse_link_name(token, &index) != 0) {
snprintf(response, max_len, "ERROR: Invalid route field\r\n");
return AT_INVALID_PARAM;
}
token = config_next_token(&cursor);
if (token == NULL) {
char rip_str[16];
config_ip_to_str(g_config.links[index].remote_ip, rip_str);
snprintf(response, max_len,
"+LINK:%s,EN=%u,LPORT=%u,RIP=%s,RPORT=%u,UART=%s\r\nOK\r\n",
link_index_to_name(index),
g_config.links[index].enabled,
g_config.links[index].local_port,
rip_str,
g_config.links[index].remote_port,
link_uart_to_str(g_config.links[index].uart));
return AT_OK;
}
if (parse_u32_value(token, 0u, 1u, &enabled) != 0) {
snprintf(response, max_len, "ERROR: Invalid value\r\n");
return AT_INVALID_PARAM;
}
token = config_next_token(&cursor);
if (token == NULL || parse_u32_value(token, 1u, 65535u, &local_port) != 0) {
snprintf(response, max_len, "ERROR: Invalid port\r\n");
return AT_INVALID_PARAM;
}
token = config_next_token(&cursor);
if (token == NULL || config_str_to_ip(token, rip) != 0) {
snprintf(response, max_len, "ERROR: Invalid remote IP format\r\n");
return AT_INVALID_PARAM;
}
token = config_next_token(&cursor);
if (token == NULL || parse_u32_value(token, 0u, 65535u, &remote_port) != 0) {
snprintf(response, max_len, "ERROR: Invalid port\r\n");
return AT_INVALID_PARAM;
}
token = config_next_token(&cursor);
if (token == NULL || parse_link_uart(token, &uart) != 0) {
snprintf(response, max_len, "ERROR: Invalid route field\r\n");
return AT_INVALID_PARAM;
}
g_config.links[index].enabled = (uint8_t)enabled;
g_config.links[index].local_port = (uint16_t)local_port;
memcpy(g_config.links[index].remote_ip, rip, sizeof(rip));
g_config.links[index].remote_port = (uint16_t)remote_port;
g_config.links[index].uart = uart;
snprintf(response, max_len, "OK\r\n");
return AT_NEED_REBOOT;
}
snprintf(response, max_len, "ERROR: Unknown command\r\n");
return AT_UNKNOWN_CMD;
}
void config_ip_to_str(const uint8_t *ip, char *str)
{
sprintf(str, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
}
int config_str_to_ip(const char *str, uint8_t *ip)
{
int a, b, c, d;
if (sscanf(str, "%d.%d.%d.%d", &a, &b, &c, &d) != 4) {
return -1;
}
if (a < 0 || a > 255 || b < 0 || b > 255 || c < 0 || c > 255 || d < 0 || d > 255) {
return -1;
}
ip[0] = (uint8_t)a;
ip[1] = (uint8_t)b;
ip[2] = (uint8_t)c;
ip[3] = (uint8_t)d;
return 0;
}
void config_mac_to_str(const uint8_t *mac, char *str)
{
sprintf(str, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
int config_str_to_mac(const char *str, uint8_t *mac)
{
int a[6];
int i;
if (sscanf(str, "%x:%x:%x:%x:%x:%x", &a[0], &a[1], &a[2], &a[3], &a[4], &a[5]) != 6 &&
sscanf(str, "%x-%x-%x-%x-%x-%x", &a[0], &a[1], &a[2], &a[3], &a[4], &a[5]) != 6) {
return -1;
}
for (i = 0; i < 6; ++i) {
if (a[i] < 0 || a[i] > 255) {
return -1;
}
mac[i] = (uint8_t)a[i];
}
return 0;
}
void config_uart_idle_handler(void)
{
uint16_t dma_counter = __HAL_DMA_GET_COUNTER(huart1.hdmarx);
uint16_t len = CONFIG_RX_BUFFER_SIZE - dma_counter;
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
HAL_StatusTypeDef hal_status;
route_send_result_t route_result;
if (g_uart1_tx_busy) {
return;
}
if (len > 0u && xConfigQueue != NULL) {
route_result = route_send_from_isr(xConfigQueue,
0u,
0u,
ROUTE_CONN_UART1,
g_uart1_rx_buffer,
len,
&xHigherPriorityTaskWoken);
if (route_result != ROUTE_SEND_OK) {
g_config_rx_route_fail_reason = route_result;
g_config_rx_route_fail_count += 1u;
}
}
hal_status = HAL_UART_DMAStop(&huart1);
if (hal_status != HAL_OK) {
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
return;
}
hal_status = HAL_UART_Receive_DMA(&huart1, g_uart1_rx_buffer, CONFIG_RX_BUFFER_SIZE);
if (hal_status != HAL_OK) {
__HAL_UART_DISABLE_IT(&huart1, UART_IT_IDLE);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
return;
}
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
void config_start_reception(void)
{
debug_log_write("[CFG] rx-start enter\r\n");
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
if (HAL_UART_Receive_DMA(&huart1, g_uart1_rx_buffer, CONFIG_RX_BUFFER_SIZE) != HAL_OK) {
debug_log_write("[CFG] rx-start fail\r\n");
Debug_TrapWithRttHint("cfg-rx-start-fail");
return;
}
debug_log_write("[CFG] rx-start exit\r\n");
}
static void config_respond_to_uart(route_msg_t *msg, const char *response)
{
if (msg->conn_type == ROUTE_CONN_UART1) {
g_uart1_tx_busy = true;
__HAL_UART_DISABLE_IT(&huart1, UART_IT_IDLE);
(void)HAL_UART_Transmit(&huart1, (const uint8_t *)response, (uint16_t)strlen(response), 200u);
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
g_uart1_tx_busy = false;
} else if (msg->src_id == ENDPOINT_UART2 || msg->src_id == ENDPOINT_UART3) {
uart_channel_t channel = (msg->src_id == ENDPOINT_UART3) ? UART_CHANNEL_U1 : UART_CHANNEL_U0;
uint8_t frame[ROUTE_MSG_MAX_PAYLOAD + 6u];
uint16_t frame_len = 0u;
uart_trans_send_result_t uart_result;
if (uart_mux_encode_frame(msg->src_id, 0u, (const uint8_t *)response, (uint16_t)strlen(response), frame, &frame_len, sizeof(frame))) {
uart_result = uart_trans_send_buffer(channel, frame, frame_len);
if (uart_result != UART_TRANS_SEND_OK) {
debug_log_printf("[CFG] resp-tx-fail ch=%u rc=%s len=%u\r\n",
(unsigned int)channel,
uart_trans_send_result_to_str(uart_result),
(unsigned int)frame_len);
}
} else {
debug_log_printf("[CFG] resp-enc-fail src=0x%02X len=%u\r\n",
(unsigned int)msg->src_id,
(unsigned int)strlen(response));
}
}
}
static void config_report_route_failures(uint32_t *reported_route_fail_count)
{
uint32_t fail_count;
route_send_result_t fail_reason;
if (reported_route_fail_count == NULL) {
return;
}
fail_count = g_config_rx_route_fail_count;
fail_reason = g_config_rx_route_fail_reason;
if (fail_count != *reported_route_fail_count) {
*reported_route_fail_count = fail_count;
debug_log_printf("[CFG] rx-route-fail rc=%s cnt=%lu\r\n",
route_send_result_to_str(fail_reason),
(unsigned long)fail_count);
}
}
void ConfigTask(void *argument)
{
route_msg_t *msg;
at_result_t result;
uint32_t reported_route_fail_count = 0u;
(void)argument;
debug_log_write("[CFG] task-entry\r\n");
config_start_reception();
debug_log_write("[CFG] task-ready\r\n");
for (;;) {
config_report_route_failures(&reported_route_fail_count);
if (xQueueReceive(xConfigQueue, &msg, pdMS_TO_TICKS(50)) != pdPASS) {
continue;
}
config_report_route_failures(&reported_route_fail_count);
if (msg->len >= sizeof(g_config_cmd_buffer)) {
msg->len = sizeof(g_config_cmd_buffer) - 1u;
}
memcpy(g_config_cmd_buffer, msg->data, msg->len);
g_config_cmd_buffer[msg->len] = '\0';
result = config_process_at_cmd(g_config_cmd_buffer, g_config_response_buffer, sizeof(g_config_response_buffer));
config_respond_to_uart(msg, g_config_response_buffer);
if (result == AT_NEED_REBOOT) {
config_respond_to_uart(msg, "Note: Use AT+SAVE then AT+RESET to apply changes\r\n");
}
route_msg_free(msg);
if (g_reset_requested) {
g_reset_requested = false;
vTaskDelay(pdMS_TO_TICKS(100));
NVIC_SystemReset();
}
}
}
uint8_t config_link_index_to_endpoint(uint8_t index)
{
switch (index) {
case CONFIG_LINK_S1:
return ENDPOINT_S1;
case CONFIG_LINK_S2:
return ENDPOINT_S2;
case CONFIG_LINK_C1:
return ENDPOINT_C1;
case CONFIG_LINK_C2:
return ENDPOINT_C2;
default:
return 0u;
}
}
uint8_t config_uart_index_to_endpoint(uint8_t uart_index)
{
return (uart_index == LINK_UART_U1) ? ENDPOINT_UART3 : ENDPOINT_UART2;
}
bool config_endpoint_is_single(uint8_t endpoint)
{
return endpoint == ENDPOINT_C1 || endpoint == ENDPOINT_C2 ||
endpoint == ENDPOINT_UART2 || endpoint == ENDPOINT_UART3 ||
endpoint == ENDPOINT_S1 || endpoint == ENDPOINT_S2;
}
+106
View File
@@ -0,0 +1,106 @@
#ifndef CONFIG_H
#define CONFIG_H
#include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#define CONFIG_MAGIC 0x54435055u
#define CONFIG_VERSION 0x0003u
#define CONFIG_UART_COUNT 2u
#define CONFIG_LINK_COUNT 4u
#define CONFIG_LINK_S1 0u
#define CONFIG_LINK_S2 1u
#define CONFIG_LINK_C1 2u
#define CONFIG_LINK_C2 3u
#define ENDPOINT_C1 0x01u
#define ENDPOINT_C2 0x02u
#define ENDPOINT_UART2 0x04u
#define ENDPOINT_UART3 0x08u
#define ENDPOINT_S1 0x10u
#define ENDPOINT_S2 0x20u
#define LINK_UART_U0 0u
#define LINK_UART_U1 1u
typedef enum {
MUX_MODE_RAW = 0,
MUX_MODE_FRAME = 1
} mux_mode_t;
typedef struct {
uint8_t ip[4];
uint8_t mask[4];
uint8_t gw[4];
uint8_t mac[6];
uint8_t reserved[2];
} net_config_t;
typedef struct {
uint8_t enabled;
uint8_t uart;
uint16_t local_port;
uint8_t remote_ip[4];
uint16_t remote_port;
uint16_t reserved;
} link_config_t;
typedef struct {
uint32_t magic;
uint16_t version;
uint8_t mux_mode;
uint8_t reserved0;
net_config_t net;
link_config_t links[CONFIG_LINK_COUNT];
uint32_t uart_baudrate[CONFIG_UART_COUNT];
uint32_t reconnect_interval_ms;
uint32_t crc;
} device_config_t;
#define DEFAULT_NET_IP {192, 168, 31, 100}
#define DEFAULT_NET_MASK {255, 255, 255, 0}
#define DEFAULT_NET_GW {192, 168, 31, 1}
#define DEFAULT_NET_MAC {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
#define DEFAULT_UART_BAUDRATE 115200u
#define DIAG_CH390_RAW_POLL 0
typedef enum {
AT_OK = 0,
AT_ERROR,
AT_INVALID_PARAM,
AT_UNKNOWN_CMD,
AT_SAVE_FAILED,
AT_NEED_REBOOT
} at_result_t;
int config_init(void);
int config_load(void);
int config_save(void);
void config_set_defaults(void);
const device_config_t *config_get(void);
device_config_t *config_get_mutable(void);
uint32_t config_get_uart_baudrate(uint8_t uart_index);
at_result_t config_process_at_cmd(const char *cmd, char *response, uint16_t max_len);
void ConfigTask(void *argument);
void config_uart_idle_handler(void);
void config_start_reception(void);
void config_ip_to_str(const uint8_t *ip, char *str);
int config_str_to_ip(const char *str, uint8_t *ip);
void config_mac_to_str(const uint8_t *mac, char *str);
int config_str_to_mac(const char *str, uint8_t *mac);
uint8_t config_link_index_to_endpoint(uint8_t index);
uint8_t config_uart_index_to_endpoint(uint8_t uart_index);
bool config_endpoint_is_single(uint8_t endpoint);
#ifdef __cplusplus
}
#endif
#endif
+278
View File
@@ -0,0 +1,278 @@
/**
* @file flash_param.c
* @brief Flash parameter storage module implementation
*/
#include "flash_param.h"
#include "stm32f1xx_hal.h"
#include <string.h>
/*---------------------------------------------------------------------------
* Private Definitions
*---------------------------------------------------------------------------*/
/* CRC32 polynomial (IEEE 802.3) */
#define CRC32_POLYNOMIAL 0xEDB88320
/*---------------------------------------------------------------------------
* Private Variables
*---------------------------------------------------------------------------*/
/* CRC32 lookup table */
static uint32_t g_crc_table[256];
static bool g_crc_table_initialized = false;
/*---------------------------------------------------------------------------
* Private Functions
*---------------------------------------------------------------------------*/
/**
* @brief Initialize CRC32 lookup table
*/
static void crc32_init_table(void)
{
uint32_t i, j, crc;
for (i = 0; i < 256; i++)
{
crc = i;
for (j = 0; j < 8; j++)
{
if (crc & 1)
{
crc = (crc >> 1) ^ CRC32_POLYNOMIAL;
}
else
{
crc >>= 1;
}
}
g_crc_table[i] = crc;
}
g_crc_table_initialized = true;
}
/**
* @brief Unlock Flash for writing
*/
static HAL_StatusTypeDef flash_unlock(void)
{
return HAL_FLASH_Unlock();
}
/**
* @brief Lock Flash after writing
*/
static void flash_lock(void)
{
HAL_FLASH_Lock();
}
/**
* @brief Erase Flash page
*/
static HAL_StatusTypeDef flash_erase_page(uint32_t page_addr)
{
FLASH_EraseInitTypeDef erase_init;
uint32_t page_error;
HAL_StatusTypeDef status;
erase_init.TypeErase = FLASH_TYPEERASE_PAGES;
erase_init.PageAddress = page_addr;
erase_init.NbPages = 1;
status = HAL_FLASHEx_Erase(&erase_init, &page_error);
return status;
}
/**
* @brief Program Flash half-word (16-bit)
*/
static HAL_StatusTypeDef flash_program_halfword(uint32_t addr, uint16_t data)
{
return HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, addr, data);
}
/*---------------------------------------------------------------------------
* Public Functions
*---------------------------------------------------------------------------*/
/**
* @brief Initialize Flash parameter storage
*/
int flash_param_init(void)
{
/* Initialize CRC table */
if (!g_crc_table_initialized)
{
crc32_init_table();
}
return 0;
}
/**
* @brief Read parameters from Flash
*/
int flash_param_read(void *data, uint32_t len)
{
if (data == NULL || len == 0)
{
return -1;
}
/* Check if length exceeds available space */
if (len > FLASH_PARAM_PAGE_SIZE)
{
return -1;
}
/* Direct memory read from Flash */
memcpy(data, (const void *)FLASH_PARAM_START_ADDR, len);
return 0;
}
/**
* @brief Write parameters to Flash
*/
int flash_param_write(const void *data, uint32_t len)
{
HAL_StatusTypeDef status;
uint32_t addr;
const uint8_t *src;
uint16_t halfword;
uint32_t i;
if (data == NULL || len == 0)
{
return -1;
}
/* Check if length exceeds available space */
if (len > FLASH_PARAM_PAGE_SIZE)
{
return -1;
}
/* Unlock Flash */
status = flash_unlock();
if (status != HAL_OK)
{
return -1;
}
/* Erase the page */
status = flash_erase_page(FLASH_PARAM_START_ADDR);
if (status != HAL_OK)
{
flash_lock();
return -1;
}
/* Program Flash (half-word at a time for STM32F1) */
addr = FLASH_PARAM_START_ADDR;
src = (const uint8_t *)data;
for (i = 0; i < len; i += 2)
{
/* Build half-word (little-endian) */
halfword = src[i];
if (i + 1 < len)
{
halfword |= ((uint16_t)src[i + 1]) << 8;
}
else
{
halfword |= 0xFF00; /* Pad with 0xFF */
}
status = flash_program_halfword(addr, halfword);
if (status != HAL_OK)
{
flash_lock();
return -1;
}
addr += 2;
}
/* Lock Flash */
flash_lock();
/* Verify write */
if (memcmp((const void *)FLASH_PARAM_START_ADDR, data, len) != 0)
{
return -1;
}
return 0;
}
/**
* @brief Erase parameter storage area
*/
int flash_param_erase(void)
{
HAL_StatusTypeDef status;
/* Unlock Flash */
status = flash_unlock();
if (status != HAL_OK)
{
return -1;
}
/* Erase the page */
status = flash_erase_page(FLASH_PARAM_START_ADDR);
/* Lock Flash */
flash_lock();
return (status == HAL_OK) ? 0 : -1;
}
/**
* @brief Calculate CRC32
*/
uint32_t flash_param_crc32(const void *data, uint32_t len)
{
const uint8_t *p = (const uint8_t *)data;
uint32_t crc = 0xFFFFFFFF;
uint32_t i;
/* Initialize table if needed */
if (!g_crc_table_initialized)
{
crc32_init_table();
}
for (i = 0; i < len; i++)
{
crc = g_crc_table[(crc ^ p[i]) & 0xFF] ^ (crc >> 8);
}
return crc ^ 0xFFFFFFFF;
}
/**
* @brief Verify parameter storage integrity
*/
int flash_param_verify(void)
{
uint32_t magic;
/* Read magic number */
memcpy(&magic, (const void *)FLASH_PARAM_START_ADDR, sizeof(magic));
/* Check if Flash is erased (all 0xFF) */
if (magic == 0xFFFFFFFF)
{
return -1; /* Empty/erased */
}
return 0;
}
+74
View File
@@ -0,0 +1,74 @@
/**
* @file flash_param.h
* @brief Flash parameter storage module for TCP2UART
*
* Stores configuration parameters in STM32F103 internal Flash.
* Uses the last page of Flash (1KB) for parameter storage.
*/
#ifndef __FLASH_PARAM_H__
#define __FLASH_PARAM_H__
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Flash configuration for the current STM32F103RDT6 target (384KB Flash). */
#define FLASH_PARAM_PAGE_SIZE 1024 /* 1KB per page for STM32F103 */
#define FLASH_PARAM_START_ADDR 0x0805FC00 /* Last 1KB page of 384KB Flash */
#define FLASH_PARAM_END_ADDR 0x08060000 /* End of 384KB Flash */
/* Historical reference: STM32F103RCT6 would use 0x0803FC00 as its last page. */
/**
* @brief Initialize Flash parameter storage
* @return 0 on success, negative on error
*/
int flash_param_init(void);
/**
* @brief Read parameters from Flash
* @param data Output buffer
* @param len Length to read
* @return 0 on success, negative on error
*/
int flash_param_read(void *data, uint32_t len);
/**
* @brief Write parameters to Flash
* @param data Data to write
* @param len Length to write
* @return 0 on success, negative on error
*
* Note: This function will erase the Flash page before writing.
*/
int flash_param_write(const void *data, uint32_t len);
/**
* @brief Erase parameter storage area
* @return 0 on success, negative on error
*/
int flash_param_erase(void);
/**
* @brief Calculate CRC32 for data
* @param data Data buffer
* @param len Data length
* @return CRC32 value
*/
uint32_t flash_param_crc32(const void *data, uint32_t len);
/**
* @brief Verify parameter storage integrity
* @return 0 if valid, negative if invalid or corrupted
*/
int flash_param_verify(void);
#ifdef __cplusplus
}
#endif
#endif /* __FLASH_PARAM_H__ */
+227
View File
@@ -0,0 +1,227 @@
#include "route_msg.h"
#include <string.h>
#include "task.h"
typedef struct {
route_msg_t msg;
uint8_t data[ROUTE_MSG_MAX_PAYLOAD];
uint8_t in_use;
} route_slot_t;
static route_slot_t g_route_slots[ROUTE_MSG_POOL_SIZE];
const char *route_send_result_to_str(route_send_result_t result)
{
switch (result) {
case ROUTE_SEND_OK:
return "ok";
case ROUTE_SEND_INVALID_INPUT:
return "invalid";
case ROUTE_SEND_POOL_EXHAUSTED:
return "pool";
case ROUTE_SEND_QUEUE_FULL:
return "queue";
default:
return "unknown";
}
}
void route_msg_init(void)
{
memset(g_route_slots, 0, sizeof(g_route_slots));
}
static route_msg_t *route_msg_try_alloc_locked(void)
{
uint32_t index;
for (index = 0; index < ROUTE_MSG_POOL_SIZE; ++index) {
if (g_route_slots[index].in_use == 0u) {
g_route_slots[index].in_use = 1u;
g_route_slots[index].msg.data = g_route_slots[index].data;
g_route_slots[index].msg.len = 0u;
g_route_slots[index].msg.src_id = 0u;
g_route_slots[index].msg.dst_mask = 0u;
g_route_slots[index].msg.conn_type = 0u;
return &g_route_slots[index].msg;
}
}
return NULL;
}
route_msg_t *route_msg_alloc(TickType_t wait_ticks)
{
TickType_t start_tick = xTaskGetTickCount();
route_msg_t *msg;
do {
taskENTER_CRITICAL();
msg = route_msg_try_alloc_locked();
taskEXIT_CRITICAL();
if (msg != NULL) {
return msg;
}
if (wait_ticks == 0u) {
break;
}
vTaskDelay(pdMS_TO_TICKS(1));
} while ((xTaskGetTickCount() - start_tick) < wait_ticks);
return NULL;
}
route_msg_t *route_msg_alloc_from_isr(BaseType_t *xHigherPriorityTaskWoken)
{
route_msg_t *msg;
UBaseType_t saved_interrupt_status;
(void)xHigherPriorityTaskWoken;
saved_interrupt_status = taskENTER_CRITICAL_FROM_ISR();
msg = route_msg_try_alloc_locked();
taskEXIT_CRITICAL_FROM_ISR(saved_interrupt_status);
return msg;
}
void route_msg_free(route_msg_t *msg)
{
uint32_t index;
if (msg == NULL) {
return;
}
taskENTER_CRITICAL();
for (index = 0; index < ROUTE_MSG_POOL_SIZE; ++index) {
if (&g_route_slots[index].msg == msg) {
g_route_slots[index].in_use = 0u;
g_route_slots[index].msg.len = 0u;
break;
}
}
taskEXIT_CRITICAL();
}
void route_msg_free_from_isr(route_msg_t *msg)
{
uint32_t index;
UBaseType_t saved_interrupt_status;
if (msg == NULL) {
return;
}
saved_interrupt_status = taskENTER_CRITICAL_FROM_ISR();
for (index = 0; index < ROUTE_MSG_POOL_SIZE; ++index) {
if (&g_route_slots[index].msg == msg) {
g_route_slots[index].in_use = 0u;
g_route_slots[index].msg.len = 0u;
break;
}
}
taskEXIT_CRITICAL_FROM_ISR(saved_interrupt_status);
}
static route_send_result_t route_prepare(route_msg_t *msg,
uint8_t src_id,
uint8_t dst_mask,
uint8_t conn_type,
const uint8_t *data,
uint16_t len)
{
if (msg == NULL || data == NULL || len == 0u || len > ROUTE_MSG_MAX_PAYLOAD) {
return ROUTE_SEND_INVALID_INPUT;
}
msg->src_id = src_id;
msg->dst_mask = dst_mask;
msg->conn_type = conn_type;
msg->len = len;
memcpy(msg->data, data, len);
return ROUTE_SEND_OK;
}
static route_send_result_t route_validate_args(QueueHandle_t queue,
const uint8_t *data,
uint16_t len)
{
if (queue == NULL || data == NULL || len == 0u || len > ROUTE_MSG_MAX_PAYLOAD) {
return ROUTE_SEND_INVALID_INPUT;
}
return ROUTE_SEND_OK;
}
route_send_result_t route_send(QueueHandle_t queue,
uint8_t src_id,
uint8_t dst_mask,
uint8_t conn_type,
const uint8_t *data,
uint16_t len,
TickType_t wait_ticks)
{
route_send_result_t result;
route_msg_t *msg;
result = route_validate_args(queue, data, len);
if (result != ROUTE_SEND_OK) {
return result;
}
msg = route_msg_alloc(wait_ticks);
if (msg == NULL) {
return ROUTE_SEND_POOL_EXHAUSTED;
}
result = route_prepare(msg, src_id, dst_mask, conn_type, data, len);
if (result != ROUTE_SEND_OK) {
route_msg_free(msg);
return result;
}
if (xQueueSend(queue, &msg, wait_ticks) != pdPASS) {
route_msg_free(msg);
return ROUTE_SEND_QUEUE_FULL;
}
return ROUTE_SEND_OK;
}
route_send_result_t route_send_from_isr(QueueHandle_t queue,
uint8_t src_id,
uint8_t dst_mask,
uint8_t conn_type,
const uint8_t *data,
uint16_t len,
BaseType_t *xHigherPriorityTaskWoken)
{
route_send_result_t result;
route_msg_t *msg;
result = route_validate_args(queue, data, len);
if (result != ROUTE_SEND_OK) {
return result;
}
msg = route_msg_alloc_from_isr(xHigherPriorityTaskWoken);
if (msg == NULL) {
return ROUTE_SEND_POOL_EXHAUSTED;
}
result = route_prepare(msg, src_id, dst_mask, conn_type, data, len);
if (result != ROUTE_SEND_OK) {
route_msg_free_from_isr(msg);
return result;
}
if (xQueueSendFromISR(queue, &msg, xHigherPriorityTaskWoken) != pdPASS) {
route_msg_free_from_isr(msg);
return ROUTE_SEND_QUEUE_FULL;
}
return ROUTE_SEND_OK;
}
+72
View File
@@ -0,0 +1,72 @@
#ifndef ROUTE_MSG_H
#define ROUTE_MSG_H
#include <stdbool.h>
#include <stdint.h>
#include "FreeRTOS.h"
#include "queue.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifndef ROUTE_MSG_POOL_SIZE
#define ROUTE_MSG_POOL_SIZE 8u
#endif
#ifndef ROUTE_MSG_MAX_PAYLOAD
#define ROUTE_MSG_MAX_PAYLOAD 512u
#endif
typedef enum {
ROUTE_CONN_UART1 = 0,
ROUTE_CONN_UART2,
ROUTE_CONN_UART3,
ROUTE_CONN_S1,
ROUTE_CONN_S2,
ROUTE_CONN_C1,
ROUTE_CONN_C2
} route_conn_type_t;
typedef enum {
ROUTE_SEND_OK = 0,
ROUTE_SEND_INVALID_INPUT,
ROUTE_SEND_POOL_EXHAUSTED,
ROUTE_SEND_QUEUE_FULL
} route_send_result_t;
typedef struct {
uint8_t src_id;
uint8_t dst_mask;
uint16_t len;
uint8_t conn_type;
uint8_t *data;
} route_msg_t;
void route_msg_init(void);
route_msg_t *route_msg_alloc(TickType_t wait_ticks);
route_msg_t *route_msg_alloc_from_isr(BaseType_t *xHigherPriorityTaskWoken);
void route_msg_free(route_msg_t *msg);
void route_msg_free_from_isr(route_msg_t *msg);
const char *route_send_result_to_str(route_send_result_t result);
route_send_result_t route_send(QueueHandle_t queue,
uint8_t src_id,
uint8_t dst_mask,
uint8_t conn_type,
const uint8_t *data,
uint16_t len,
TickType_t wait_ticks);
route_send_result_t route_send_from_isr(QueueHandle_t queue,
uint8_t src_id,
uint8_t dst_mask,
uint8_t conn_type,
const uint8_t *data,
uint16_t len,
BaseType_t *xHigherPriorityTaskWoken);
#ifdef __cplusplus
}
#endif
#endif
+192
View File
@@ -0,0 +1,192 @@
#include "task_net_poll.h"
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "CH390.h"
#include <string.h>
#if !DIAG_CH390_RAW_POLL
#include "lwip/tcpip.h"
#include "lwip/ip4_addr.h"
#endif
#include "ethernetif.h"
#include "config.h"
#include "app_runtime.h"
#include "debug_log.h"
#define CH390_RESTART_HOLD_DOWN_MS 500u
#define NETWORK_TASK_DELETE_SETTLE_MS 50u
#define CH390_EXPECTED_VENDOR_ID 0x1C00u
#define CH390_EXPECTED_PRODUCT_ID 0x9151u
static void net_poll_wait_for_network_tasks_stop(void)
{
while (app_network_tasks_are_stopped() == pdFALSE) {
vTaskDelay(pdMS_TO_TICKS(20));
}
}
static BaseType_t net_poll_restart_network_stack(const device_config_t *cfg)
{
#if !DIAG_CH390_RAW_POLL
ip4_addr_t ipaddr;
ip4_addr_t netmask;
ip4_addr_t gateway;
uint16_t vendor_id;
uint16_t product_id;
uint8_t revision;
IP4_ADDR(&ipaddr, cfg->net.ip[0], cfg->net.ip[1], cfg->net.ip[2], cfg->net.ip[3]);
IP4_ADDR(&netmask, cfg->net.mask[0], cfg->net.mask[1], cfg->net.mask[2], cfg->net.mask[3]);
IP4_ADDR(&gateway, cfg->net.gw[0], cfg->net.gw[1], cfg->net.gw[2], cfg->net.gw[3]);
#endif
ethernetif_force_link_down();
g_netif_ready = pdFALSE;
app_request_network_task_stop();
net_poll_wait_for_network_tasks_stop();
vTaskDelay(pdMS_TO_TICKS(NETWORK_TASK_DELETE_SETTLE_MS));
vTaskDelay(pdMS_TO_TICKS(CH390_RESTART_HOLD_DOWN_MS));
#if DIAG_CH390_RAW_POLL
ethernetif_diag_ch390_init();
#else
ethernetif_force_full_recovery(&ipaddr, &netmask, &gateway, cfg->net.mac);
vendor_id = ethernetif_ch390_get_vendor_id();
product_id = ethernetif_ch390_get_product_id();
revision = ethernetif_ch390_get_revision();
if ((vendor_id != CH390_EXPECTED_VENDOR_ID) || (product_id != CH390_EXPECTED_PRODUCT_ID)) {
debug_log_printf("[NET] restart-recovery id-warn vid=0x%04X pid=0x%04X rev=0x%02X free=%lu min=%lu\r\n",
(unsigned int)vendor_id,
(unsigned int)product_id,
(unsigned int)revision,
(unsigned long)xPortGetFreeHeapSize(),
(unsigned long)xPortGetMinimumEverFreeHeapSize());
}
#endif
app_clear_network_task_stop();
g_netif_ready = pdTRUE;
app_start_network_tasks();
app_clear_network_restart_request();
return pdTRUE;
}
void NetPollTask(void *argument)
{
const device_config_t *cfg;
#if !DIAG_CH390_RAW_POLL
ip4_addr_t ipaddr;
ip4_addr_t netmask;
ip4_addr_t gateway;
#else
static uint8_t s_diag_rx_buffer[CH390_PKT_MAX];
#endif
BaseType_t loop_logged = pdFALSE;
(void)argument;
debug_log_write("[NET] task-entry\r\n");
cfg = config_get();
debug_log_write("[NET] config-ok\r\n");
#if DIAG_CH390_RAW_POLL
g_netif_phase = 1u;
debug_log_write("[NET] diag-ch390-init enter\r\n");
ethernetif_diag_ch390_init();
g_netif_phase = 7u;
debug_log_write("[NET] diag-ch390-init exit\r\n");
if (g_netif_init_ok != 1)
{
for (;;)
{
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
g_netif_ready = pdTRUE;
app_start_network_tasks();
debug_log_write("[NET] diag-ch390-ready\r\n");
#else
debug_log_write("[NET] tcpip-init enter\r\n");
tcpip_init(NULL, NULL);
debug_log_write("[NET] tcpip-init exit\r\n");
vTaskDelay(pdMS_TO_TICKS(50));
debug_log_write("[NET] post-delay\r\n");
IP4_ADDR(&ipaddr, cfg->net.ip[0], cfg->net.ip[1], cfg->net.ip[2], cfg->net.ip[3]);
IP4_ADDR(&netmask, cfg->net.mask[0], cfg->net.mask[1], cfg->net.mask[2], cfg->net.mask[3]);
IP4_ADDR(&gateway, cfg->net.gw[0], cfg->net.gw[1], cfg->net.gw[2], cfg->net.gw[3]);
g_netif_phase = 1u;
debug_log_printf("[NET] netif-call hwm=%lu\r\n", (unsigned long)uxTaskGetStackHighWaterMark(NULL));
debug_log_write("[NET] netif-init enter\r\n");
lwip_netif_init(&ipaddr, &netmask, &gateway);
g_netif_phase = 7u;
debug_log_write("[NET] netif-init exit\r\n");
debug_log_printf("[NET] post-init ok=%ld hwm=%lu free=%lu min=%lu\r\n",
(long)g_netif_init_ok,
(unsigned long)uxTaskGetStackHighWaterMark(NULL),
(unsigned long)xPortGetFreeHeapSize(),
(unsigned long)xPortGetMinimumEverFreeHeapSize());
if (g_netif_init_ok != 1) {
for (;;) {
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
debug_log_write("[NET] pre-ready\r\n");
g_netif_ready = pdTRUE;
debug_log_write("[NET] start-network-tasks call\r\n");
app_start_network_tasks();
debug_log_printf("[NET] post-ready free=%lu min=%lu\r\n",
(unsigned long)xPortGetFreeHeapSize(),
(unsigned long)xPortGetMinimumEverFreeHeapSize());
debug_log_write("[NET] netif-ready\r\n");
#endif
for (;;) {
if (loop_logged == pdFALSE) {
g_netif_phase = 8u;
debug_log_write("[NET] loop-enter\r\n");
loop_logged = pdTRUE;
}
if (app_network_restart_requested() != pdFALSE) {
(void)net_poll_restart_network_stack(cfg);
}
(void)xSemaphoreTake(xNetSemaphore, pdMS_TO_TICKS(2));
#if DIAG_CH390_RAW_POLL
ethernetif_diag_poll_status();
if (ch390_read_reg(CH390_NSR) & NSR_RXRDY)
{
uint8_t rx_status = 0u;
uint32_t rx_len = ch390_runtime_receive_packet(s_diag_rx_buffer, &rx_status);
if (rx_len > 0u)
{
debug_log_printf("[RAW] rx len=%lu st=0x%02X h=%02X %02X %02X %02X\r\n",
(unsigned long)rx_len,
(unsigned int)rx_status,
(unsigned int)s_diag_rx_buffer[0],
(unsigned int)s_diag_rx_buffer[1],
(unsigned int)s_diag_rx_buffer[2],
(unsigned int)s_diag_rx_buffer[3]);
}
}
#else
if (g_netif_ready != pdFALSE) {
ethernetif_poll();
ethernetif_check_link();
}
#endif
}
}
+14
View File
@@ -0,0 +1,14 @@
#ifndef TASK_NET_POLL_H
#define TASK_NET_POLL_H
#ifdef __cplusplus
extern "C" {
#endif
void NetPollTask(void *argument);
#ifdef __cplusplus
}
#endif
#endif
+258
View File
@@ -0,0 +1,258 @@
#include "tcp_client.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "lwip/api.h"
#include "lwip/ip_addr.h"
#include "lwip/tcp.h"
#include "lwip/tcpip.h"
#include "app_runtime.h"
#include "config.h"
#include "debug_log.h"
#include "ethernetif.h"
#include "route_msg.h"
#define TCP_CLIENT_CONNECT_TIMEOUT_MS 500
#define TCP_CLIENT_RECONNECT_INTERVAL_MS 3000u
#define TCP_CLIENT_STOP_POLL_MS 50u
static BaseType_t tcp_client_stop_requested(void)
{
return (app_network_task_stop_requested() != pdFALSE) ? pdTRUE : pdFALSE;
}
static BaseType_t tcp_client_delay_with_stop(uint32_t delay_ms)
{
uint32_t remaining_ms = delay_ms;
while (remaining_ms > 0u) {
uint32_t slice_ms = (remaining_ms > TCP_CLIENT_STOP_POLL_MS) ? TCP_CLIENT_STOP_POLL_MS : remaining_ms;
if (tcp_client_stop_requested() != pdFALSE) {
return pdFALSE;
}
vTaskDelay(pdMS_TO_TICKS(slice_ms));
remaining_ms -= slice_ms;
}
return (tcp_client_stop_requested() == pdFALSE) ? pdTRUE : pdFALSE;
}
static void tcp_client_abort_and_delete(struct netconn *conn, uint8_t link_index)
{
struct tcp_pcb *pcb;
if (conn == NULL) {
return;
}
pcb = conn->pcb.tcp;
if (pcb != NULL) {
LOCK_TCPIP_CORE();
pcb = conn->pcb.tcp;
if (pcb != NULL) {
tcp_abort(pcb);
conn->pcb.tcp = NULL;
conn->state = NETCONN_NONE;
debug_log_printf("[CLI] idx=%u abort-close\r\n", (unsigned int)link_index);
}
UNLOCK_TCPIP_CORE();
}
netconn_delete(conn);
}
static err_t tcp_client_worker(struct netconn *conn, uint8_t link_index)
{
struct netbuf *buf;
const device_config_t *cfg = config_get();
uint8_t uart_endpoint = config_uart_index_to_endpoint(cfg->links[link_index].uart);
uint8_t src_endpoint = config_link_index_to_endpoint(link_index);
err_t err;
route_msg_t *tx_msg;
route_send_result_t route_result;
netconn_set_recvtimeout(conn, 10);
for (;;) {
if (tcp_client_stop_requested() != pdFALSE) {
return ERR_CLSD;
}
err = netconn_recv(conn, &buf);
if (err == ERR_OK) {
do {
void *data;
uint16_t len;
netbuf_data(buf, &data, &len);
route_result = route_send(xTcpRxQueue,
src_endpoint,
uart_endpoint,
(link_index == CONFIG_LINK_C1) ? ROUTE_CONN_C1 : ROUTE_CONN_C2,
(const uint8_t *)data,
len,
pdMS_TO_TICKS(10));
if (route_result != ROUTE_SEND_OK) {
debug_log_printf("[CLI] idx=%u rx-route-fail rc=%s len=%u\r\n",
(unsigned int)link_index,
route_send_result_to_str(route_result),
(unsigned int)len);
netbuf_delete(buf);
return ERR_CLSD;
}
} while (netbuf_next(buf) >= 0);
netbuf_delete(buf);
} else if (err == ERR_TIMEOUT) {
if (tcp_client_stop_requested() != pdFALSE) {
return ERR_CLSD;
}
} else {
return err;
}
while (xQueueReceive(xLinkTxQueues[link_index], &tx_msg, 0) == pdPASS) {
if (tcp_client_stop_requested() != pdFALSE) {
route_msg_free(tx_msg);
return ERR_CLSD;
}
err = netconn_write(conn, tx_msg->data, tx_msg->len, NETCONN_COPY);
route_msg_free(tx_msg);
if (err != ERR_OK) {
return err;
}
}
}
}
static void tcp_client_task(uint8_t link_index)
{
const device_config_t *cfg;
struct netconn *conn;
ip_addr_t remote_ip;
uint32_t delay_ms;
err_t err;
uint8_t first_connect_deferred;
netconn_thread_init();
first_connect_deferred = (link_index == CONFIG_LINK_C1) ? 1u : 0u;
for (;;) {
if (tcp_client_stop_requested() != pdFALSE) {
break;
}
while ((g_netif_ready == pdFALSE) || (ethernetif_link_is_up() == 0u)) {
if (tcp_client_stop_requested() != pdFALSE) {
goto exit_task;
}
vTaskDelay(pdMS_TO_TICKS(100));
}
cfg = config_get();
if (cfg->links[link_index].enabled == 0u) {
if (tcp_client_stop_requested() != pdFALSE) {
break;
}
if (tcp_client_delay_with_stop(500u) == pdFALSE) {
break;
}
continue;
}
delay_ms = TCP_CLIENT_RECONNECT_INTERVAL_MS;
if (first_connect_deferred != 0u) {
first_connect_deferred = 0u;
debug_log_write("[CLI] C1 first-connect defer\r\n");
if (tcp_client_delay_with_stop(delay_ms) == pdFALSE) {
break;
}
continue;
}
conn = netconn_new(NETCONN_TCP);
if (conn == NULL) {
if (tcp_client_delay_with_stop(delay_ms) == pdFALSE) {
break;
}
continue;
}
if (cfg->links[link_index].local_port != 0u) {
err = netconn_bind(conn, IP_ADDR_ANY, cfg->links[link_index].local_port);
if (err != ERR_OK) {
debug_log_printf("[CLI] idx=%u bind-fail err=%d lport=%u\r\n",
(unsigned int)link_index,
(int)err,
(unsigned int)cfg->links[link_index].local_port);
netconn_delete(conn);
if (tcp_client_delay_with_stop(delay_ms) == pdFALSE) {
break;
}
continue;
}
}
IP_ADDR4(&remote_ip,
cfg->links[link_index].remote_ip[0],
cfg->links[link_index].remote_ip[1],
cfg->links[link_index].remote_ip[2],
cfg->links[link_index].remote_ip[3]);
netconn_set_recvtimeout(conn, TCP_CLIENT_CONNECT_TIMEOUT_MS);
err = netconn_connect(conn, &remote_ip, cfg->links[link_index].remote_port);
if (err == ERR_OK) {
debug_log_printf("[CLI] idx=%u connect-ok\r\n", (unsigned int)link_index);
(void)tcp_client_worker(conn, link_index);
} else {
if (err == ERR_TIMEOUT) {
debug_log_printf("[CLI] idx=%u connect-timeout ms=%u rip=%u.%u.%u.%u rport=%u\r\n",
(unsigned int)link_index,
(unsigned int)TCP_CLIENT_CONNECT_TIMEOUT_MS,
(unsigned int)cfg->links[link_index].remote_ip[0],
(unsigned int)cfg->links[link_index].remote_ip[1],
(unsigned int)cfg->links[link_index].remote_ip[2],
(unsigned int)cfg->links[link_index].remote_ip[3],
(unsigned int)cfg->links[link_index].remote_port);
} else {
debug_log_printf("[CLI] idx=%u connect-fail err=%d rip=%u.%u.%u.%u rport=%u\r\n",
(unsigned int)link_index,
(int)err,
(unsigned int)cfg->links[link_index].remote_ip[0],
(unsigned int)cfg->links[link_index].remote_ip[1],
(unsigned int)cfg->links[link_index].remote_ip[2],
(unsigned int)cfg->links[link_index].remote_ip[3],
(unsigned int)cfg->links[link_index].remote_port);
}
}
tcp_client_abort_and_delete(conn, link_index);
if (tcp_client_stop_requested() != pdFALSE) {
break;
}
if (tcp_client_delay_with_stop(delay_ms) == pdFALSE) {
break;
}
}
exit_task:
netconn_thread_cleanup();
app_on_network_task_exit(xTaskGetCurrentTaskHandle());
vTaskDelete(NULL);
}
void TcpCliTask_C1(void *argument)
{
(void)argument;
tcp_client_task(CONFIG_LINK_C1);
}
void TcpCliTask_C2(void *argument)
{
(void)argument;
tcp_client_task(CONFIG_LINK_C2);
}
+15
View File
@@ -0,0 +1,15 @@
#ifndef TCP_CLIENT_H
#define TCP_CLIENT_H
#ifdef __cplusplus
extern "C" {
#endif
void TcpCliTask_C1(void *argument);
void TcpCliTask_C2(void *argument);
#ifdef __cplusplus
}
#endif
#endif
+185
View File
@@ -0,0 +1,185 @@
#include "tcp_server.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "lwip/api.h"
#include "lwip/ip_addr.h"
#include "app_runtime.h"
#include "config.h"
#include "debug_log.h"
#include "route_msg.h"
#define TCP_SERVER_ACCEPT_TIMEOUT_MS 100
#define TCP_SERVER_STOP_POLL_MS 50u
static BaseType_t tcp_server_stop_requested(void)
{
return (app_network_task_stop_requested() != pdFALSE) ? pdTRUE : pdFALSE;
}
static BaseType_t tcp_server_delay_with_stop(uint32_t delay_ms)
{
uint32_t remaining_ms = delay_ms;
while (remaining_ms > 0u) {
uint32_t slice_ms = (remaining_ms > TCP_SERVER_STOP_POLL_MS) ? TCP_SERVER_STOP_POLL_MS : remaining_ms;
if (tcp_server_stop_requested() != pdFALSE) {
return pdFALSE;
}
vTaskDelay(pdMS_TO_TICKS(slice_ms));
remaining_ms -= slice_ms;
}
return (tcp_server_stop_requested() == pdFALSE) ? pdTRUE : pdFALSE;
}
static err_t tcp_server_worker(struct netconn *conn, uint8_t link_index)
{
struct netbuf *buf;
const device_config_t *cfg = config_get();
uint8_t uart_endpoint = config_uart_index_to_endpoint(cfg->links[link_index].uart);
uint8_t src_endpoint = config_link_index_to_endpoint(link_index);
err_t err;
route_msg_t *tx_msg;
route_send_result_t route_result;
netconn_set_recvtimeout(conn, 10);
for (;;) {
if (tcp_server_stop_requested() != pdFALSE) {
return ERR_CLSD;
}
err = netconn_recv(conn, &buf);
if (err == ERR_OK) {
do {
void *data;
uint16_t len;
netbuf_data(buf, &data, &len);
route_result = route_send(xTcpRxQueue,
src_endpoint,
uart_endpoint,
(link_index == CONFIG_LINK_S1) ? ROUTE_CONN_S1 : ROUTE_CONN_S2,
(const uint8_t *)data,
len,
pdMS_TO_TICKS(10));
if (route_result != ROUTE_SEND_OK) {
debug_log_printf("[SRV] idx=%u rx-route-fail rc=%s len=%u\r\n",
(unsigned int)link_index,
route_send_result_to_str(route_result),
(unsigned int)len);
netbuf_delete(buf);
return ERR_CLSD;
}
} while (netbuf_next(buf) >= 0);
netbuf_delete(buf);
} else if (err == ERR_TIMEOUT) {
if (tcp_server_stop_requested() != pdFALSE) {
return ERR_CLSD;
}
} else {
break;
}
while (xQueueReceive(xLinkTxQueues[link_index], &tx_msg, 0) == pdPASS) {
if (tcp_server_stop_requested() != pdFALSE) {
route_msg_free(tx_msg);
return ERR_CLSD;
}
err = netconn_write(conn, tx_msg->data, tx_msg->len, NETCONN_COPY);
route_msg_free(tx_msg);
if (err != ERR_OK) {
return err;
}
}
}
return err;
}
static void tcp_server_task(uint8_t link_index)
{
const device_config_t *cfg;
struct netconn *listener;
struct netconn *newconn;
netconn_thread_init();
for (;;) {
if (tcp_server_stop_requested() != pdFALSE) {
break;
}
while (g_netif_ready == pdFALSE) {
if (tcp_server_stop_requested() != pdFALSE) {
goto exit_task;
}
vTaskDelay(pdMS_TO_TICKS(100));
}
cfg = config_get();
if (cfg->links[link_index].enabled == 0u) {
if (tcp_server_stop_requested() != pdFALSE) {
break;
}
if (tcp_server_delay_with_stop(500u) == pdFALSE) {
break;
}
continue;
}
listener = netconn_new(NETCONN_TCP);
if (listener == NULL) {
if (tcp_server_delay_with_stop(500u) == pdFALSE) {
break;
}
continue;
}
netconn_set_recvtimeout(listener, TCP_SERVER_ACCEPT_TIMEOUT_MS);
if (netconn_bind(listener, IP_ADDR_ANY, cfg->links[link_index].local_port) != ERR_OK ||
netconn_listen(listener) != ERR_OK) {
netconn_delete(listener);
if (tcp_server_delay_with_stop(500u) == pdFALSE) {
break;
}
continue;
}
for (;;) {
if (tcp_server_stop_requested() != pdFALSE || cfg->links[link_index].enabled == 0u) {
break;
}
if (netconn_accept(listener, &newconn) == ERR_OK) {
tcp_server_worker(newconn, link_index);
netconn_close(newconn);
netconn_delete(newconn);
}
}
netconn_close(listener);
netconn_delete(listener);
}
exit_task:
netconn_thread_cleanup();
app_on_network_task_exit(xTaskGetCurrentTaskHandle());
vTaskDelete(NULL);
}
void TcpSrvTask_S1(void *argument)
{
(void)argument;
tcp_server_task(CONFIG_LINK_S1);
}
void TcpSrvTask_S2(void *argument)
{
(void)argument;
tcp_server_task(CONFIG_LINK_S2);
}
+15
View File
@@ -0,0 +1,15 @@
#ifndef TCP_SERVER_H
#define TCP_SERVER_H
#ifdef __cplusplus
extern "C" {
#endif
void TcpSrvTask_S1(void *argument);
void TcpSrvTask_S2(void *argument);
#ifdef __cplusplus
}
#endif
#endif
+649
View File
@@ -0,0 +1,649 @@
#include "uart_trans.h"
#include <string.h>
#include "FreeRTOS.h"
#include "task.h"
#include "usart.h"
#include "app_runtime.h"
#include "config.h"
#include "debug_log.h"
#include "route_msg.h"
#define UART_MUX_SYNC 0x7Eu
#define UART_MUX_TAIL 0x7Fu
#define UART_NOTIFY_RX_U0 (1UL << 0)
#define UART_NOTIFY_RX_U1 (1UL << 1)
#define UART_NOTIFY_TX_U0 (1UL << 8)
#define UART_NOTIFY_TX_U1 (1UL << 9)
typedef struct {
UART_HandleTypeDef *huart;
uint8_t rx_dma_buffer[UART_RX_DMA_BUFFER_SIZE];
uint8_t tx_dma_buffer[UART_TX_DMA_BUFFER_SIZE];
uint8_t rx_ring[UART_RX_RING_BUFFER_SIZE];
uint8_t tx_ring[UART_TX_RING_BUFFER_SIZE];
volatile uint16_t rx_dma_read_index;
volatile uint16_t rx_head;
volatile uint16_t rx_tail;
volatile uint16_t tx_head;
volatile uint16_t tx_tail;
volatile uint16_t tx_dma_len;
volatile uint8_t tx_busy;
volatile uint8_t tx_kick_fail_logged;
} uart_channel_ctx_t;
static uart_channel_ctx_t g_channels[UART_CHANNEL_MAX];
const char *uart_trans_send_result_to_str(uart_trans_send_result_t result)
{
switch (result) {
case UART_TRANS_SEND_OK:
return "ok";
case UART_TRANS_SEND_INVALID_INPUT:
return "invalid";
case UART_TRANS_SEND_RING_FULL:
return "full";
case UART_TRANS_SEND_KICK_FAILED:
return "kick";
default:
return "unknown";
}
}
static uint16_t ring_used(uint16_t head, uint16_t tail, uint16_t size)
{
return (head >= tail) ? (head - tail) : (size - tail + head);
}
static uint16_t ring_free(uint16_t head, uint16_t tail, uint16_t size)
{
return (uint16_t)(size - ring_used(head, tail, size) - 1u);
}
static void process_rx_snapshot(uart_channel_t channel)
{
uart_channel_ctx_t *ctx = &g_channels[channel];
uint16_t dma_write_index = (uint16_t)(UART_RX_DMA_BUFFER_SIZE - __HAL_DMA_GET_COUNTER(ctx->huart->hdmarx));
if (dma_write_index >= UART_RX_DMA_BUFFER_SIZE) {
dma_write_index = 0u;
}
while (ctx->rx_dma_read_index != dma_write_index) {
uint16_t next_head = (uint16_t)((ctx->rx_head + 1u) % UART_RX_RING_BUFFER_SIZE);
if (next_head == ctx->rx_tail) {
break;
}
ctx->rx_ring[ctx->rx_head] = ctx->rx_dma_buffer[ctx->rx_dma_read_index];
ctx->rx_head = next_head;
ctx->rx_dma_read_index = (uint16_t)((ctx->rx_dma_read_index + 1u) % UART_RX_DMA_BUFFER_SIZE);
}
}
static uart_trans_send_result_t kick_tx(uart_channel_t channel)
{
uart_channel_ctx_t *ctx = &g_channels[channel];
uint16_t available;
uint16_t chunk;
uint16_t tail;
uint16_t i;
if (ctx->tx_busy != 0u) {
return UART_TRANS_SEND_OK;
}
available = ring_used(ctx->tx_head, ctx->tx_tail, UART_TX_RING_BUFFER_SIZE);
if (available == 0u) {
return UART_TRANS_SEND_OK;
}
chunk = available;
if (chunk > UART_TX_DMA_BUFFER_SIZE) {
chunk = UART_TX_DMA_BUFFER_SIZE;
}
tail = ctx->tx_tail;
for (i = 0; i < chunk; ++i) {
ctx->tx_dma_buffer[i] = ctx->tx_ring[tail];
tail = (uint16_t)((tail + 1u) % UART_TX_RING_BUFFER_SIZE);
}
if (HAL_UART_Transmit_DMA(ctx->huart, ctx->tx_dma_buffer, chunk) != HAL_OK) {
ctx->tx_dma_len = 0u;
if (ctx->tx_kick_fail_logged == 0u) {
debug_log_printf("[UART] kick-fail ch=%u len=%u\r\n",
(unsigned int)channel,
(unsigned int)chunk);
ctx->tx_kick_fail_logged = 1u;
}
return UART_TRANS_SEND_KICK_FAILED;
}
ctx->tx_tail = tail;
ctx->tx_dma_len = chunk;
ctx->tx_busy = 1u;
ctx->tx_kick_fail_logged = 0u;
return UART_TRANS_SEND_OK;
}
static uint16_t uart_ring_available(uart_channel_t channel)
{
return ring_used(g_channels[channel].rx_head, g_channels[channel].rx_tail, UART_RX_RING_BUFFER_SIZE);
}
static uint16_t uart_ring_read(uart_channel_t channel, uint8_t *data, uint16_t max_len)
{
uart_channel_ctx_t *ctx = &g_channels[channel];
uint16_t copied = 0u;
while (copied < max_len && ctx->rx_tail != ctx->rx_head) {
data[copied++] = ctx->rx_ring[ctx->rx_tail];
ctx->rx_tail = (uint16_t)((ctx->rx_tail + 1u) % UART_RX_RING_BUFFER_SIZE);
}
return copied;
}
static bool uart_ring_peek_byte(uart_channel_t channel, uint16_t offset, uint8_t *data)
{
uart_channel_ctx_t *ctx = &g_channels[channel];
uint16_t available = ring_used(ctx->rx_head, ctx->rx_tail, UART_RX_RING_BUFFER_SIZE);
if (data == NULL || offset >= available) {
return false;
}
*data = ctx->rx_ring[(ctx->rx_tail + offset) % UART_RX_RING_BUFFER_SIZE];
return true;
}
static void uart_ring_drop(uart_channel_t channel, uint16_t len)
{
uart_channel_ctx_t *ctx = &g_channels[channel];
ctx->rx_tail = (uint16_t)((ctx->rx_tail + len) % UART_RX_RING_BUFFER_SIZE);
}
static void uart_route_raw_channel(uart_channel_t channel)
{
const device_config_t *cfg = config_get();
uint8_t buffer[ROUTE_MSG_MAX_PAYLOAD];
uint16_t len;
uint8_t uart_endpoint = (channel == UART_CHANNEL_U1) ? ENDPOINT_UART3 : ENDPOINT_UART2;
uint32_t i;
route_send_result_t route_result;
len = uart_ring_read(channel, buffer, sizeof(buffer));
if (len == 0u) {
return;
}
for (i = 0; i < CONFIG_LINK_COUNT; ++i) {
if (cfg->links[i].enabled == 0u || cfg->links[i].uart != ((channel == UART_CHANNEL_U1) ? LINK_UART_U1 : LINK_UART_U0)) {
continue;
}
route_result = route_send(xLinkTxQueues[i],
uart_endpoint,
config_link_index_to_endpoint((uint8_t)i),
(channel == UART_CHANNEL_U1) ? ROUTE_CONN_UART3 : ROUTE_CONN_UART2,
buffer,
len,
pdMS_TO_TICKS(10));
if (route_result != ROUTE_SEND_OK) {
debug_log_printf("[UART] raw-route-fail idx=%u rc=%s len=%u\r\n",
(unsigned int)i,
route_send_result_to_str(route_result),
(unsigned int)len);
}
}
}
static uart_trans_send_result_t uart_send_tcp_msg_chunk(route_msg_t *msg,
uint16_t offset,
uint16_t *accepted_len)
{
uint8_t frame[ROUTE_MSG_MAX_PAYLOAD + 6u];
uint16_t frame_len = 0u;
uint16_t remaining;
uint16_t chunk_len;
uint8_t uart_mask;
uart_trans_send_result_t uart_result;
if (accepted_len == NULL || msg == NULL || offset >= msg->len) {
return UART_TRANS_SEND_INVALID_INPUT;
}
*accepted_len = 0u;
uart_mask = (uint8_t)(msg->dst_mask & (ENDPOINT_UART2 | ENDPOINT_UART3));
if ((msg->dst_mask != uart_mask) ||
(uart_mask != ENDPOINT_UART2 && uart_mask != ENDPOINT_UART3)) {
return UART_TRANS_SEND_INVALID_INPUT;
}
remaining = (uint16_t)(msg->len - offset);
if (uart_mask == ENDPOINT_UART2) {
if (config_get()->mux_mode == MUX_MODE_FRAME) {
chunk_len = remaining;
if (chunk_len > (uint16_t)(UART_TX_RING_BUFFER_SIZE - 1u - 6u)) {
chunk_len = (uint16_t)(UART_TX_RING_BUFFER_SIZE - 1u - 6u);
}
if (!uart_mux_encode_frame(msg->src_id, ENDPOINT_UART2, &msg->data[offset], chunk_len, frame, &frame_len, sizeof(frame))) {
return UART_TRANS_SEND_INVALID_INPUT;
}
uart_result = uart_trans_send_buffer(UART_CHANNEL_U0, frame, frame_len);
if (uart_result != UART_TRANS_SEND_OK) {
return uart_result;
}
*accepted_len = chunk_len;
return UART_TRANS_SEND_OK;
}
chunk_len = remaining;
if (chunk_len > (uint16_t)(UART_TX_RING_BUFFER_SIZE - 1u)) {
chunk_len = (uint16_t)(UART_TX_RING_BUFFER_SIZE - 1u);
}
uart_result = uart_trans_send_buffer(UART_CHANNEL_U0, &msg->data[offset], chunk_len);
if (uart_result != UART_TRANS_SEND_OK) {
return uart_result;
}
*accepted_len = chunk_len;
return UART_TRANS_SEND_OK;
}
if (config_get()->mux_mode == MUX_MODE_FRAME) {
chunk_len = remaining;
if (chunk_len > (uint16_t)(UART_TX_RING_BUFFER_SIZE - 1u - 6u)) {
chunk_len = (uint16_t)(UART_TX_RING_BUFFER_SIZE - 1u - 6u);
}
if (!uart_mux_encode_frame(msg->src_id, ENDPOINT_UART3, &msg->data[offset], chunk_len, frame, &frame_len, sizeof(frame))) {
return UART_TRANS_SEND_INVALID_INPUT;
}
uart_result = uart_trans_send_buffer(UART_CHANNEL_U1, frame, frame_len);
if (uart_result != UART_TRANS_SEND_OK) {
return uart_result;
}
*accepted_len = chunk_len;
return UART_TRANS_SEND_OK;
}
chunk_len = remaining;
if (chunk_len > (uint16_t)(UART_TX_RING_BUFFER_SIZE - 1u)) {
chunk_len = (uint16_t)(UART_TX_RING_BUFFER_SIZE - 1u);
}
uart_result = uart_trans_send_buffer(UART_CHANNEL_U1, &msg->data[offset], chunk_len);
if (uart_result != UART_TRANS_SEND_OK) {
return uart_result;
}
*accepted_len = chunk_len;
return UART_TRANS_SEND_OK;
}
static void uart_try_advance_pending_tcp_msg(route_msg_t **pending_tcp_msg,
uint16_t *pending_tcp_offset,
uart_trans_send_result_t *pending_tcp_result)
{
route_msg_t *msg;
uart_trans_send_result_t uart_result;
uint16_t accepted_len;
if (pending_tcp_msg == NULL || pending_tcp_offset == NULL || pending_tcp_result == NULL) {
return;
}
msg = *pending_tcp_msg;
if (msg == NULL) {
return;
}
for (;;) {
accepted_len = 0u;
uart_result = uart_send_tcp_msg_chunk(msg, *pending_tcp_offset, &accepted_len);
if (uart_result != UART_TRANS_SEND_OK) {
if (uart_result != *pending_tcp_result) {
debug_log_printf("[UART] tcp-pend src=0x%02X dst=0x%02X off=%u rc=%s\r\n",
(unsigned int)msg->src_id,
(unsigned int)msg->dst_mask,
(unsigned int)(*pending_tcp_offset),
uart_trans_send_result_to_str(uart_result));
*pending_tcp_result = uart_result;
}
break;
}
*pending_tcp_offset = (uint16_t)(*pending_tcp_offset + accepted_len);
*pending_tcp_result = UART_TRANS_SEND_OK;
if (*pending_tcp_offset >= msg->len) {
route_msg_free(msg);
*pending_tcp_msg = NULL;
*pending_tcp_offset = 0u;
break;
}
}
}
static void uart_route_mux_frame(uart_channel_t source_channel, const uart_mux_frame_t *frame)
{
const device_config_t *cfg = config_get();
uint32_t i;
uint8_t endpoint;
uint8_t source_conn = (source_channel == UART_CHANNEL_U1) ? ROUTE_CONN_UART3 : ROUTE_CONN_UART2;
uint8_t out_frame[ROUTE_MSG_MAX_PAYLOAD + 6u];
uint16_t out_frame_len = 0u;
route_send_result_t route_result;
uart_trans_send_result_t uart_result;
if (frame->dst_mask == 0u) {
route_result = route_send(xConfigQueue,
frame->src_id,
0u,
source_conn,
frame->payload,
frame->payload_len,
pdMS_TO_TICKS(10));
if (route_result != ROUTE_SEND_OK) {
debug_log_printf("[UART] mux-cfg-fail rc=%s len=%u\r\n",
route_send_result_to_str(route_result),
(unsigned int)frame->payload_len);
}
return;
}
for (i = 0; i < CONFIG_LINK_COUNT; ++i) {
if (cfg->links[i].enabled == 0u) {
continue;
}
endpoint = config_link_index_to_endpoint((uint8_t)i);
if ((frame->dst_mask & endpoint) != 0u) {
route_result = route_send(xLinkTxQueues[i], frame->src_id, endpoint, source_conn, frame->payload, frame->payload_len, pdMS_TO_TICKS(10));
if (route_result != ROUTE_SEND_OK) {
debug_log_printf("[UART] mux-route-fail idx=%u rc=%s len=%u\r\n",
(unsigned int)i,
route_send_result_to_str(route_result),
(unsigned int)frame->payload_len);
}
}
}
if ((frame->dst_mask & ENDPOINT_UART2) != 0u && source_channel != UART_CHANNEL_U0) {
if (uart_mux_encode_frame(frame->src_id, ENDPOINT_UART2, frame->payload, frame->payload_len, out_frame, &out_frame_len, sizeof(out_frame))) {
uart_result = uart_trans_send_buffer(UART_CHANNEL_U0, out_frame, out_frame_len);
if (uart_result != UART_TRANS_SEND_OK) {
debug_log_printf("[UART] mux-u0-tx-fail rc=%s len=%u\r\n",
uart_trans_send_result_to_str(uart_result),
(unsigned int)out_frame_len);
}
} else {
debug_log_printf("[UART] mux-u0-enc-fail len=%u\r\n", (unsigned int)frame->payload_len);
}
}
if ((frame->dst_mask & ENDPOINT_UART3) != 0u && source_channel != UART_CHANNEL_U1) {
if (uart_mux_encode_frame(frame->src_id, ENDPOINT_UART3, frame->payload, frame->payload_len, out_frame, &out_frame_len, sizeof(out_frame))) {
uart_result = uart_trans_send_buffer(UART_CHANNEL_U1, out_frame, out_frame_len);
if (uart_result != UART_TRANS_SEND_OK) {
debug_log_printf("[UART] mux-u1-tx-fail rc=%s len=%u\r\n",
uart_trans_send_result_to_str(uart_result),
(unsigned int)out_frame_len);
}
} else {
debug_log_printf("[UART] mux-u1-enc-fail len=%u\r\n", (unsigned int)frame->payload_len);
}
}
}
int uart_trans_init(void)
{
memset(g_channels, 0, sizeof(g_channels));
g_channels[UART_CHANNEL_U0].huart = &huart2;
g_channels[UART_CHANNEL_U1].huart = &huart3;
debug_log_printf("[UART] init u0=%p u1=%p\r\n", (void *)g_channels[UART_CHANNEL_U0].huart, (void *)g_channels[UART_CHANNEL_U1].huart);
return 0;
}
int uart_trans_start_all(void)
{
uint32_t i;
for (i = 0; i < UART_CHANNEL_MAX; ++i) {
if (g_channels[i].huart == NULL) {
debug_log_printf("[UART] start fail null handle ch=%lu\r\n", (unsigned long)i);
return -1;
}
g_channels[i].rx_dma_read_index = 0u;
g_channels[i].rx_head = 0u;
g_channels[i].rx_tail = 0u;
g_channels[i].tx_head = 0u;
g_channels[i].tx_tail = 0u;
g_channels[i].tx_dma_len = 0u;
g_channels[i].tx_busy = 0u;
__HAL_UART_ENABLE_IT(g_channels[i].huart, UART_IT_IDLE);
if (HAL_UART_Receive_DMA(g_channels[i].huart, g_channels[i].rx_dma_buffer, UART_RX_DMA_BUFFER_SIZE) != HAL_OK) {
debug_log_printf("[UART] dma start fail ch=%lu\r\n", (unsigned long)i);
return -1;
}
}
debug_log_write("[UART] rx dma started\r\n");
return 0;
}
uart_trans_send_result_t uart_trans_send_buffer(uart_channel_t channel, const uint8_t *data, uint16_t len)
{
uart_channel_ctx_t *ctx;
uart_trans_send_result_t uart_result;
uint16_t original_head;
uint16_t written = 0u;
if (channel >= UART_CHANNEL_MAX || data == NULL || len == 0u || len >= UART_TX_RING_BUFFER_SIZE) {
return UART_TRANS_SEND_INVALID_INPUT;
}
ctx = &g_channels[channel];
if (ctx->huart == NULL) {
return UART_TRANS_SEND_INVALID_INPUT;
}
taskENTER_CRITICAL();
original_head = ctx->tx_head;
if (ring_free(ctx->tx_head, ctx->tx_tail, UART_TX_RING_BUFFER_SIZE) < len) {
taskEXIT_CRITICAL();
return UART_TRANS_SEND_RING_FULL;
}
while (written < len) {
ctx->tx_ring[ctx->tx_head] = data[written++];
ctx->tx_head = (uint16_t)((ctx->tx_head + 1u) % UART_TX_RING_BUFFER_SIZE);
}
uart_result = kick_tx(channel);
if (uart_result != UART_TRANS_SEND_OK) {
ctx->tx_head = original_head;
taskEXIT_CRITICAL();
return uart_result;
}
taskEXIT_CRITICAL();
return UART_TRANS_SEND_OK;
}
void uart_trans_notify_rx_from_isr(uart_channel_t channel, BaseType_t *xHigherPriorityTaskWoken)
{
uint32_t notify = (channel == UART_CHANNEL_U1) ? UART_NOTIFY_RX_U1 : UART_NOTIFY_RX_U0;
if (xUartRxTaskHandle != NULL) {
xTaskNotifyFromISR(xUartRxTaskHandle, notify, eSetBits, xHigherPriorityTaskWoken);
}
}
void uart_trans_tx_cplt_handler(uart_channel_t channel)
{
uint32_t notify = (channel == UART_CHANNEL_U1) ? UART_NOTIFY_TX_U1 : UART_NOTIFY_TX_U0;
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if (xUartRxTaskHandle != NULL) {
xTaskNotifyFromISR(xUartRxTaskHandle, notify, eSetBits, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
bool uart_mux_try_extract_frame(uart_channel_t channel, uart_mux_frame_t *frame)
{
uint16_t available;
uint16_t payload_len;
uint8_t sync_byte;
uint16_t i;
if (frame == NULL) {
return false;
}
available = uart_ring_available(channel);
if (available == 0u) {
return false;
}
if (!uart_ring_peek_byte(channel, 0u, &sync_byte)) {
return false;
}
if (sync_byte != UART_MUX_SYNC) {
uart_ring_drop(channel, 1u);
return false;
}
if (available < 6u) {
return false;
}
if (!uart_ring_peek_byte(channel, 1u, &sync_byte)) {
return false;
}
payload_len = (uint16_t)((uint16_t)sync_byte << 8);
if (!uart_ring_peek_byte(channel, 2u, &sync_byte)) {
return false;
}
payload_len = (uint16_t)(payload_len | sync_byte);
if (payload_len > sizeof(frame->payload)) {
uart_ring_drop(channel, 1u);
return false;
}
if (available < (uint16_t)(payload_len + 6u)) {
return false;
}
if (!uart_ring_peek_byte(channel, 5u + payload_len, &sync_byte)) {
return false;
}
if (sync_byte != UART_MUX_TAIL) {
uart_ring_drop(channel, 1u);
return false;
}
if (!uart_ring_peek_byte(channel, 3u, &frame->src_id) ||
!uart_ring_peek_byte(channel, 4u, &frame->dst_mask)) {
return false;
}
frame->payload_len = payload_len;
for (i = 0u; i < payload_len; ++i) {
if (!uart_ring_peek_byte(channel, (uint16_t)(5u + i), &frame->payload[i])) {
return false;
}
}
uart_ring_drop(channel, (uint16_t)(payload_len + 6u));
return true;
}
bool uart_mux_encode_frame(uint8_t src_id,
uint8_t dst_mask,
const uint8_t *payload,
uint16_t payload_len,
uint8_t *out,
uint16_t *out_len,
uint16_t out_capacity)
{
uint16_t frame_len = (uint16_t)(payload_len + 6u);
if (out == NULL || out_len == NULL || frame_len > out_capacity) {
return false;
}
out[0] = UART_MUX_SYNC;
out[1] = (uint8_t)(payload_len >> 8);
out[2] = (uint8_t)(payload_len & 0xFFu);
out[3] = src_id;
out[4] = dst_mask;
if (payload_len > 0u && payload != NULL) {
memcpy(&out[5], payload, payload_len);
}
out[5 + payload_len] = UART_MUX_TAIL;
*out_len = frame_len;
return true;
}
void UartRxTask(void *argument)
{
uint32_t notify_value;
BaseType_t notified;
route_msg_t *msg;
route_msg_t *pending_tcp_msg = NULL;
uint16_t pending_tcp_offset = 0u;
uart_mux_frame_t frame;
const device_config_t *cfg;
uart_trans_send_result_t pending_tcp_result = UART_TRANS_SEND_OK;
(void)argument;
if (uart_trans_start_all() != 0) {
Debug_TrapWithRttHint("uart-start-fail");
vTaskSuspend(NULL);
}
debug_log_boot("uart-task-started");
for (;;) {
notify_value = 0u;
notified = xTaskNotifyWait(0u, 0xFFFFFFFFu, &notify_value, pdMS_TO_TICKS(10));
if ((notified == pdTRUE) && ((notify_value & UART_NOTIFY_RX_U0) != 0u)) {
process_rx_snapshot(UART_CHANNEL_U0);
}
if ((notified == pdTRUE) && ((notify_value & UART_NOTIFY_RX_U1) != 0u)) {
process_rx_snapshot(UART_CHANNEL_U1);
}
if ((notified == pdTRUE) && ((notify_value & UART_NOTIFY_TX_U0) != 0u)) {
g_channels[UART_CHANNEL_U0].tx_busy = 0u;
}
if ((notified == pdTRUE) && ((notify_value & UART_NOTIFY_TX_U1) != 0u)) {
g_channels[UART_CHANNEL_U1].tx_busy = 0u;
}
uart_try_advance_pending_tcp_msg(&pending_tcp_msg, &pending_tcp_offset, &pending_tcp_result);
while (pending_tcp_msg == NULL && xQueueReceive(xTcpRxQueue, &msg, 0) == pdPASS) {
pending_tcp_msg = msg;
pending_tcp_offset = 0u;
pending_tcp_result = UART_TRANS_SEND_OK;
uart_try_advance_pending_tcp_msg(&pending_tcp_msg, &pending_tcp_offset, &pending_tcp_result);
}
cfg = config_get();
if (cfg->mux_mode == MUX_MODE_FRAME) {
while (uart_mux_try_extract_frame(UART_CHANNEL_U0, &frame)) {
uart_route_mux_frame(UART_CHANNEL_U0, &frame);
}
while (uart_mux_try_extract_frame(UART_CHANNEL_U1, &frame)) {
uart_route_mux_frame(UART_CHANNEL_U1, &frame);
}
} else {
uart_route_raw_channel(UART_CHANNEL_U0);
uart_route_raw_channel(UART_CHANNEL_U1);
}
kick_tx(UART_CHANNEL_U0);
kick_tx(UART_CHANNEL_U1);
}
}
+58
View File
@@ -0,0 +1,58 @@
#ifndef UART_TRANS_H
#define UART_TRANS_H
#include <stdbool.h>
#include <stdint.h>
#include "FreeRTOS.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
UART_CHANNEL_U0 = 0,
UART_CHANNEL_U1 = 1,
UART_CHANNEL_MAX
} uart_channel_t;
typedef enum {
UART_TRANS_SEND_OK = 0,
UART_TRANS_SEND_INVALID_INPUT,
UART_TRANS_SEND_RING_FULL,
UART_TRANS_SEND_KICK_FAILED
} uart_trans_send_result_t;
typedef struct {
uint8_t src_id;
uint8_t dst_mask;
uint16_t payload_len;
uint8_t payload[256];
} uart_mux_frame_t;
#define UART_RX_DMA_BUFFER_SIZE 128u
#define UART_TX_DMA_BUFFER_SIZE 128u
#define UART_RX_RING_BUFFER_SIZE 256u
#define UART_TX_RING_BUFFER_SIZE 256u
int uart_trans_init(void);
int uart_trans_start_all(void);
const char *uart_trans_send_result_to_str(uart_trans_send_result_t result);
uart_trans_send_result_t uart_trans_send_buffer(uart_channel_t channel, const uint8_t *data, uint16_t len);
void uart_trans_notify_rx_from_isr(uart_channel_t channel, BaseType_t *xHigherPriorityTaskWoken);
void uart_trans_tx_cplt_handler(uart_channel_t channel);
void UartRxTask(void *argument);
bool uart_mux_try_extract_frame(uart_channel_t channel, uart_mux_frame_t *frame);
bool uart_mux_encode_frame(uint8_t src_id,
uint8_t dst_mask,
const uint8_t *payload,
uint16_t payload_len,
uint8_t *out,
uint16_t *out_len,
uint16_t out_capacity);
#ifdef __cplusplus
}
#endif
#endif
+245
View File
@@ -0,0 +1,245 @@
# CH390 / lwIP 固定次数 ping 失败问题修复复盘
## 1. 问题现象
在 TCP2UART 固件运行后,设备初期可以正常 ARP 和 ping,但连续 ping 一段时间后不再响应。
典型现象:
- 设备 IP`192.168.31.100`
- 设备 MAC`02:00:00:00:00:01`
- 对端/网关 IP`192.168.31.1`
- 对端/网关 MAC`00:e0:4c:28:1e:60`
- 失败后设备仍持续发送 TCP SYN/RST 或 client timeout 相关流量,说明 TX、任务调度和应用层并未整体死机。
- 失败后对端继续向设备 MAC 发送 ICMP/ARP,但设备不再回复。
关键抓包:
- `WiresharkLog/04290150.pcapng`
- `seq=1884..1891` 共 8 次 ping reply 正常。
- 第 9 次 `seq=1892` 开始无 reply。
- `WiresharkLog/04290206.pcapng`
- 曾把 `PBUF_POOL_SIZE` / `MEMP_NUM_TCPIP_MSG_INPKT` 从 8 临时扩大到 16。
- 成功 ping 从 8 次变为 `seq=1900..1915` 共 16 次。
- 第 17 次 `seq=1916` 开始无 reply。
这个“成功次数随池大小移动”的现象证明:问题不是 CH390 随机丢包,也不是 PHY/TX 死掉,而是每次成功处理 ping 后都有某个 pbuf 引用没有释放,最终耗尽 `PBUF_POOL`
## 2. 排查过程中的重要结论
### 2.1 CH390 RX 读包路径曾存在风险,但不是最终根因
早期排查时发现 CH390 RX 路径与参考驱动存在若干不一致,已修正:
- `ch390_receive_packet()` 按参考序列读取 RX ready:先读 `MRCMDX` dummy,再读 `MRCMDX1`
- 校验 RX header 的 `Head` 字节必须为 `CH390_PKT_RDY`
- CH390 RX SRAM 中的 `rx_len` 包含 Ethernet FCS,交给 lwIP 前需要减去 4 字节。
- `ch390_rx_reset()` 显式写 `MPTRCR_RST_RX` 复位 RX memory pointer。
这些修正确保 CH390 RX FIFO 读包更接近参考实现,但无法解释“固定 8 次/16 次后失败”。
### 2.2 扩大 lwIP 池只能延迟问题
曾临时将如下配置从 8 提到 16
```c
#define PBUF_POOL_SIZE 16
#define MEMP_NUM_PBUF 16
#define MEMP_NUM_TCPIP_MSG_INPKT 16
```
结果成功 ping 次数也从 8 变成 16。这说明扩大池子只是延迟耗尽,不能作为根修复。
最终已恢复为 8
```c
#define PBUF_POOL_SIZE 8
#define MEMP_NUM_PBUF 8
#define MEMP_NUM_TCPIP_MSG_INPKT 8
```
### 2.3 `tcpip_input()` 异步队列不是最终根因
项目启用了 lwIP core locking。为避免每个 RX 包占用 `MEMP_TCPIP_MSG_INPKT`,配置已改为同步输入:
```c
#define LWIP_TCPIP_CORE_LOCKING 1
#define LWIP_TCPIP_CORE_LOCKING_INPUT 1
```
这样 `tcpip_input()` 会在 core lock 下同步调用 `ethernet_input()`,不再通过 `TCPIP_MSG_INPKT` 邮箱异步排队。
但用户后续验证仍然固定 8 次后停止,且每次成功 ping 都已经有 reply,因此说明 RX 包确实已经进入 ICMP 处理路径,问题更可能是 reply 输出路径增加了 pbuf 引用但未释放。
## 3. 最终根因
最终根因位于:
```text
Drivers/LwIP/src/netif/ethernet.c
```
`ethernet_output()` 实现:
```c
q = pbuf_alloc(PBUF_RAW_TX, SIZEOF_ETH_HDR, PBUF_RAM);
if (q == NULL) {
LINK_STATS_INC(link.memerr);
LINK_STATS_INC(link.drop);
return ERR_MEM;
}
pbuf_chain(q, p);
ethhdr = (struct eth_hdr *)q->payload;
SMEMCPY(&ethhdr->dest, dst, sizeof(struct eth_addr));
SMEMCPY(&ethhdr->src, src, sizeof(struct eth_addr));
ethhdr->type = lwip_htons(eth_type);
return netif->linkoutput(netif, q);
```
问题在 `pbuf_chain(q, p)`
lwIP 的 `pbuf_chain()` 会执行:
```c
pbuf_ref(t);
```
也就是给被挂接的原始 pbuf `p` 引用计数加 1。
ICMP echo reply 路径会复用 RX pbuf
```text
ethernetif_poll()
-> tcpip_input()
-> ethernet_input()
-> ip4_input()
-> icmp_input()
-> ip4_output_if()
-> etharp_output()
-> ethernet_output()
```
`icmp_input()` 末尾本身会 `pbuf_free(p)`,这部分是正确的。但在原实现中,`ethernet_output()` 通过 `pbuf_chain(q, p)``p` 额外加了一次引用,却没有在 `linkoutput()` 返回后释放临时 header pbuf `q`
因此每次 ping 的引用计数变化是:
```text
RX pbuf 初始 ref = 1
pbuf_chain(q, p) 后 ref = 2
icmp_input() 末尾 pbuf_free(p) 后 ref = 1
=> p 永远没有回到 0PBUF_POOL 泄漏 1 个
```
所以:
- `PBUF_POOL_SIZE=8` 时,8 次 ping reply 后耗尽。
- 临时扩大到 16 时,16 次 ping reply 后耗尽。
## 4. 修复方案
修复 `ethernet_output()`,在同步 `linkoutput()` 完成后释放临时 header pbuf 链:
```c
err_t ethernet_output(struct netif *netif,
struct pbuf *p,
const struct eth_addr *src,
const struct eth_addr *dst,
u16_t eth_type)
{
struct pbuf *q;
struct eth_hdr *ethhdr;
err_t err;
LWIP_ASSERT("netif != NULL", netif != NULL);
LWIP_ASSERT("p != NULL", p != NULL);
LWIP_ASSERT("src != NULL", src != NULL);
LWIP_ASSERT("dst != NULL", dst != NULL);
q = pbuf_alloc(PBUF_RAW_TX, SIZEOF_ETH_HDR, PBUF_RAM);
if (q == NULL) {
LINK_STATS_INC(link.memerr);
LINK_STATS_INC(link.drop);
return ERR_MEM;
}
pbuf_chain(q, p);
ethhdr = (struct eth_hdr *)q->payload;
SMEMCPY(&ethhdr->dest, dst, sizeof(struct eth_addr));
SMEMCPY(&ethhdr->src, src, sizeof(struct eth_addr));
ethhdr->type = lwip_htons(eth_type);
err = netif->linkoutput(netif, q);
pbuf_free(q);
return err;
}
```
为什么这里可以释放 `q`
- 本项目 `low_level_output()` 是同步发送。
- 它会立即遍历 pbuf 链,把数据复制到 `s_tx_buffer`
- 随后调用 `ch390_runtime_send_packet()` 把连续 buffer 发给 CH390。
- `low_level_output()` 返回后不再持有 pbuf 指针。
因此 `ethernet_output()``linkoutput()` 返回后释放 `q` 是正确的。
`pbuf_free(q)` 会同时:
- 释放临时 Ethernet header pbuf `q`
- 解除 `pbuf_chain()` 对原始 RX pbuf `p` 增加的引用;
- 之后 `icmp_input()` 末尾的 `pbuf_free(p)` 可以真正把 RX pbuf 归还 `PBUF_POOL`
## 5. 不要做的错误修复
### 5.1 不要在 `netif->input()` 成功后手动释放 pbuf
驱动层当前逻辑是正确的:
```c
input_err = ch390_netif.input(p, &ch390_netif);
if (input_err != ERR_OK) {
pbuf_free(p);
}
```
`netif->input()` 返回 `ERR_OK` 时,pbuf ownership 已经交给 lwIP。此时驱动不能再 `pbuf_free(p)`,否则会造成 double-free 或 use-after-free。
### 5.2 不要只扩大 `PBUF_POOL_SIZE`
扩大池子只会让失败次数从 8 变 16、32……不会修复泄漏。
### 5.3 不要继续优先怀疑 CH390 PHY/TX
抓包中失败后设备仍持续发送 TCP SYN/RST,说明 TX 和任务仍活着。固定次数失败更符合 pbuf 引用泄漏。
## 6. 验证结果
修复后 Keil 构建通过:
```text
"TCP2UART\TCP2UART.axf" - 0 Error(s), 0 Warning(s).
Program Size: Code=93376 RO-data=2768 RW-data=456 ZI-data=56032
```
用户烧录验证后确认问题已修复。
## 7. 后续排查建议
如后续再次出现固定次数网络停止,优先检查:
1. 是否存在 `pbuf_chain()` / `pbuf_ref()` 后没有配对 `pbuf_free()` 的路径。
2. 是否有 ARP pending queue 长时间持有 pbuf。
3. 是否有 TCP `recvmbox` / 应用桥接队列背压长期持有 pbuf。
4. 是否有人在 `netif->input()` 成功后错误释放 pbuf,导致内存破坏。
推荐排查点:
- `Drivers/LwIP/src/netif/ethernet.c`
- `Drivers/LwIP/src/core/ipv4/icmp.c`
- `Drivers/LwIP/src/core/ipv4/etharp.c`
- `Drivers/LwIP/src/core/pbuf.c`
- `Drivers/LwIP/src/netif/ethernetif.c`
+66
View File
@@ -0,0 +1,66 @@
# TCP2UART 当前交接 Prompt
## 1. 用途
本文件不再承担“项目从零编码任务说明”的职责,而是作为**当前工程的交接入口 Prompt**使用。
长期有效的工程知识、调试经验和现状说明,已经分别固化到其它文档,不再全部堆在本文件中。
---
## 2. 接手后先读什么
请按以下顺序阅读:
1. `交接清单.md` —— 当前状态、接下来要做什么、怎么做
2. `工程调试指南.md` —— 已固化的调试经验与当前工程真实边界
3. `项目技术实现.md` —— 架构、任务模型、协议模型
4. `项目需求说明.md`
5. `AT固件使用手册.md`
---
## 3. 当前工程一句话状态
当前项目已从早期 bring-up 阶段推进到 full-task 运行期调试阶段;`DIAG_TASK_ISOLATION=1` 稳定,`DIAG_TASK_ISOLATION=0` 仍会卡死,但故障边界已被多轮 discriminator 推进到 enabled 的 `netconn_*` 路径。当前在 `STM32F103RCT6` 上的 RAM/heap 余量过低,已被认定为调试噪声的主要来源之一,因此推荐下一阶段先切到 pin2pin 的 `STM32F103RDT6` 再继续分析。
---
## 4. 下一位 agent 的当前目标
请不要把当前任务理解成“立刻继续加逻辑修补”。当前更重要的是:
1. 完成 `STM32F103RCT6 -> STM32F103RDT6` 目标切换
2. 使用真实 Keil 日志重新确认构建成功
3. 在新器件上复测当前代码基线
4. 比较:
- 故障是否消失
- 是否明显后移
- 是否仍停在相同 enabled path
5. 只有拿到新器件上的第一轮 RTT / heap / HWM 证据后,再决定下一步最小化改动
---
## 5. 工作约束
1. 构建真值以 `MDK-ARM/build_capture.txt``TCP2UART.build_log.htm``.map` 为准
2. 不要再把 viewer 当作当前构建真值
3. 不要忽视 `DIAG_TASK_ISOLATION=1 正常、=0 异常` 这个前提
4. 在新器件的第一轮复测前,避免继续做大范围代码改动
5. 每次只做一个能明显改变故障边界的最小改动,并保留 RTT 证据
---
## 6. 可直接复制给下一位 agent 的起始 Prompt
```text
请先阅读:`交接清单.md`、`工程调试指南.md`、`项目技术实现.md`。
当前项目是 STM32F103 + FreeRTOS + lwIP + CH390 的 TCP↔UART 透传工程。此前在 `STM32F103RCT6` 上调试时,`DIAG_TASK_ISOLATION=1` 稳定,`DIAG_TASK_ISOLATION=0` 仍会卡死,但故障边界已被多轮 discriminator 推进到 enabled 的 `netconn_*` 路径。当前最关键的资源事实是:在 `RCT6` 上 full-task 创建完四个 TCP 任务后,FreeRTOS heap 只剩约 944 bytes,静态 RAM 也已逼近物理上限,因此当前推荐先切换到 pin2pin 的 `STM32F103RDT6`,保持现有代码基线基本不变,先完成第一轮换片复测,再根据新器件上的 RTT、free/min heap 和 enabled `S1/C1` 行为决定下一步。
你的当前目标不是立刻修完所有问题,而是:
1. 完成 `RCT6 -> RDT6` 目标切换;
2. 用真实 Keil 日志确认构建通过;
3. 在新器件上复测当前代码,判断故障是否消失、后移或保持原状;
4. 仅在拿到新器件上的第一轮 RTT 后,再继续做最小化的下一步判别。
```
+36 -4
View File
@@ -44,6 +44,7 @@
/* USER CODE BEGIN Includes */
/* Section where include file can be added */
#include "debug_log.h"
/* USER CODE END Includes */
/* Ensure definitions are only used by the compiler, and not by the assembler. */
@@ -59,13 +60,13 @@
#define configUSE_PREEMPTION 1
#define configSUPPORT_STATIC_ALLOCATION 1
#define configSUPPORT_DYNAMIC_ALLOCATION 1
#define configUSE_IDLE_HOOK 0
#define configUSE_IDLE_HOOK 1
#define configUSE_TICK_HOOK 0
#define configCPU_CLOCK_HZ ( SystemCoreClock )
#define configTICK_RATE_HZ ((TickType_t)1000)
#define configMAX_PRIORITIES ( 56 )
#define configMAX_PRIORITIES ( 7 )
#define configMINIMAL_STACK_SIZE ((uint16_t)128)
#define configTOTAL_HEAP_SIZE ((size_t)3072)
#define configTOTAL_HEAP_SIZE ((size_t)21760)
#define configMAX_TASK_NAME_LEN ( 16 )
#define configUSE_TRACE_FACILITY 1
#define configUSE_16_BIT_TICKS 0
@@ -74,6 +75,8 @@
#define configUSE_RECURSIVE_MUTEXES 1
#define configUSE_COUNTING_SEMAPHORES 1
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
#define configUSE_STREAM_BUFFERS 1 /* Enable StreamBuffer for UART data transfer */
#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 1
/* Co-routine definitions. */
#define configUSE_CO_ROUTINES 0
@@ -84,6 +87,8 @@
#define configTIMER_TASK_PRIORITY ( 2 )
#define configTIMER_QUEUE_LENGTH 10
#define configTIMER_TASK_STACK_DEPTH 256
#define configCHECK_FOR_STACK_OVERFLOW 2
#define configUSE_MALLOC_FAILED_HOOK 1
/* Set the following definitions to 1 to include the API function, or zero
to exclude the API function. */
@@ -135,7 +140,7 @@ See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
/* Normal assert() semantics without relying on the provision of an assert.h
header file. */
/* USER CODE BEGIN 1 */
#define configASSERT( x ) if ((x) == 0) {taskDISABLE_INTERRUPTS(); for( ;; );}
#define configASSERT( x ) do { if ((x) == 0) { debug_log_fault_context("config-assert", __FILE__, __LINE__); taskDISABLE_INTERRUPTS(); for( ;; ) { } } } while (0)
/* USER CODE END 1 */
/* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS
@@ -149,6 +154,33 @@ standard names. */
/* USER CODE BEGIN Defines */
/* Section where parameter definitions can be added (for instance, to override default ones in FreeRTOS.h) */
/* Application task priorities (higher number = higher priority) */
#define TASK_PRIORITY_TCPIP 6
#define TASK_PRIORITY_NET_POLL 5
#define TASK_PRIORITY_TCP_SERVER 4
#define TASK_PRIORITY_TCP_CLIENT 4
#define TASK_PRIORITY_UART_RX 4
#define TASK_PRIORITY_ROUTE 3
#define TASK_PRIORITY_CONFIG 2
#define TASK_PRIORITY_DEFAULT 1
/* Application task stack sizes (in words) */
#define TASK_STACK_TCPIP 512
#define TASK_STACK_NET_POLL 512
#define TASK_STACK_TCP_SERVER 512
#define TASK_STACK_TCP_CLIENT 512
#define TASK_STACK_UART_RX 384
#define TASK_STACK_ROUTE 512
#define TASK_STACK_CONFIG 384
#define TASK_STACK_DEFAULT 192
/* Route message pool for zero-copy inter-task communication */
#define ROUTE_MSG_POOL_SIZE 8
#define ROUTE_MSG_MAX_PAYLOAD 512
#define DIAG_TASK_ISOLATION 0
/* USER CODE END Defines */
#endif /* FREERTOS_CONFIG_H */
+26
View File
@@ -0,0 +1,26 @@
#ifndef DEBUG_LOG_H
#define DEBUG_LOG_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
extern volatile uint32_t g_rtt_log_seq;
extern volatile uint32_t g_rtt_log_drop_count;
extern volatile uint32_t g_rtt_log_last_drop_seq;
void debug_log_init(void);
void debug_log_write(const char *msg);
void debug_log_printf(const char *fmt, ...);
void debug_log_boot(const char *tag);
void debug_log_fault(const char *tag);
void debug_log_runtime_snapshot(void);
void debug_log_fault_context(const char *tag, const char *file, int line);
#ifdef __cplusplus
}
#endif
#endif
+1 -1
View File
@@ -38,7 +38,7 @@ extern IWDG_HandleTypeDef hiwdg;
/* USER CODE END Private defines */
void MX_IWDG_Init(void);
HAL_StatusTypeDef MX_IWDG_Init(void);
/* USER CODE BEGIN Prototypes */
+1
View File
@@ -51,6 +51,7 @@ extern "C" {
/* Exported functions prototypes ---------------------------------------------*/
void Error_Handler(void);
void Debug_TrapWithRttHint(const char *tag);
/* USER CODE BEGIN EFP */
+5 -48
View File
@@ -1,74 +1,31 @@
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file stm32f1xx_it.h
* @brief This file contains the headers of the interrupt handlers.
******************************************************************************
* @attention
*
* Copyright (c) 2026 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __STM32F1xx_IT_H
#define __STM32F1xx_IT_H
#ifndef __STM32F1XX_IT_H
#define __STM32F1XX_IT_H
#ifdef __cplusplus
extern "C" {
#endif
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Exported types ------------------------------------------------------------*/
/* USER CODE BEGIN ET */
/* USER CODE END ET */
/* Exported constants --------------------------------------------------------*/
/* USER CODE BEGIN EC */
/* USER CODE END EC */
/* Exported macro ------------------------------------------------------------*/
/* USER CODE BEGIN EM */
/* USER CODE END EM */
/* Exported functions prototypes ---------------------------------------------*/
void NMI_Handler(void);
void HardFault_Handler(void);
void MemManage_Handler(void);
void BusFault_Handler(void);
void UsageFault_Handler(void);
void DebugMon_Handler(void);
void SysTick_Handler(void);
void DMA1_Channel2_IRQHandler(void);
void DMA1_Channel3_IRQHandler(void);
void DMA1_Channel4_IRQHandler(void);
void DMA1_Channel5_IRQHandler(void);
void DMA1_Channel6_IRQHandler(void);
void DMA1_Channel7_IRQHandler(void);
void EXTI0_IRQHandler(void);
void SPI1_IRQHandler(void);
void USART1_IRQHandler(void);
void USART2_IRQHandler(void);
void USART3_IRQHandler(void);
/* USER CODE BEGIN EFP */
/* USER CODE END EFP */
void TIM4_IRQHandler(void);
#ifdef __cplusplus
}
#endif
#endif /* __STM32F1xx_IT_H */
#endif
+1
View File
@@ -47,6 +47,7 @@ void MX_USART2_UART_Init(void);
void MX_USART3_UART_Init(void);
/* USER CODE BEGIN Prototypes */
void USART_SetConfiguredBaudrates(uint32_t usart2_baudrate, uint32_t usart3_baudrate);
/* USER CODE END Prototypes */
+117
View File
@@ -0,0 +1,117 @@
#include "debug_log.h"
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include "FreeRTOS.h"
#include "SEGGER_RTT.h"
#include "task.h"
volatile uint32_t g_rtt_log_seq = 0u;
volatile uint32_t g_rtt_log_drop_count = 0u;
volatile uint32_t g_rtt_log_last_drop_seq = 0u;
static void debug_backend_write(const char *msg)
{
unsigned len;
unsigned written;
if ((msg == NULL) || (msg[0] == '\0')) {
return;
}
len = (unsigned)strlen(msg);
g_rtt_log_seq += 1u;
written = SEGGER_RTT_Write(0u, msg, len);
if (written < len) {
g_rtt_log_drop_count += 1u;
g_rtt_log_last_drop_seq = g_rtt_log_seq;
}
}
void debug_log_init(void)
{
SEGGER_RTT_Init();
}
void debug_log_write(const char *msg)
{
if (msg == NULL) {
return;
}
debug_backend_write(msg);
}
void debug_log_printf(const char *fmt, ...)
{
char buffer[256];
va_list args;
int len;
if (fmt == NULL) {
return;
}
va_start(args, fmt);
len = vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
if (len < 0) {
return;
}
buffer[sizeof(buffer) - 1u] = '\0';
debug_backend_write(buffer);
}
void debug_log_boot(const char *tag)
{
debug_log_printf("[BOOT] %s\r\n", (tag != NULL) ? tag : "(null)");
}
void debug_log_fault(const char *tag)
{
debug_log_printf("[FAULT] %s\r\n", (tag != NULL) ? tag : "(null)");
}
void debug_log_fault_context(const char *tag, const char *file, int line)
{
debug_log_printf("[FAULT] %s file=%s line=%d free=%lu min=%lu seq=%lu drop=%lu last_drop=%lu\r\n",
(tag != NULL) ? tag : "(null)",
(file != NULL) ? file : "(null)",
line,
(unsigned long)xPortGetFreeHeapSize(),
(unsigned long)xPortGetMinimumEverFreeHeapSize(),
(unsigned long)g_rtt_log_seq,
(unsigned long)g_rtt_log_drop_count,
(unsigned long)g_rtt_log_last_drop_seq);
}
void debug_log_runtime_snapshot(void)
{
UBaseType_t default_hwm;
size_t free_heap;
size_t min_heap;
free_heap = xPortGetFreeHeapSize();
min_heap = xPortGetMinimumEverFreeHeapSize();
default_hwm = uxTaskGetStackHighWaterMark(NULL);
debug_log_printf("[RTOS] free=%lu min=%lu self_hwm=%lu\r\n",
(unsigned long)free_heap,
(unsigned long)min_heap,
(unsigned long)default_hwm);
}
void lwip_platform_assert(const char *msg, const char *file, int line)
{
debug_log_printf("[FAULT] lwip-assert msg=%s file=%s line=%d\r\n",
(msg != NULL) ? msg : "(null)",
(file != NULL) ? file : "(null)",
line);
taskDISABLE_INTERRUPTS();
while (1) {
}
}
+221 -116
View File
@@ -1,129 +1,234 @@
/* USER CODE BEGIN Header */
/**
******************************************************************************
* File Name : freertos.c
* Description : Code for freertos applications
******************************************************************************
* @attention
*
* Copyright (c) 2026 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "main.h"
#include "cmsis_os.h"
#include "gpio.h"
#include "iwdg.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "config.h"
#include "debug_log.h"
#include "route_msg.h"
#include "app_runtime.h"
#include "task_net_poll.h"
#include "tcp_server.h"
#include "tcp_client.h"
#include "uart_trans.h"
/* USER CODE END Includes */
QueueHandle_t xTcpRxQueue = NULL;
QueueHandle_t xConfigQueue = NULL;
QueueHandle_t xLinkTxQueues[CONFIG_LINK_COUNT] = {0};
SemaphoreHandle_t xNetSemaphore = NULL;
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
TaskHandle_t xUartRxTaskHandle = NULL;
TaskHandle_t xConfigTaskHandle = NULL;
volatile BaseType_t g_netif_ready = pdFALSE;
volatile uint32_t g_netif_phase = 0u;
volatile int32_t g_netif_add_err = 0x7FFFFFFF;
volatile int32_t g_netif_set_default_err = 0x7FFFFFFF;
volatile int32_t g_netif_set_link_down_err = 0x7FFFFFFF;
volatile int32_t g_netif_set_up_err = 0x7FFFFFFF;
volatile int32_t g_netif_init_ok = 0;
/* USER CODE END PTD */
static TaskHandle_t xNetPollTaskHandle = NULL;
static TaskHandle_t xTcpSrvTaskS1Handle = NULL;
static TaskHandle_t xTcpSrvTaskS2Handle = NULL;
static TaskHandle_t xTcpCliTaskC1Handle = NULL;
static TaskHandle_t xTcpCliTaskC2Handle = NULL;
static TaskHandle_t xDefaultTaskHandle = NULL;
static BaseType_t xNetworkTasksStarted = pdFALSE;
static volatile BaseType_t xNetworkTaskStopRequested = pdFALSE;
static volatile BaseType_t xNetworkRestartRequested = pdFALSE;
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables */
/* USER CODE END Variables */
/* Definitions for defaultTask */
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
.name = "defaultTask",
.stack_size = 128 * 4,
.priority = (osPriority_t) osPriorityNormal,
};
/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */
/* USER CODE END FunctionPrototypes */
void StartDefaultTask(void *argument);
void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */
/**
* @brief FreeRTOS initialization
* @param None
* @retval None
*/
void MX_FREERTOS_Init(void) {
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* USER CODE BEGIN RTOS_MUTEX */
/* add mutexes, ... */
/* USER CODE END RTOS_MUTEX */
/* USER CODE BEGIN RTOS_SEMAPHORES */
/* add semaphores, ... */
/* USER CODE END RTOS_SEMAPHORES */
/* USER CODE BEGIN RTOS_TIMERS */
/* start timers, add new ones, ... */
/* USER CODE END RTOS_TIMERS */
/* USER CODE BEGIN RTOS_QUEUES */
/* add queues, ... */
/* USER CODE END RTOS_QUEUES */
/* Create the thread(s) */
/* creation of defaultTask */
defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
/* USER CODE BEGIN RTOS_THREADS */
/* add threads, ... */
/* USER CODE END RTOS_THREADS */
/* USER CODE BEGIN RTOS_EVENTS */
/* add events, ... */
/* USER CODE END RTOS_EVENTS */
}
/* USER CODE BEGIN Header_StartDefaultTask */
/**
* @brief Function implementing the defaultTask thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument)
void app_start_network_tasks(void)
{
/* USER CODE BEGIN StartDefaultTask */
/* Infinite loop */
for(;;)
{
osDelay(1);
}
/* USER CODE END StartDefaultTask */
#if !DIAG_TASK_ISOLATION
BaseType_t rc;
const device_config_t *cfg;
if (xNetworkTasksStarted != pdFALSE) {
debug_log_write("[NET] start-network-tasks already\r\n");
return;
}
if (xNetworkTaskStopRequested != pdFALSE) {
debug_log_write("[NET] start-network-tasks stop-pending\r\n");
return;
}
cfg = config_get();
debug_log_printf("[NET] start-network-tasks enter free=%lu min=%lu\r\n",
(unsigned long)xPortGetFreeHeapSize(),
(unsigned long)xPortGetMinimumEverFreeHeapSize());
if (cfg->links[CONFIG_LINK_S1].enabled != 0u) {
rc = xTaskCreate(TcpSrvTask_S1, "TcpSrvS1", TASK_STACK_TCP_SERVER, NULL, TASK_PRIORITY_TCP_SERVER, &xTcpSrvTaskS1Handle);
debug_log_printf("[NET] create TcpSrvS1 rc=%ld free=%lu min=%lu\r\n",
(long)rc,
(unsigned long)xPortGetFreeHeapSize(),
(unsigned long)xPortGetMinimumEverFreeHeapSize());
configASSERT(rc == pdPASS);
} else {
debug_log_write("[NET] skip TcpSrvS1 en=0\r\n");
}
if (cfg->links[CONFIG_LINK_S2].enabled != 0u) {
rc = xTaskCreate(TcpSrvTask_S2, "TcpSrvS2", TASK_STACK_TCP_SERVER, NULL, TASK_PRIORITY_TCP_SERVER, &xTcpSrvTaskS2Handle);
debug_log_printf("[NET] create TcpSrvS2 rc=%ld free=%lu min=%lu\r\n",
(long)rc,
(unsigned long)xPortGetFreeHeapSize(),
(unsigned long)xPortGetMinimumEverFreeHeapSize());
configASSERT(rc == pdPASS);
} else {
debug_log_write("[NET] skip TcpSrvS2 en=0\r\n");
}
if (cfg->links[CONFIG_LINK_C1].enabled != 0u) {
rc = xTaskCreate(TcpCliTask_C1, "TcpCliC1", TASK_STACK_TCP_CLIENT, NULL, TASK_PRIORITY_TCP_CLIENT, &xTcpCliTaskC1Handle);
debug_log_printf("[NET] create TcpCliC1 rc=%ld free=%lu min=%lu\r\n",
(long)rc,
(unsigned long)xPortGetFreeHeapSize(),
(unsigned long)xPortGetMinimumEverFreeHeapSize());
configASSERT(rc == pdPASS);
} else {
debug_log_write("[NET] skip TcpCliC1 en=0\r\n");
}
if (cfg->links[CONFIG_LINK_C2].enabled != 0u) {
rc = xTaskCreate(TcpCliTask_C2, "TcpCliC2", TASK_STACK_TCP_CLIENT, NULL, TASK_PRIORITY_TCP_CLIENT, &xTcpCliTaskC2Handle);
debug_log_printf("[NET] create TcpCliC2 rc=%ld free=%lu min=%lu\r\n",
(long)rc,
(unsigned long)xPortGetFreeHeapSize(),
(unsigned long)xPortGetMinimumEverFreeHeapSize());
configASSERT(rc == pdPASS);
} else {
debug_log_write("[NET] skip TcpCliC2 en=0\r\n");
}
xNetworkTasksStarted = pdTRUE;
debug_log_printf("[NET] start-network-tasks exit free=%lu min=%lu\r\n",
(unsigned long)xPortGetFreeHeapSize(),
(unsigned long)xPortGetMinimumEverFreeHeapSize());
#endif
}
/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */
void app_request_network_task_stop(void)
{
xNetworkTaskStopRequested = pdTRUE;
}
/* USER CODE END Application */
void app_clear_network_task_stop(void)
{
xNetworkTaskStopRequested = pdFALSE;
}
BaseType_t app_network_task_stop_requested(void)
{
return xNetworkTaskStopRequested;
}
BaseType_t app_network_tasks_are_stopped(void)
{
return (xTcpSrvTaskS1Handle == NULL &&
xTcpSrvTaskS2Handle == NULL &&
xTcpCliTaskC1Handle == NULL &&
xTcpCliTaskC2Handle == NULL) ? pdTRUE : pdFALSE;
}
void app_on_network_task_exit(TaskHandle_t task_handle)
{
taskENTER_CRITICAL();
if (task_handle == xTcpSrvTaskS1Handle) {
xTcpSrvTaskS1Handle = NULL;
} else if (task_handle == xTcpSrvTaskS2Handle) {
xTcpSrvTaskS2Handle = NULL;
} else if (task_handle == xTcpCliTaskC1Handle) {
xTcpCliTaskC1Handle = NULL;
} else if (task_handle == xTcpCliTaskC2Handle) {
xTcpCliTaskC2Handle = NULL;
}
if (xTcpSrvTaskS1Handle == NULL &&
xTcpSrvTaskS2Handle == NULL &&
xTcpCliTaskC1Handle == NULL &&
xTcpCliTaskC2Handle == NULL) {
xNetworkTasksStarted = pdFALSE;
}
taskEXIT_CRITICAL();
}
void app_request_network_restart(void)
{
xNetworkRestartRequested = pdTRUE;
}
void app_clear_network_restart_request(void)
{
xNetworkRestartRequested = pdFALSE;
}
BaseType_t app_network_restart_requested(void)
{
return xNetworkRestartRequested;
}
static void StartDefaultTask(void *argument)
{
BaseType_t iwdg_ready = pdFALSE;
(void)argument;
debug_log_boot("default-task");
if (MX_IWDG_Init() == HAL_OK) {
debug_log_write("[BOOT] iwdg-started\r\n");
iwdg_ready = pdTRUE;
} else {
debug_log_write("[BOOT] iwdg-init-fail\r\n");
}
for (;;) {
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
if (iwdg_ready == pdTRUE) {
HAL_IWDG_Refresh(&hiwdg);
}
vTaskDelay(pdMS_TO_TICKS(500));
}
}
void MX_FREERTOS_Init(void)
{
uint32_t i;
route_msg_init();
configASSERT(uart_trans_init() == 0);
debug_log_boot("uart-trans-init");
xNetSemaphore = xSemaphoreCreateBinary();
xTcpRxQueue = xQueueCreate(6, sizeof(route_msg_t *));
xConfigQueue = xQueueCreate(2, sizeof(route_msg_t *));
for (i = 0; i < CONFIG_LINK_COUNT; ++i) {
xLinkTxQueues[i] = xQueueCreate(4, sizeof(route_msg_t *));
}
configASSERT(xNetSemaphore != NULL);
configASSERT(xTcpRxQueue != NULL);
configASSERT(xConfigQueue != NULL);
for (i = 0; i < CONFIG_LINK_COUNT; ++i) {
configASSERT(xLinkTxQueues[i] != NULL);
}
configASSERT(xTaskCreate(StartDefaultTask, "defaultTask", TASK_STACK_DEFAULT, NULL, TASK_PRIORITY_DEFAULT, &xDefaultTaskHandle) == pdPASS);
configASSERT(xTaskCreate(NetPollTask, "NetPoll", TASK_STACK_NET_POLL, NULL, TASK_PRIORITY_NET_POLL, &xNetPollTaskHandle) == pdPASS);
#if !DIAG_TASK_ISOLATION
configASSERT(xTaskCreate(UartRxTask, "UartRx", TASK_STACK_UART_RX, NULL, TASK_PRIORITY_UART_RX, &xUartRxTaskHandle) == pdPASS);
configASSERT(xTaskCreate(ConfigTask, "Config", TASK_STACK_CONFIG, NULL, TASK_PRIORITY_CONFIG, &xConfigTaskHandle) == pdPASS);
#else
debug_log_write("[DIAG] task-isolation enabled\r\n");
#endif
debug_log_boot("tasks-created");
}
+11 -1
View File
@@ -65,7 +65,7 @@ void MX_GPIO_Init(void)
/*Configure GPIO pin : PB0 */
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
@@ -76,6 +76,16 @@ void MX_GPIO_Init(void)
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* EXTI interrupt init
* Keep CH390 INT masked during early boot. PB0 may already be asserted at
* power-on, while the FreeRTOS semaphore is not created until
* MX_FREERTOS_Init(). The network driver enables EXTI0 after CH390 and the
* RTOS objects are ready.
*/
HAL_NVIC_DisableIRQ(EXTI0_IRQn);
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0);
HAL_NVIC_SetPriority(EXTI0_IRQn, 6, 0);
}
/* USER CODE BEGIN 2 */
+4 -4
View File
@@ -27,7 +27,7 @@
IWDG_HandleTypeDef hiwdg;
/* IWDG init function */
void MX_IWDG_Init(void)
HAL_StatusTypeDef MX_IWDG_Init(void)
{
/* USER CODE BEGIN IWDG_Init 0 */
@@ -38,16 +38,16 @@ void MX_IWDG_Init(void)
/* USER CODE END IWDG_Init 1 */
hiwdg.Instance = IWDG;
hiwdg.Init.Prescaler = IWDG_PRESCALER_4;
hiwdg.Init.Prescaler = IWDG_PRESCALER_64;
hiwdg.Init.Reload = 4095;
if (HAL_IWDG_Init(&hiwdg) != HAL_OK)
{
Error_Handler();
return HAL_ERROR;
}
/* USER CODE BEGIN IWDG_Init 2 */
/* USER CODE END IWDG_Init 2 */
return HAL_OK;
}
/* USER CODE BEGIN 1 */
+103 -5
View File
@@ -27,7 +27,10 @@
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include "config.h"
#include "debug_log.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
@@ -37,7 +40,15 @@
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* CH390 硬件控制引脚 */
#define CH390_RST_PIN GPIO_PIN_1
#define CH390_RST_PORT GPIOB
#define CH390_CS_PIN GPIO_PIN_4
#define CH390_CS_PORT GPIOA
/* LED 指示灯 */
#define LED_PIN GPIO_PIN_13
#define LED_PORT GPIOC
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
@@ -55,12 +66,56 @@
void SystemClock_Config(void);
void MX_FREERTOS_Init(void);
/* USER CODE BEGIN PFP */
static void CH390_HardwareReset(void);
static void LED_Init(void);
static void ApplyConfiguredUartBaudrates(void);
void Debug_TrapWithRttHint(const char *tag);
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/**
* @brief CH390 硬件复位
* @note 复位时序: RST 低电平至少 10us,然后高电平等待 50ms 完成初始化
*/
static void CH390_HardwareReset(void)
{
/* 拉低 RST 引脚 */
HAL_GPIO_WritePin(CH390_RST_PORT, CH390_RST_PIN, GPIO_PIN_RESET);
HAL_Delay(1); /* 保持低电平 1ms (远超最小 10us 要求) */
/* 拉高 RST 引脚,等待芯片初始化完成 */
HAL_GPIO_WritePin(CH390_RST_PORT, CH390_RST_PIN, GPIO_PIN_SET);
HAL_Delay(50); /* 等待 50ms */
/* 确保 CS 为高电平(未选中状态) */
HAL_GPIO_WritePin(CH390_CS_PORT, CH390_CS_PIN, GPIO_PIN_SET);
}
/**
* @brief LED 初始化(点亮表示系统启动)
*/
static void LED_Init(void)
{
/* LED 灭(PC13 高电平灭,低电平亮) */
HAL_GPIO_WritePin(LED_PORT, LED_PIN, GPIO_PIN_SET);
}
/**
* @brief LED 闪烁(用于指示系统运行状态)
*/
void LED_Toggle(void)
{
HAL_GPIO_TogglePin(LED_PORT, LED_PIN);
}
static void ApplyConfiguredUartBaudrates(void)
{
USART_SetConfiguredBaudrates(config_get_uart_baudrate(LINK_UART_U0),
config_get_uart_baudrate(LINK_UART_U1));
}
/* USER CODE END 0 */
/**
@@ -80,33 +135,46 @@ int main(void)
HAL_Init();
/* USER CODE BEGIN Init */
debug_log_init();
debug_log_boot("hal-init");
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
debug_log_boot("clock-config");
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_IWDG_Init();
MX_USART1_UART_Init();
config_init();
ApplyConfiguredUartBaudrates();
MX_USART2_UART_Init();
MX_USART3_UART_Init();
MX_SPI1_Init();
/* USER CODE BEGIN 2 */
debug_log_boot("peripherals-ready");
/* LED 初始化 */
LED_Init();
/* CH390 硬件复位 */
CH390_HardwareReset();
debug_log_boot("config-ready");
/* USER CODE END 2 */
/* Init scheduler */
osKernelInitialize(); /* Call init function for freertos objects (in cmsis_os2.c) */
MX_FREERTOS_Init();
debug_log_boot("freertos-init");
/* Start scheduler */
debug_log_boot("scheduler-start");
osKernelStart();
/* We should never get here as control is now taken by the scheduler */
@@ -164,6 +232,35 @@ void SystemClock_Config(void)
/* USER CODE BEGIN 4 */
/**
* @brief 重定向 printf 到 UART1(调试输出)
*/
void Debug_TrapWithRttHint(const char *tag)
{
debug_log_fault_context(tag, __FILE__, __LINE__);
}
void vApplicationMallocFailedHook(void)
{
debug_log_fault_context("malloc-failed", __FILE__, __LINE__);
__disable_irq();
for (;;)
{
}
}
void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName)
{
debug_log_printf("[FAULT] stack-overflow task=%s\r\n",
(pcTaskName != NULL) ? pcTaskName : "(null)");
debug_log_fault_context("stack-overflow", __FILE__, __LINE__);
(void)xTask;
__disable_irq();
for (;;)
{
}
}
/* USER CODE END 4 */
/**
@@ -174,6 +271,7 @@ void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
debug_log_fault_context("error-handler", __FILE__, __LINE__);
__disable_irq();
while (1)
{
+118
View File
@@ -0,0 +1,118 @@
#include <rt_sys.h>
#include <stdio.h>
#include <string.h>
#pragma import(__use_no_semihosting)
const char __stdin_name[] = ":tt";
const char __stdout_name[] = ":tt";
const char __stderr_name[] = ":tt";
#define NULL_FH_STDIN 0x8001
#define NULL_FH_STDOUT 0x8002
#define NULL_FH_STDERR 0x8003
static int rtt_is_terminal_name(const char *name)
{
return (name != NULL) && (strcmp(name, ":tt") == 0);
}
FILEHANDLE _sys_open(const char *name, int openmode)
{
if (!rtt_is_terminal_name(name)) {
return -1;
}
if ((openmode & OPEN_W) == OPEN_W) {
return NULL_FH_STDOUT;
}
if ((openmode & OPEN_A) == OPEN_A) {
return NULL_FH_STDERR;
}
return NULL_FH_STDIN;
}
int _sys_close(FILEHANDLE fh)
{
(void)fh;
return 0;
}
int _sys_write(FILEHANDLE fh, const unsigned char *buf, unsigned len, int mode)
{
(void)mode;
if ((fh != NULL_FH_STDOUT) && (fh != NULL_FH_STDERR)) {
return -1;
}
if ((buf == NULL) || (len == 0u)) {
return 0;
}
(void)buf;
(void)len;
return 0;
}
int _sys_read(FILEHANDLE fh, unsigned char *buf, unsigned len, int mode)
{
(void)fh;
(void)buf;
(void)len;
(void)mode;
return -1;
}
int _sys_istty(FILEHANDLE fh)
{
return (fh == NULL_FH_STDIN) || (fh == NULL_FH_STDOUT) || (fh == NULL_FH_STDERR);
}
int _sys_seek(FILEHANDLE fh, long pos)
{
(void)fh;
(void)pos;
return -1;
}
int _sys_ensure(FILEHANDLE fh)
{
(void)fh;
return 0;
}
long _sys_flen(FILEHANDLE fh)
{
(void)fh;
return 0;
}
int _sys_tmpnam(char *name, int sig, unsigned maxlen)
{
(void)name;
(void)sig;
(void)maxlen;
return 0;
}
char *_sys_command_string(char *cmd, int len)
{
(void)cmd;
(void)len;
return NULL;
}
void _ttywrch(int ch)
{
(void)ch;
}
void _sys_exit(int returncode)
{
(void)returncode;
while (1) {
}
}
+12 -8
View File
@@ -41,10 +41,10 @@ void MX_SPI1_Init(void)
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_HARD_OUTPUT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH; /* CH390 requires CPOL=High (Mode 3) */
hspi1.Init.CLKPhase = SPI_PHASE_2EDGE; /* CH390 requires CPHA=2Edge (Mode 3) */
hspi1.Init.NSS = SPI_NSS_SOFT; /* Software CS control for CH390 */
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8; /* 72MHz/8 = 9MHz (CH390 max 10MHz) */
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
@@ -73,20 +73,24 @@ void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
__HAL_RCC_GPIOA_CLK_ENABLE();
/**SPI1 GPIO Configuration
PA4 ------> SPI1_NSS
PA4 ------> CH390 CS (GPIO Output, controlled by CH390_Interface.c)
PA5 ------> SPI1_SCK
PA6 ------> SPI1_MISO
PA7 ------> SPI1_MOSI
*/
GPIO_InitStruct.Pin = GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_7;
/* SCK and MOSI as AF Push-Pull */
GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* MISO as Input */
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* PA4 (CS) is configured as GPIO output in CH390_Interface.c */
/* SPI1 interrupt Init */
HAL_NVIC_SetPriority(SPI1_IRQn, 5, 0);
@@ -109,12 +113,12 @@ void HAL_SPI_MspDeInit(SPI_HandleTypeDef* spiHandle)
__HAL_RCC_SPI1_CLK_DISABLE();
/**SPI1 GPIO Configuration
PA4 ------> SPI1_NSS
PA4 ------> CH390 CS (handled by CH390_Interface.c)
PA5 ------> SPI1_SCK
PA6 ------> SPI1_MISO
PA7 ------> SPI1_MOSI
*/
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7);
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7);
/* SPI1 interrupt Deinit */
HAL_NVIC_DisableIRQ(SPI1_IRQn);
+47
View File
@@ -0,0 +1,47 @@
#include "stm32f1xx_hal.h"
HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
RCC_ClkInitTypeDef clkconfig;
uint32_t pFLatency;
uint32_t uwTimclock;
uint32_t uwPrescalerValue;
__HAL_RCC_TIM4_CLK_ENABLE();
HAL_RCC_GetClockConfig(&clkconfig, &pFLatency);
uwTimclock = HAL_RCC_GetPCLK1Freq();
if (clkconfig.APB1CLKDivider != RCC_HCLK_DIV1) {
uwTimclock *= 2u;
}
uwPrescalerValue = (uwTimclock / 1000000u) - 1u;
TIM4->PSC = uwPrescalerValue;
TIM4->ARR = 1000u - 1u;
TIM4->EGR = TIM_EGR_UG;
TIM4->DIER |= TIM_DIER_UIE;
TIM4->CR1 |= TIM_CR1_CEN;
HAL_NVIC_SetPriority(TIM4_IRQn, TickPriority, 0u);
HAL_NVIC_EnableIRQ(TIM4_IRQn);
return HAL_OK;
}
HAL_StatusTypeDef HAL_DeInitTick(void)
{
TIM4->CR1 &= ~TIM_CR1_CEN;
TIM4->DIER &= ~TIM_DIER_UIE;
HAL_NVIC_DisableIRQ(TIM4_IRQn);
return HAL_OK;
}
void HAL_SuspendTick(void)
{
TIM4->DIER &= ~TIM_DIER_UIE;
}
void HAL_ResumeTick(void)
{
TIM4->DIER |= TIM_DIER_UIE;
}
+78 -250
View File
@@ -1,62 +1,15 @@
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file stm32f1xx_it.c
* @brief Interrupt Service Routines.
******************************************************************************
* @attention
*
* Copyright (c) 2026 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32f1xx_it.h"
#include "FreeRTOS.h"
#include "semphr.h"
#include "task.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN TD */
#include "app_runtime.h"
#include "config.h"
#include "debug_log.h"
#include "uart_trans.h"
/* USER CODE END TD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/* External variables --------------------------------------------------------*/
extern SPI_HandleTypeDef hspi1;
extern DMA_HandleTypeDef hdma_usart1_rx;
extern DMA_HandleTypeDef hdma_usart1_tx;
@@ -67,270 +20,145 @@ extern DMA_HandleTypeDef hdma_usart3_tx;
extern UART_HandleTypeDef huart1;
extern UART_HandleTypeDef huart2;
extern UART_HandleTypeDef huart3;
/* USER CODE BEGIN EV */
/* USER CODE END EV */
/******************************************************************************/
/* Cortex-M3 Processor Interruption and Exception Handlers */
/******************************************************************************/
/**
* @brief This function handles Non maskable interrupt.
*/
void NMI_Handler(void)
{
/* USER CODE BEGIN NonMaskableInt_IRQn 0 */
/* USER CODE END NonMaskableInt_IRQn 0 */
/* USER CODE BEGIN NonMaskableInt_IRQn 1 */
while (1)
{
}
/* USER CODE END NonMaskableInt_IRQn 1 */
while (1) {
}
}
/**
* @brief This function handles Hard fault interrupt.
*/
void HardFault_Handler(void)
{
/* USER CODE BEGIN HardFault_IRQn 0 */
/* USER CODE END HardFault_IRQn 0 */
while (1)
{
/* USER CODE BEGIN W1_HardFault_IRQn 0 */
/* USER CODE END W1_HardFault_IRQn 0 */
}
Debug_TrapWithRttHint("hardfault");
while (1) {
}
}
/**
* @brief This function handles Memory management fault.
*/
void MemManage_Handler(void)
{
/* USER CODE BEGIN MemoryManagement_IRQn 0 */
/* USER CODE END MemoryManagement_IRQn 0 */
while (1)
{
/* USER CODE BEGIN W1_MemoryManagement_IRQn 0 */
/* USER CODE END W1_MemoryManagement_IRQn 0 */
}
Debug_TrapWithRttHint("memmanage");
while (1) {
}
}
/**
* @brief This function handles Prefetch fault, memory access fault.
*/
void BusFault_Handler(void)
{
/* USER CODE BEGIN BusFault_IRQn 0 */
/* USER CODE END BusFault_IRQn 0 */
while (1)
{
/* USER CODE BEGIN W1_BusFault_IRQn 0 */
/* USER CODE END W1_BusFault_IRQn 0 */
}
Debug_TrapWithRttHint("busfault");
while (1) {
}
}
/**
* @brief This function handles Undefined instruction or illegal state.
*/
void UsageFault_Handler(void)
{
/* USER CODE BEGIN UsageFault_IRQn 0 */
/* USER CODE END UsageFault_IRQn 0 */
while (1)
{
/* USER CODE BEGIN W1_UsageFault_IRQn 0 */
/* USER CODE END W1_UsageFault_IRQn 0 */
}
Debug_TrapWithRttHint("usagefault");
while (1) {
}
}
/**
* @brief This function handles Debug monitor.
*/
void DebugMon_Handler(void)
{
/* USER CODE BEGIN DebugMonitor_IRQn 0 */
/* USER CODE END DebugMonitor_IRQn 0 */
/* USER CODE BEGIN DebugMonitor_IRQn 1 */
/* USER CODE END DebugMonitor_IRQn 1 */
}
/**
* @brief This function handles System tick timer.
*/
void SysTick_Handler(void)
{
/* USER CODE BEGIN SysTick_IRQn 0 */
/* USER CODE END SysTick_IRQn 0 */
HAL_IncTick();
#if (INCLUDE_xTaskGetSchedulerState == 1 )
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
{
#endif /* INCLUDE_xTaskGetSchedulerState */
xPortSysTickHandler();
#if (INCLUDE_xTaskGetSchedulerState == 1 )
}
#endif /* INCLUDE_xTaskGetSchedulerState */
/* USER CODE BEGIN SysTick_IRQn 1 */
/* USER CODE END SysTick_IRQn 1 */
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
xPortSysTickHandler();
}
}
/******************************************************************************/
/* STM32F1xx Peripheral Interrupt Handlers */
/* Add here the Interrupt Handlers for the used peripherals. */
/* For the available peripheral interrupt handler names, */
/* please refer to the startup file (startup_stm32f1xx.s). */
/******************************************************************************/
/**
* @brief This function handles DMA1 channel2 global interrupt.
*/
void DMA1_Channel2_IRQHandler(void)
{
/* USER CODE BEGIN DMA1_Channel2_IRQn 0 */
/* USER CODE END DMA1_Channel2_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_usart3_tx);
/* USER CODE BEGIN DMA1_Channel2_IRQn 1 */
/* USER CODE END DMA1_Channel2_IRQn 1 */
HAL_DMA_IRQHandler(&hdma_usart3_tx);
}
/**
* @brief This function handles DMA1 channel3 global interrupt.
*/
void DMA1_Channel3_IRQHandler(void)
{
/* USER CODE BEGIN DMA1_Channel3_IRQn 0 */
/* USER CODE END DMA1_Channel3_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_usart3_rx);
/* USER CODE BEGIN DMA1_Channel3_IRQn 1 */
/* USER CODE END DMA1_Channel3_IRQn 1 */
HAL_DMA_IRQHandler(&hdma_usart3_rx);
}
/**
* @brief This function handles DMA1 channel4 global interrupt.
*/
void DMA1_Channel4_IRQHandler(void)
{
/* USER CODE BEGIN DMA1_Channel4_IRQn 0 */
/* USER CODE END DMA1_Channel4_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_usart1_tx);
/* USER CODE BEGIN DMA1_Channel4_IRQn 1 */
/* USER CODE END DMA1_Channel4_IRQn 1 */
HAL_DMA_IRQHandler(&hdma_usart1_tx);
}
/**
* @brief This function handles DMA1 channel5 global interrupt.
*/
void DMA1_Channel5_IRQHandler(void)
{
/* USER CODE BEGIN DMA1_Channel5_IRQn 0 */
/* USER CODE END DMA1_Channel5_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_usart1_rx);
/* USER CODE BEGIN DMA1_Channel5_IRQn 1 */
/* USER CODE END DMA1_Channel5_IRQn 1 */
HAL_DMA_IRQHandler(&hdma_usart1_rx);
}
/**
* @brief This function handles DMA1 channel6 global interrupt.
*/
void DMA1_Channel6_IRQHandler(void)
{
/* USER CODE BEGIN DMA1_Channel6_IRQn 0 */
/* USER CODE END DMA1_Channel6_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_usart2_rx);
/* USER CODE BEGIN DMA1_Channel6_IRQn 1 */
/* USER CODE END DMA1_Channel6_IRQn 1 */
HAL_DMA_IRQHandler(&hdma_usart2_rx);
}
/**
* @brief This function handles DMA1 channel7 global interrupt.
*/
void DMA1_Channel7_IRQHandler(void)
{
/* USER CODE BEGIN DMA1_Channel7_IRQn 0 */
/* USER CODE END DMA1_Channel7_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_usart2_tx);
/* USER CODE BEGIN DMA1_Channel7_IRQn 1 */
/* USER CODE END DMA1_Channel7_IRQn 1 */
HAL_DMA_IRQHandler(&hdma_usart2_tx);
}
void EXTI0_IRQHandler(void)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if (__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_0) != RESET) {
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0);
if ((xNetSemaphore != NULL) &&
(xTaskGetSchedulerState() == taskSCHEDULER_RUNNING)) {
xSemaphoreGiveFromISR(xNetSemaphore, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
}
/**
* @brief This function handles SPI1 global interrupt.
*/
void SPI1_IRQHandler(void)
{
/* USER CODE BEGIN SPI1_IRQn 0 */
/* USER CODE END SPI1_IRQn 0 */
HAL_SPI_IRQHandler(&hspi1);
/* USER CODE BEGIN SPI1_IRQn 1 */
/* USER CODE END SPI1_IRQn 1 */
HAL_SPI_IRQHandler(&hspi1);
}
/**
* @brief This function handles USART1 global interrupt.
*/
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE) != RESET) {
__HAL_UART_CLEAR_IDLEFLAG(&huart1);
config_uart_idle_handler();
}
HAL_UART_IRQHandler(&huart1);
}
/**
* @brief This function handles USART2 global interrupt.
*/
void USART2_IRQHandler(void)
{
/* USER CODE BEGIN USART2_IRQn 0 */
/* USER CODE END USART2_IRQn 0 */
HAL_UART_IRQHandler(&huart2);
/* USER CODE BEGIN USART2_IRQn 1 */
/* USER CODE END USART2_IRQn 1 */
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if (__HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE) != RESET) {
__HAL_UART_CLEAR_IDLEFLAG(&huart2);
uart_trans_notify_rx_from_isr(UART_CHANNEL_U0, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
HAL_UART_IRQHandler(&huart2);
}
/**
* @brief This function handles USART3 global interrupt.
*/
void USART3_IRQHandler(void)
{
/* USER CODE BEGIN USART3_IRQn 0 */
/* USER CODE END USART3_IRQn 0 */
HAL_UART_IRQHandler(&huart3);
/* USER CODE BEGIN USART3_IRQn 1 */
/* USER CODE END USART3_IRQn 1 */
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if (__HAL_UART_GET_FLAG(&huart3, UART_FLAG_IDLE) != RESET) {
__HAL_UART_CLEAR_IDLEFLAG(&huart3);
uart_trans_notify_rx_from_isr(UART_CHANNEL_U1, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
HAL_UART_IRQHandler(&huart3);
}
/* USER CODE BEGIN 1 */
void TIM4_IRQHandler(void)
{
if ((TIM4->SR & TIM_SR_UIF) != 0u) {
TIM4->SR &= ~TIM_SR_UIF;
}
HAL_IncTick();
}
/* USER CODE END 1 */
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart == &huart2) {
uart_trans_tx_cplt_handler(UART_CHANNEL_U0);
} else if (huart == &huart3) {
uart_trans_tx_cplt_handler(UART_CHANNEL_U1);
}
}
+13 -4
View File
@@ -22,6 +22,9 @@
/* USER CODE BEGIN 0 */
static uint32_t g_usart2_baudrate = 115200u;
static uint32_t g_usart3_baudrate = 115200u;
/* USER CODE END 0 */
UART_HandleTypeDef huart1;
@@ -76,7 +79,7 @@ void MX_USART2_UART_Init(void)
/* USER CODE END USART2_Init 1 */
huart2.Instance = USART2;
huart2.Init.BaudRate = 115200;
huart2.Init.BaudRate = g_usart2_baudrate;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
@@ -105,7 +108,7 @@ void MX_USART3_UART_Init(void)
/* USER CODE END USART3_Init 1 */
huart3.Instance = USART3;
huart3.Init.BaudRate = 115200;
huart3.Init.BaudRate = g_usart3_baudrate;
huart3.Init.WordLength = UART_WORDLENGTH_8B;
huart3.Init.StopBits = UART_STOPBITS_1;
huart3.Init.Parity = UART_PARITY_NONE;
@@ -220,7 +223,7 @@ void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
hdma_usart2_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_usart2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_usart2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_usart2_rx.Init.Mode = DMA_NORMAL;
hdma_usart2_rx.Init.Mode = DMA_CIRCULAR;
hdma_usart2_rx.Init.Priority = DMA_PRIORITY_LOW;
if (HAL_DMA_Init(&hdma_usart2_rx) != HAL_OK)
{
@@ -283,7 +286,7 @@ void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
hdma_usart3_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_usart3_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_usart3_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_usart3_rx.Init.Mode = DMA_NORMAL;
hdma_usart3_rx.Init.Mode = DMA_CIRCULAR;
hdma_usart3_rx.Init.Priority = DMA_PRIORITY_LOW;
if (HAL_DMA_Init(&hdma_usart3_rx) != HAL_OK)
{
@@ -396,4 +399,10 @@ void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
/* USER CODE BEGIN 1 */
void USART_SetConfiguredBaudrates(uint32_t usart2_baudrate, uint32_t usart3_baudrate)
{
g_usart2_baudrate = usart2_baudrate;
g_usart3_baudrate = usart3_baudrate;
}
/* USER CODE END 1 */
+766
View File
@@ -0,0 +1,766 @@
/********************************** (C) COPYRIGHT *****************************
* File Name : CH390.c
* Author : WCH
* Version : V1.1
* Date : 2024/08/20
* Description : CH390 Ethernet controller source file
******************************************************************************
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
* Attention: This software (modified or not) and binary are used for
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
******************************************************************************/
#include "CH390.h"
#include "CH390_Interface.h"
#define CH390_EPCR_POLL_LIMIT 100000u
static int ch390_wait_epcr_ready(void)
{
uint32_t poll_count = CH390_EPCR_POLL_LIMIT;
while ((ch390_read_reg(CH390_EPCR) & 0x01u) != 0u)
{
if (poll_count == 0u)
{
ch390_write_reg(CH390_EPCR, 0x00u);
return -1;
}
--poll_count;
}
return 0;
}
void ch390_probe_rx_header(uint8_t *head)
{
if (head == 0)
{
return;
}
ch390_read_mem(head, 4);
}
int ch390_peek_packet(uint8_t *rx_status, uint16_t *rx_len)
{
uint8_t nsr;
uint8_t header[4];
uint16_t mrr;
if (rx_status != 0)
{
*rx_status = 0u;
}
if (rx_len != 0)
{
*rx_len = 0u;
}
nsr = ch390_read_reg(CH390_NSR);
if ((nsr & NSR_RXRDY) == 0u)
{
return 0;
}
mrr = (uint16_t)ch390_read_mrrl() | ((uint16_t)ch390_read_mrrh() << 8);
ch390_read_mem(header, 4);
ch390_write_reg(CH390_MRRL, (uint8_t)(mrr & 0xffu));
ch390_write_reg(CH390_MRRH, (uint8_t)((mrr >> 8) & 0xffu));
if (rx_status != 0)
{
*rx_status = header[1];
}
if (rx_len != 0)
{
*rx_len = (uint16_t)header[2] | ((uint16_t)header[3] << 8);
}
return 1;
}
/**
* @name ch390_receive_packet
* @brief Receive packet
* @param buff - Size equal to CH390_PKT_MAX
* @param rx_status - Output abnormal status while receiving packet.
* It has the same meaning as RSR(06h).
* @return Packet length
*/
uint32_t ch390_receive_packet(uint8_t *buff, uint8_t *rx_status)
{
uint8_t nsr;
uint8_t ready;
uint16_t rx_len = 0;
uint8_t ReceiveData[4];
if (rx_status != 0)
{
*rx_status = 0u;
}
nsr = ch390_read_reg(CH390_NSR);
if ((nsr & NSR_RXRDY) == 0u)
{
return 0;
}
(void)ch390_read_mrcmdx();
ready = ch390_read_mrcmdx1();
if (ready == 0u)
{
return 0;
}
if (ready != CH390_PKT_RDY)
{
ch390_rx_reset();
return 0;
}
ch390_read_mem(ReceiveData, 4);
if (rx_status != 0)
{
*rx_status = ReceiveData[1];
}
rx_len = (uint16_t)ReceiveData[2] | ((uint16_t)ReceiveData[3] << 8);
if ((ReceiveData[0] != CH390_PKT_RDY) ||
((ReceiveData[1] & 0x3Fu) != 0u) ||
(rx_len < (uint16_t)(14u + CH390_PKT_CRC_LEN)) ||
(rx_len > CH390_PKT_MAX))
{
ch390_rx_reset();
return 0;
}
ch390_read_mem(buff, rx_len);
return (uint32_t)(rx_len - CH390_PKT_CRC_LEN);
}
/**
* @name ch390_send_packet
* @brief Send packet
* @param buff - Data to be sent
* @param length - Less than 3k bytes.
*/
int ch390_send_packet(uint8_t *buff, uint16_t length)
{
uint32_t spin_count = 0u;
// Write data to SRAM
ch390_write_mem(buff, length);
// Wait until last transmit complete
while ((ch390_read_reg(CH390_TCR) & TCR_TXREQ) != 0u)
{
++spin_count;
if (spin_count >= 4096u)
{
return -1;
}
}
// Set current packet length
ch390_write_reg(CH390_TXPLL, length & 0xff);
ch390_write_reg(CH390_TXPLH, (length >> 8) & 0xff);
// Issue transmit request
ch390_send_request();
return 0;
}
/**
* @name ch390_send_request
* @brief Issue transmit request
*/
void ch390_send_request()
{
uint8_t tcr = ch390_read_reg(CH390_TCR);
ch390_write_reg(CH390_TCR, tcr | TCR_TXREQ);
}
/**
* @name ch390_drop_packet
* @brief Drop packet in RX SRAM if don't want to read it. This function
* modify the memory data read pointer and skip specified length
* @param len - Skip length, length of the current packet.
*/
void ch390_drop_packet(uint16_t len)
{
uint16_t mdr = (uint16_t)ch390_read_mrrl() | ((uint16_t)ch390_read_mrrh() << 8);
#ifdef CH390_INTERFACE_16_BIT
mdr = mdr + (len + 1) / 2 * 2;
#else
mdr = mdr + len;
#endif
mdr = mdr < 0x4000 ? mdr : mdr - 0x3400;
ch390_write_reg(CH390_MRRL, mdr & 0xff);
ch390_write_reg(CH390_MRRH, (mdr >> 8) & 0xff);
}
void ch390_rx_reset(void)
{
uint8_t rcr = ch390_read_reg(CH390_RCR);
ch390_write_reg(CH390_RCR, (uint8_t)(rcr & (uint8_t)(~RCR_RXEN)));
ch390_write_reg(CH390_MPTRCR, MPTRCR_RST_RX);
ch390_write_reg(CH390_NSR, NSR_RXOV);
ch390_write_reg(CH390_ISR, (uint8_t)(ISR_ROS | ISR_ROO | ISR_PR));
ch390_write_reg(CH390_RCR, (uint8_t)(rcr | RCR_RXEN));
}
/**
* @name ch390_read_phy
* @brief Read PHY register
* @param reg - PHY register address
*/
uint16_t ch390_read_phy(uint8_t reg)
{
ch390_write_reg(CH390_EPAR, CH390_PHY | reg);
// Chose PHY, send read command
ch390_write_reg(CH390_EPCR, EPCR_ERPRR | EPCR_EPOS);
if (ch390_wait_epcr_ready() != 0)
{
return 0xFFFFu;
}
// Clear read command
ch390_write_reg(CH390_EPCR, 0x00);
return (ch390_read_reg(CH390_EPDRH) << 8) |
(ch390_read_reg(CH390_EPDRL) & 0xFF);
}
/**
* @name ch390_write_phy
* @brief Write PHY register
* @param reg - PHY register address
* @param value - Value to be written
*/
void ch390_write_phy(uint8_t reg, uint16_t value)
{
ch390_write_reg(CH390_EPAR, CH390_PHY | reg);
ch390_write_reg(CH390_EPDRL, (value & 0xff)); // Low byte
ch390_write_reg(CH390_EPDRH, ((value >> 8) & 0xff)); // High byte
// Chose PHY, send write command
ch390_write_reg(CH390_EPCR, 0x0A);
if (ch390_wait_epcr_ready() != 0)
{
return;
}
// Clear write command
ch390_write_reg(CH390_EPCR, 0x00);
}
/**
* @name ch390_write_eeprom
* @brief Write EEPROM register
* @param reg - EEPROM register address
* @param value - Value to be written
*/
void ch390_write_eeprom(uint8_t reg, uint16_t value)
{
ch390_write_reg(CH390_EPAR, reg);
ch390_write_reg(CH390_EPDRL, (value & 0xff)); // Low byte
ch390_write_reg(CH390_EPDRH, ((value >> 8) & 0xff)); // High byte
// Chose EEPROM, send write command
ch390_write_reg(CH390_EPCR, EPCR_ERPRW);
if (ch390_wait_epcr_ready() != 0)
{
return;
}
// Clear write command
ch390_write_reg(CH390_EPCR, 0x00);
}
/**
* @name ch390_software_reset
* @brief Software reset CH390 by NCR
*/
void ch390_software_reset()
{
ch390_write_reg(CH390_NCR, NCR_RST);
ch390_delay_us(10);
ch390_write_reg(CH390_NCR, 0);
ch390_write_reg(CH390_NCR, NCR_RST);
ch390_delay_us(10);
}
/**
* @name ch390_default_config
* @brief Config CH390 with default options:
* LED mode 1;
* Enable transmit check sum generation;
* Enable RX;
* Enable all interrupt and PAR
*/
void ch390_default_config()
{
// CH390 has built-in MAC, this is not necessary
// uint8_t mac_addr[6] = { 0x50, 0x54, 0x7B, 0x84, 0x00, 0x73 };
// Multicast address hash table
uint8_t multicase_addr[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
ch390_set_phy_mode(CH390_AUTO);
// Clear status
ch390_write_reg(CH390_NSR, NSR_WAKEST | NSR_TX2END | NSR_TX1END);
ch390_write_reg(CH390_ISR, 0xFF); // Clear interrupt status
ch390_write_reg(CH390_INTCR, (uint8_t)(INCR_TYPE_OD | INCR_POL_L));
ch390_write_reg(CH390_TCR2, 0x80); // LED mode 1
ch390_write_reg(CH390_TCSCR, TCSCR_ALL); // Enable check sum generation
// ch390_set_mac_address(mac_addr);
ch390_set_multicast(multicase_addr);
ch390_write_reg(CH390_BCASTCR, 0x00);
ch390_write_reg(CH390_MAR + 7, 0x80);
// Keep pointer auto-return enabled to stay aligned with the reference behavior.
ch390_write_reg(CH390_IMR, (uint8_t)(IMR_PAR | IMR_PRI | IMR_LNKCHGI | IMR_ROOI | IMR_ROI));
// Enable RX
ch390_write_reg(CH390_RCR, RCR_DIS_CRC | RCR_RXEN);
}
/**
* @name ch390_set_phy_mode
* @brief Set PHY mode and enable PHY.
* PHY mode: Auto-negotiation, 10M/100M, full-duplex/half-duplex
* @param mode - PHY mode
*/
void ch390_set_phy_mode(enum ch390_phy_mode mode)
{
uint16_t BMCR_value = 0;
uint16_t ANAR_value = 0;
switch (mode)
{
case CH390_10MFD:
BMCR_value = 0x1100;
ANAR_value = 0x41;
break;
case CH390_100MFD:
BMCR_value = 0x3100;
ANAR_value = 0x101;
break;
case CH390_AUTO:
BMCR_value = 0x1000;
ANAR_value = 0x01E1;
break;
}
ch390_write_phy(CH390_PHY_BMCR, BMCR_value);
ch390_write_phy(CH390_PHY_ANAR, ANAR_value);
ch390_write_reg(CH390_GPR, 0x00); // Enable PHY
}
/**
* @name ch390_set_mac_address
* @brief Set mac address
* @param mac_addr - 6-byte length mac address array
*/
void ch390_set_mac_address(uint8_t *mac_addr)
{
uint8_t i;
for (i = 0; i < 6; i++)
{
ch390_write_reg(CH390_PAR + i, mac_addr[i]);
}
}
/**
* @name ch390_set_multicast
* @brief Set multicast address hash table
* @param multicast_addr - 8-byte length multicast address hash table array
*/
void ch390_set_multicast(uint8_t *multicast_hash)
{
uint8_t i;
for (i = 0; i < 8; i++)
{
ch390_write_reg(CH390_MAR + i, multicast_hash[i]);
}
}
/**
* @brief reflect an 8bit value.
* Only for "ch390_compute_hash_bit"
*/
static uint8_t reflect_8(uint8_t val)
{
int i;
uint8_t resVal = 0;
for (i = 0; i < 8; i++)
{
if ((val & (1 << i)) != 0)
{
resVal |= 1 << (7 - i);
}
}
return resVal;
}
/**
* @brief Calculate the corresponding hash bit of the MAC address.
* Only for "ch390_set_hash_bit"
* @param mac - Destination address
* @return Hash bit number
*/
static uint8_t ch390_compute_hash_bit(uint8_t *mac)
{
int i;
const uint32_t poly = 0x4C11DB7;
uint32_t crc = 0xffffffff;
int byte_i = 0;
for(byte_i = 0; byte_i < 6; byte_i++)
{
uint8_t cur_byte = reflect_8(mac[byte_i]);
crc ^= cur_byte << 24;
for (i = 0; i < 8; i++)
{
if ((crc & 0x80000000) != 0)
{
crc = (crc << 1) ^ poly;
}
else
{
crc <<= 1;
}
}
}
return (crc ^ 0xffffffff) >> 26;
}
/**
* @brief Set MAR bit for a particular MAC address
* @param mac - Destination address
*/
void ch390_set_hash_bit(uint8_t *mac)
{
uint8_t bit = ch390_compute_hash_bit(mac);
uint8_t mar = CH390_MAR + bit / 8;
uint8_t mar_val = ch390_read_reg(mar);
mar_val |= 1 << (bit % 8);
ch390_write_reg(mar, mar_val);
}
/**
* @name ch390_get_mac
* @brief Get mac address
* @param mac_addr - 6-byte length mac address output
*/
void ch390_get_mac(uint8_t *mac_addr)
{
uint8_t i;
for (i = 0; i < 6; i++)
{
mac_addr[i] = ch390_read_reg(CH390_PAR + i);
}
}
/**
* @name ch390_get_multicast
* @brief Get multicast address hash table
* @param multicast_addr - 8-byte length multicast address hash table output
*/
void ch390_get_multicast(uint8_t *multicast_hash)
{
uint8_t i;
for (i = 0; i < 8; i++)
{
multicast_hash[i] = ch390_read_reg(CH390_MAR + i);
}
}
/**
* @name ch390_get_vendor_id
* @brief Get vendor ID
* @return Vendor ID
*/
uint16_t ch390_get_vendor_id()
{
uint16_t id;
id = (ch390_read_reg(CH390_VIDL) & 0xff);
id |= ch390_read_reg(CH390_VIDH) << 8;
return id;
}
/**
* @name ch390_get_product_id
* @brief Get product ID
* @return Product ID
*/
uint16_t ch390_get_product_id()
{
uint16_t id;
id = (ch390_read_reg(CH390_PIDL) & 0xff);
id |= ch390_read_reg(CH390_PIDH) << 8;
return id;
}
/**
* @name ch390_get_revision
* @brief Get chip revision
* @return Chip revision
*/
uint8_t ch390_get_revision()
{
return ch390_read_reg(CH390_CHIPR);
}
/**
* @name ch390_interrupt_config
* @brief Interrupt configuration
* @param mask - Interrupt to be enabled, see "CH390.h" IMR_xxx
*/
void ch390_interrupt_config(uint8_t mask)
{
ch390_write_reg(CH390_IMR, mask);
}
/**
* @name ch390_rx_enable
* @brief Enable or disable packet receive
* @param op - 0: disable 1: enable
*/
void ch390_rx_enable(int op)
{
uint8_t rcr = ch390_read_reg(CH390_RCR);
if(op == 0)
rcr &= ~RCR_RXEN;
else
rcr |= RCR_RXEN;
ch390_write_reg(CH390_RCR, rcr);
}
/**
* @name ch390_rx_filter_config
* @brief Configure receive filter.
* @param config - See "CH390.h" RCR_xxx
*/
void ch390_rx_filter_config(uint8_t config)
{
uint8_t rcr = ch390_read_reg(CH390_RCR) & RCR_RXEN;
ch390_write_reg(CH390_RCR, rcr | config);
}
/**
* @name ch390_wakeup_config
* @brief Enable or disable wakeup_function
* @param events - Events that trigger wakeup
* WCR_LINKEN - Link status change
* WCR_SAMPLEEN - Sample frame
* WCR_MAGICEN - Magic packet
* 0 - Disable wakeup function
*/
void ch390_wakeup_config(uint8_t events)
{
uint8_t ncr = ch390_read_reg(CH390_NCR);
if(events)
ncr |= NCR_WAKEEN;
else {
ncr &= ~NCR_WAKEEN;
}
ch390_write_reg(CH390_NCR, ncr);
ch390_write_reg(CH390_WCR, events);
}
/**
* @name ch390_wake_notify
* @brief Wait for Magic Packet or Sample Frame and discard all
* other packets.
* If the application needs to use Wake On LAN, call this
* function every time before MCU enters low power mode.
* An external interrupt signal is accessible on WOL pin
* when wake up event occurred.
*/
void ch390_wake_notify(void)
{
uint8_t ncr = ch390_read_reg(CH390_NCR);
ch390_write_reg(CH390_NCR, ncr ^ 0x10);
}
/**
* @name ch390_loop_back_enable
* @brief Enable loop back mode
* @param op - 0: disable 1: enable
*/
void ch390_loop_back_enable(int op)
{
uint8_t ncr = ch390_read_reg(CH390_NCR) & ~0x06;
if(op == 1) ncr |= NCR_LBK_MAC;
ch390_write_reg(CH390_NCR, ncr);
}
/**
* @name ch390_get_duplex_mode
* @brief Get current duplex mode of the internal PHY
* @return 0: Half-duplex 1: Full-duplex
*/
int ch390_get_duplex_mode()
{
return !!(ch390_read_reg(CH390_NCR) & NCR_FDX);
}
/**
* @name ch390_get_phy_speed
* @brief Get the speed of the internal PHY.
* Only valid after PHY linked
* @return 0: 100Mbps 1: 10Mbps
*/
int ch390_get_phy_speed()
{
return !!(ch390_read_reg(CH390_NSR) & NSR_SPEED);
}
/**
* @name ch390_get_link_status
* @brief Get link status of the internal PHY
* @return 0: Link failed 1: Link OK
*/
int ch390_get_link_status()
{
uint8_t nsr = ch390_read_reg(CH390_NSR);
return !!(nsr & NSR_LINKST);
}
/**
* @name ch390_sleep_control
* @brief Enter or exit sleep mode
* @param op - 0: Power up 1: Power down
*/
void ch390_sleep_control(int op)
{
if(op)
{
ch390_write_reg(CH390_SCCR, 0x01);
}
else
{
ch390_read_reg(CH390_RSCCR);
ch390_delay_us(100);
}
}
#ifndef CH390_INTERFACE_16_BIT
/**
* @name ch390_gpio_config
* @brief Config the input/output direction of GPIO1~3
* In 8-bit mode, GPIO4~6 are output only
* @param GPIOx - CH390_GPIO1 ~ CH390_GPIO3
* dir - 0: Input 1: Output
*/
void ch390_gpio_config(uint8_t GPIOx, uint8_t dir)
{
uint8_t gpcr = ch390_read_reg(CH390_GPCR);
if(dir)
{
gpcr |= GPIOx;
}
else {
gpcr &= ~GPIOx;
}
ch390_write_reg(CH390_GPCR, gpcr);
}
/**
* @name ch390_gpio_write_bit
* @brief Sets or clears the selected gpio bit.
* In SPI mode, only GPIO1~3 are available
* @param GPIOx - CH390_GPIO1 ~ CH390_GPIO6
* level - 0: Clear pin 1: Set pin
*/
void ch390_gpio_write_bit(uint8_t GPIOx, uint8_t level)
{
uint8_t gpr = ch390_read_reg(CH390_GPR);
if(level)
{
gpr |= GPIOx;
}
else {
gpr &= ~GPIOx;
}
ch390_write_reg(CH390_GPR, gpr);
}
/**
* @name ch390_gpio_read_bit
* @brief Read gpio input, only CH390_GPIO1 ~ 3 are available
* @param GPIOx - CH390_GPIO1 ~ CH390_GPIO3
* @return Input pin value
*/
uint8_t ch390_gpio_read_bit(uint8_t GPIOx)
{
uint8_t gpr = ch390_read_reg(CH390_GPR);
return !!(gpr & GPIOx);
}
#endif
/**
* @name ch390_int_pin_config
* @brief Configure INT pin output type and polarity
* @param type - INCR_TYPE_OD: Open drain output
* INCR_TYPE_PP: Push pull output
* pol - INCR_POL_L: Active low
* INCR_POL_H: Active high
*/
void ch390_int_pin_config(uint8_t type, uint8_t pol)
{
ch390_write_reg(CH390_INTCR, type | pol);
}
/**
* @name ch390_get_int_status
* @brief Get CH390 interrupt status and clear them
* @return Interrupt status
*/
uint8_t ch390_get_int_status()
{
uint8_t int_status = ch390_read_reg(CH390_ISR);
// Clear interrupt status by write 1
ch390_write_reg(CH390_ISR, int_status);
return int_status;
}
uint8_t ch390_runtime_poll(struct ch390_runtime_status *status)
{
uint8_t int_status = ch390_read_reg(CH390_ISR);
if (status != 0)
{
status->int_status = int_status;
status->nsr = ch390_read_reg(CH390_NSR);
status->bcastcr = ch390_read_reg(CH390_BCASTCR);
status->mar7 = ch390_read_reg(CH390_MAR + 7u);
status->mrcmdx = 0u;
status->mrcmdx1 = 0u;
status->mrrl = 0u;
status->mrrh = 0u;
status->link_up = ((status->nsr & NSR_LINKST) != 0u) ? 1u : 0u;
}
ch390_write_reg(CH390_ISR, int_status);
return int_status;
}
int ch390_runtime_link_up_from_status(const struct ch390_runtime_status *status)
{
if (status == 0)
{
return 0;
}
return (status->link_up != 0u) ? 1 : 0;
}
uint32_t ch390_runtime_receive_packet(uint8_t *buff, uint8_t *rx_status)
{
return ch390_receive_packet(buff, rx_status);
}
int ch390_runtime_send_packet(uint8_t *buff, uint16_t length)
{
return ch390_send_packet(buff, length);
}
+697
View File
@@ -0,0 +1,697 @@
/********************************** (C) COPYRIGHT *****************************
* File Name : CH390.h
* Author : WCH
* Version : V1.1
* Date : 2024/08/20
* Description : CH390 Ethernet controller header file
******************************************************************************
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
* Attention: This software (modified or not) and binary are used for
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
******************************************************************************/
#ifndef __CH390_H
#define __CH390_H
#include <stdint.h>
/********************************************************************
* Interface selection
*/
#define CH390_INTERFACE_SPI // CH390H/CH390D
// #define CH390_INTERFACE_8_BIT // CH390L/CH390F 8-bit mode
// #define CH390_INTERFACE_16_BIT // CH390L 16-bit mode
/* PHY mode definition */
enum ch390_phy_mode
{
CH390_10MFD, // 10M full-duplex
CH390_100MFD, // 100M full-duplex
CH390_AUTO, // Auto negotiation
};
/********************************************************************
* Register definition
*
* There are some differences between the register definitions of
* CH390H and CH390L
*/
#ifdef CH390_INTERFACE_SPI
#define CH390_NCR 0x00
#define NCR_WAKEEN (1<<6) // Enable wakeup function
#define NCR_FDX (1<<3) // Duplex mode of the internal PHY
#define NCR_LBK_MAC (1<<1) // MAC loop-back
#define NCR_RST (1<<0) // Softwate reset
#define CH390_NSR 0x01
#define NSR_SPEED (1<<7) // Speed of internal PHY
#define NSR_LINKST (1<<6) // Link status of internal PHY
#define NSR_WAKEST (1<<5) // Wakeup event status
#define NSR_TX2END (1<<3) // Tx packet B complete status
#define NSR_TX1END (1<<2) // Tx packet A complete status
#define NSR_RXOV (1<<1) // Rx fifo overflow
#define NSR_RXRDY (1<<0)
#define CH390_TCR 0x02
#define TCR_TJDIS (1<<6) // Transmit jabber timer
#define TCR_PAD_DIS2 (1<<4) // PAD appends for packet B
#define TCR_CRC_DIS2 (1<<3) // CRC appends for packet B
#define TCR_PAD_DIS1 (1<<2) // PAD appends for packet A
#define TCR_CRC_DIS1 (1<<1) // CRC appends for packet A
#define TCR_TXREQ (1<<0) // Tx request
#define CH390_TSRA 0x03
#define CH390_TSRB 0x04
#define TSR_TJTO (1<<7) // Transmit jabber time out
#define TSR_LC (1<<6) // Loss of carrier
#define TSR_NC (1<<5) // No carrier
#define TSR_LCOL (1<<4) // Late collision
#define TSR_COL (1<<3) // Collision packet
#define TSR_EC (1<<2) // Excessive collision
#define CH390_RCR 0x05
#define RCR_DEFAULT 0x00 // Default settings
#define RCR_WTDIS (1<<6) // Disable 2048 bytes watch dog
#define RCR_DIS_CRC (1<<4) // Discard CRC error packet
#define RCR_ALL (1<<3) // Pass all multicast
#define RCR_RUNT (1<<2) // Pass runt packet
#define RCR_PRMSC (1<<1) // Promiscuous mode
#define RCR_RXEN (1<<0) // Enable RX
#define CH390_RSR 0x06
#define RSR_RF (1<<7) // Rnt frame
#define RSR_MF (1<<6) // Multicast frame
#define RSR_LCS (1<<5) // Late collision seen
#define RSR_RWTO (1<<4) // Receive watchdog time-out
#define RSR_PLE (1<<3) // Physical layer error
#define RSR_AE (1<<2) // Alignment error
#define RSR_CE (1<<1) // CRC error
#define RSR_FOE (1<<0) // FIFO overflow error
#define CH390_ROCR 0x07
#define CH390_BPTR 0x08
#define CH390_FCTR 0x09
#define FCTR_HWOT(ot) (( ot & 0xf ) << 4)
#define FCTR_LWOT(ot) ( ot & 0xf )
#define CH390_FCR 0x0A
#define CH390_EPCR 0x0B
#define EPCR_REEP (1<<5) // Reload EEPROM
#define EPCR_EPOS (1<<3) // EEPROM or PHY operation select
#define EPCR_ERPRR (1<<2) // EEPROM or PHY read command
#define EPCR_ERPRW (1<<1) // EEPROM or PHY write command
#define EPCR_ERRE (1<<0) // EEPROM or PHY access status
#define CH390_EPAR 0x0C
#define CH390_EPDRL 0x0D
#define CH390_EPDRH 0x0E
#define CH390_WCR 0x0F
#define WCR_LINKEN (1<<5) // Link status change wakeup
#define WCR_SAMPLEEN (1<<4) // Sample frame wakeup
#define WCR_MAGICEN (1<<3) // Magic packet wakeup
#define WCR_LINKST (1<<2) // Link status change event
#define WCR_SAMPLEST (1<<1) // Sample frame event
#define WCR_MAGICST (1<<0) // Magic packet event
#define CH390_PAR 0x10
#define CH390_MAR 0x16
#define CH390_GPCR 0x1E
#define CH390_GPR 0x1F
#define CH390_TRPAL 0x22
#define CH390_TRPAH 0x23
#define CH390_RWPAL 0x24
#define CH390_RWPAH 0x25
#define CH390_VIDL 0x28
#define CH390_VIDH 0x29
#define CH390_PIDL 0x2A
#define CH390_PIDH 0x2B
#define CH390_CHIPR 0x2C
#define CH390_TCR2 0x2D
#define CH390_ATCR 0x30
#define CH390_TCSCR 0x31
#define TCSCR_ALL 0x1F
#define TCSCR_IPv6TCPCSE (1<<4) // IPv6 TCP checksum generation
#define TCSCR_IPv6UDPCSE (1<<3) // IPv6 UDP checksum generation
#define TCSCR_UDPCSE (1<<2) // UDP checksum generation
#define TCSCR_TCPCSE (1<<1) // TCP checksum generation
#define TCSCR_IPCSE (1<<0) // IP checksum generation
#define CH390_RCSCSR 0x32
#define RCSCSR_UDPS (1<<7) // UDP checksum status
#define RCSCSR_TCPS (1<<6) // TCP checksum status
#define RCSCSR_IPS (1<<5) // IP checksum status
#define RCSCSR_UDPP (1<<4) // UDP packet of current received packet
#define RCSCSR_TCPP (1<<3) // TCP packet of current received packet
#define RCSCSR_IPP (1<<2) // IP packet of current received packet
#define RCSCSR_RCSEN (1<<1) // Receive checksum checking enable
#define RCSCSR_DCSE (1<<0) // Discard checksum error packet
#define CH390_MPAR 0x33
#define CH390_SBCR 0x38
#define CH390_INTCR 0x39
#define INCR_TYPE_OD 0x02
#define INCR_TYPE_PP 0x00
#define INCR_POL_L 0x01
#define INCR_POL_H 0x00
#define CH390_ALNCR 0x4A
#define CH390_SCCR 0x50
#define CH390_RSCCR 0x51
#define CH390_RLENCR 0x52
#define CH390_BCASTCR 0x53
#define CH390_INTCKCR 0x54
#define CH390_MPTRCR 0x55
#define MPTRCR_RST_TX (1<<1)
#define MPTRCR_RST_RX (1<<0)
#define CH390_MLEDCR 0x57
#define CH390_MRCMDX 0x70
#define CH390_MRCMDX1 0x71
#define CH390_MRCMD 0x72
#define CH390_MRRL 0x74
#define CH390_MRRH 0x75
#define CH390_MWCMDX 0x76
#define CH390_MWCMD 0x78
#define CH390_MWRL 0x7A
#define CH390_MWRH 0x7B
#define CH390_TXPLL 0x7C
#define CH390_TXPLH 0x7D
#define CH390_ISR 0x7E
#define ISR_LNKCHG (1<<5) // Link status change
#define ISR_ROO (1<<3) // Receive overflow counter overflow
#define ISR_ROS (1<<2) // Receive overflow
#define ISR_PT (1<<1) // Packet transmitted
#define ISR_PR (1<<0) // Packet received
#define CH390_IMR 0x7F
#define IMR_NONE 0x00 // Disable all interrupt
#define IMR_ALL 0xFF // Enable all interrupt
#define IMR_PAR (1<<7) // Pointer auto-return mode
#define IMR_LNKCHGI (1<<5) // Enable link status change interrupt
#define IMR_UDRUNI (1<<4) // Enable transmit under-run interrupt
#define IMR_ROOI (1<<3) // Enable receive overflow counter overflow interrupt
#define IMR_ROI (1<<2) // Enable receive overflow interrupt
#define IMR_PTI (1<<1) // Enable packet transmitted interrupt
#define IMR_PRI (1<<0) // Enable packet received interrupt
// SPI commands
#define OPC_REG_W 0x80 // Register Write
#define OPC_REG_R 0x00 // Register Read
#define OPC_MEM_DMY_R 0x70 // Memory Dummy Read
#define OPC_MEM_WRITE 0xF8 // Memory Write
#define OPC_MEM_READ 0x72 // Memory Read
// GPIO
#define CH390_GPIO1 0x02
#define CH390_GPIO2 0x04
#define CH390_GPIO3 0x08
#else
#define CH390_NCR 0x00
#define NCR_WAKEEN (1<<6) // Enable wakeup function
#define NCR_FDX (1<<3) // Duplex mode of the internal PHY
#define NCR_LBK_MAC (1<<1) // MAC loop-back
#define NCR_RST (1<<0) // Softwate reset
#define CH390_NSR 0x01
#define NSR_SPEED (1<<7) // Speed of internal PHY
#define NSR_LINKST (1<<6) // Link status of internal PHY
#define NSR_WAKEST (1<<5) // Wakeup event status
#define NSR_TX2END (1<<3) // Tx packet B complete status
#define NSR_TX1END (1<<2) // Tx packet A complete status
#define NSR_RXOV (1<<1) // Rx fifo overflow
#define CH390_TCR 0x02
#define TCR_TJDIS (1<<6) // Transmit jabber timer
#define TCR_PAD_DIS2 (1<<4) // PAD appends for packet B
#define TCR_CRC_DIS2 (1<<3) // CRC appends for packet B
#define TCR_PAD_DIS1 (1<<2) // PAD appends for packet A
#define TCR_CRC_DIS1 (1<<1) // CRC appends for packet A
#define TCR_TXREQ (1<<0) // Tx request
#define CH390_TSRA 0x03
#define CH390_TSRB 0x04
#define TSR_TJTO (1<<7) // Transmit jabber time out
#define TSR_LC (1<<6) // Loss of carrier
#define TSR_NC (1<<5) // No carrier
#define TSR_LCOL (1<<4) // Late collision
#define TSR_COL (1<<3) // Collision packet
#define TSR_EC (1<<2) // Excessive collision
#define CH390_RCR 0x05
#define RCR_DEFAULT 0x00 // Default settings
#define RCR_WTDIS (1<<6) // Disable 2048 bytes watch dog
#define RCR_DIS_CRC (1<<4) // Discard CRC error packet
#define RCR_ALL (1<<3) // Pass all multicast
#define RCR_RUNT (1<<2) // Pass runt packet
#define RCR_PRMSC (1<<1) // Promiscuous mode
#define RCR_RXEN (1<<0) // Enable RX
#define CH390_RSR 0x06
#define RSR_RF (1<<7) // Rnt frame
#define RSR_MF (1<<6) // Multicast frame
#define RSR_LCS (1<<5) // Late collision seen
#define RSR_RWTO (1<<4) // Receive watchdog time-out
#define RSR_PLE (1<<3) // Physical layer error
#define RSR_AE (1<<2) // Alignment error
#define RSR_CE (1<<1) // CRC error
#define RSR_FOE (1<<0) // FIFO overflow error
#define CH390_ROCR 0x07
#define CH390_BPTR 0x08
#define CH390_FCTR 0x09
#define FCTR_HWOT(ot) (( ot & 0xf ) << 4)
#define FCTR_LWOT(ot) ( ot & 0xf )
#define CH390_FCR 0x0A
#define CH390_EPCR 0x0B
#define EPCR_REEP (1<<5) // Reload EEPROM
#define EPCR_EPOS (1<<3) // EEPROM or PHY operation select
#define EPCR_ERPRR (1<<2) // EEPROM or PHY read command
#define EPCR_ERPRW (1<<1) // EEPROM or PHY write command
#define EPCR_ERRE (1<<0) // EEPROM or PHY access status
#define CH390_EPAR 0x0C
#define CH390_EPDRL 0x0D
#define CH390_EPDRH 0x0E
#define CH390_WCR 0x0F
#define WCR_LINKEN (1<<5) // Link status change wakeup
#define WCR_SAMPLEEN (1<<4) // Sample frame wakeup
#define WCR_MAGICEN (1<<3) // Magic packet wakeup
#define WCR_LINKST (1<<2) // Link status change event
#define WCR_SAMPLEST (1<<1) // Sample frame event
#define WCR_MAGICST (1<<0) // Magic packet event
#define CH390_PAR 0x10
#define CH390_MAR 0x16
#define CH390_GPCR 0x1E
#define CH390_GPR 0x1F
#define CH390_TRPAL 0x22
#define CH390_TRPAH 0x23
#define CH390_RWPAL 0x24
#define CH390_RWPAH 0x25
#define CH390_VIDL 0x28
#define CH390_VIDH 0x29
#define CH390_PIDL 0x2A
#define CH390_PIDH 0x2B
#define CH390_CHIPR 0x2C
#define CH390_TCR2 0x2D
#define CH390_ETXCSR 0x30
#define CH390_TCSCR 0x31
#define TCSCR_ALL 0x1F
#define TCSCR_IPv6TCPCSE (1<<4) // IPv6 TCP checksum generation
#define TCSCR_IPv6UDPCSE (1<<3) // IPv6 UDP checksum generation
#define TCSCR_UDPCSE (1<<2) // UDP checksum generation
#define TCSCR_TCPCSE (1<<1) // TCP checksum generation
#define TCSCR_IPCSE (1<<0) // IP checksum generation
#define CH390_RCSCSR 0x32
#define RCSCSR_UDPS (1<<7) // UDP checksum status
#define RCSCSR_TCPS (1<<6) // TCP checksum status
#define RCSCSR_IPS (1<<5) // IP checksum status
#define RCSCSR_UDPP (1<<4) // UDP packet of current received packet
#define RCSCSR_TCPP (1<<3) // TCP packet of current received packet
#define RCSCSR_IPP (1<<2) // IP packet of current received packet
#define RCSCSR_RCSEN (1<<1) // Receive checksum checking enable
#define RCSCSR_DCSE (1<<0) // Discard checksum error packet
#define CH390_MPAR 0x33
#define CH390_LEDCR 0x34
#define CH390_INTCR 0x39
#define INCR_TYPE_OD 0x02
#define INCR_TYPE_PP 0x00
#define INCR_POL_L 0x01
#define INCR_POL_H 0x00
#define CH390_SCCR 0x50
#define CH390_RSCCR 0x51
#define CH390_RLENCR 0x52
#define CH390_BCASTCR 0x53
#define CH390_MPTRCR 0x55
#define MPTRCR_RST_TX (1<<1)
#define MPTRCR_RST_RX (1<<0)
#define CH390_MRCMDX 0xF0
#define CH390_MRCMDX1 0xF1
#define CH390_MRCMD 0xF2
#define CH390_MRRL 0xF4
#define CH390_MRRH 0xF5
#define CH390_MWCMDX 0xF6
#define CH390_MWCMD 0xF8
#define CH390_MWRL 0xFA
#define CH390_MWRH 0xFB
#define CH390_TXPLL 0xFC
#define CH390_TXPLH 0xFD
#define CH390_ISR 0xFE
#define ISR_IOMODE (1<<7) // Parallel interface mode
#define ISR_LNKCHG (1<<5) // Link status change
#define ISR_UDRUN (1<<4) // Transmit under-run
#define ISR_ROO (1<<3) // Receive overflow counter overflow
#define ISR_ROS (1<<2) // Receive overflow
#define ISR_PT (1<<1) // Packet transmitted
#define ISR_PR (1<<0) // Packet received
#define CH390_IMR 0xFF
#define IMR_NONE 0x00 // Disable all interrupt
#define IMR_ALL 0xFF // Enable all interrupt
#define IMR_PAR (1<<7) // Pointer auto-return mode
#define IMR_LNKCHGI (1<<5) // Enable link status change interrupt
#define IMR_UDRUNI (1<<4) // Enable transmit under-run interrupt
#define IMR_ROOI (1<<3) // Enable receive overflow counter overflow interrupt
#define IMR_ROI (1<<2) // Enable receive overflow interrupt
#define IMR_PTI (1<<1) // Enable packet transmitted interrupt
#define IMR_PRI (1<<0) // Enable packet received interrupt
// GPIO
#define CH390_GPIO1 0x02
#define CH390_GPIO2 0x04
#define CH390_GPIO3 0x08
#define CH390_GPIO4 0x10
#define CH390_GPIO5 0x20
#define CH390_GPIO6 0x40
#endif
// PHY registers
#define CH390_PHY 0x40
#define CH390_PHY_BMCR 0x00
#define CH390_PHY_BMSR 0x01
#define CH390_PHY_PHYID1 0x02
#define CH390_PHY_PHYID2 0x03
#define CH390_PHY_ANAR 0x04
#define CH390_PHY_ANLPAR 0x05
#define CH390_PHY_ANER 0x06
#define CH390_PHY_PAGE_SEL 0x1F
// Packet status
#define CH390_PKT_NONE 0x00 /* No packet received */
#define CH390_PKT_RDY 0x01 /* Packet ready to receive */
#define CH390_PKT_ERR 0xFE /* Un-stable states */
#define CH390_PKT_CRC_LEN 4u /* Ethernet FCS stored in RX SRAM */
#define CH390_PKT_MAX 1536 /* Received packet max size */
#define CH390_PKT_MIN 64
struct ch390_runtime_status
{
uint8_t int_status;
uint8_t nsr;
uint8_t bcastcr;
uint8_t mar7;
uint8_t mrcmdx;
uint8_t mrcmdx1;
uint8_t mrrl;
uint8_t mrrh;
uint8_t link_up;
};
/********************************************************************
* Functions
*/
/**
* @name ch390_receive_packet
* @brief Receive packet
* @param buff - Size equal to CH390_PKT_MAX
* @param rx_status - Output abnormal status while receiving packet.
* It has the same format as RSR(06h).
* @return Packet length
*/
uint32_t ch390_receive_packet(uint8_t *buff, uint8_t *rx_status);
/**
* @name ch390_send_packet
* @brief Send packet
* @param buff - Data to be sent
* @param length - Less than 3k bytes.
*/
int ch390_send_packet(uint8_t *buff, uint16_t length);
/**
* @name ch390_send_request
* @brief Issue transmit request
*/
void ch390_send_request(void);
/**
* @name ch390_drop_packet
* @brief Drop packet in RX SRAM if don't want to read it. This function
* modify the memory data read pointer and skip specified length
* @param len - Skip length, length of the current packet.
*/
void ch390_drop_packet(uint16_t len);
/**
* @name ch390_read_phy
* @brief Read PHY register
* @param reg - PHY register address
*/
uint16_t ch390_read_phy(uint8_t reg);
/**
* @name ch390_write_phy
* @brief Write PHY register
* @param reg - PHY register address
* @param value - Value to be written
*/
void ch390_write_phy(uint8_t reg, uint16_t value);
/**
* @name ch390_write_eeprom
* @brief Write EEPROM register
* @param reg - EEPROM register address
* @param value - Value to be written
*/
void ch390_write_eeprom(uint8_t reg, uint16_t value);
/**
* @name ch390_software_reset
* @brief Software reset CH390 by NCR
*/
void ch390_software_reset(void);
/**
* @name ch390_default_config
* @brief Config CH390 with default options:
* LED mode 1;
* Enable transmit check sum generation;
* Enable RX;
* Enable all interrupt and PAR
*/
void ch390_default_config(void);
/**
* @name ch390_set_phy_mode
* @brief Set PHY mode and enable PHY.
* PHY mode: Auto-negotiation, 10M/100M, full-duplex/half-duplex
* @param mode - PHY mode
*/
void ch390_set_phy_mode(enum ch390_phy_mode mode);
/**
* @name ch390_set_mac_address
* @brief Set mac address
* @param mac_addr - 6-byte length mac address array
*/
void ch390_set_mac_address(uint8_t *mac_addr);
/**
* @name ch390_set_multicast
* @brief Set multicast address hash table
* @param multicast_addr - 8-byte length multicast address hash table array
*/
void ch390_set_multicast(uint8_t *multicast_hash);
/**
* @brief Set MAR bit for a particular MAC address
* @param mac - Destination address
*/
void ch390_set_hash_bit(uint8_t *mac);
/**
* @name ch390_get_mac
* @brief Get mac address
* @param mac_addr - 6 bytes mac address output
*/
void ch390_get_mac(uint8_t *mac_addr);
/**
* @name ch390_get_multicast
* @brief Get multicast address hash table
* @param multicast_addr - 8-byte length multicast address hash table output
*/
void ch390_get_multicast(uint8_t *multicast_addr);
/**
* @name ch390_get_vendor_id
* @brief Get vendor ID
* @return Vendor ID
*/
uint16_t ch390_get_vendor_id(void);
/**
* @name ch390_get_product_id
* @brief Get product ID
* @return Product ID
*/
uint16_t ch390_get_product_id(void);
/**
* @name ch390_get_revision
* @brief Get chip revision
* @return Chip revision
*/
uint8_t ch390_get_revision(void);
/**
* @name ch390_interrupt_config
* @brief Interrupt configuration
* @param mask - Interrupt to be enabled, see "CH390.h" IMR_xxx
*/
void ch390_interrupt_config(uint8_t mask);
/**
* @name ch390_rx_enable
* @brief Enable or disable packet receive
* @param op - 0: disable 1: enable
*/
void ch390_rx_enable(int op);
/**
* @name ch390_rx_filter_config
* @brief Configure receive filter.
* @param config - See "CH390.h" RCR_xxx
*/
void ch390_rx_filter_config(uint8_t config);
/**
* @name ch390_wakeup_config
* @brief Enable or disable wakeup_function
* @param events - Events that trigger wakeup,
* WCR_LINKEN - Link status change
* WCR_SAMPLEEN - Sample frame
* WCR_MAGICEN - Magic packet
* 0 - Disable wakeup function
*/
void ch390_wakeup_config(uint8_t events);
/**
* @name ch390_wake_notify
* @brief Wait for Magic Packet or Sample Frame and discard all
* other packets.
* If the application needs to use Wake On LAN, call this
* function before MCU enters low power mode. An external
* interrupt signal is accessible on WOL pin when wake
* up event occurred.
*/
void ch390_wake_notify(void);
/**
* @name ch390_loop_back_enable
* @brief Enable loop back mode
* @param op - 0: disable 1: enable
*/
void ch390_loop_back_enable(int op);
/**
* @name ch390_get_duplex_mode
* @brief Get current duplex mode of the internal PHY
* @return 0: Half-duplex 1: Full-duplex
*/
int ch390_get_duplex_mode(void);
/**
* @name ch390_get_phy_speed
* @brief Get the speed of the internal PHY.
* Only valid after PHY linked
* @return 0: 100Mbps 1: 10Mbps
*/
int ch390_get_phy_speed(void);
/**
* @name ch390_get_link_status
* @brief Get link status of the internal PHY
* @return 0: Link failed 1: Link OK
*/
int ch390_get_link_status(void);
/**
* @name ch390_sleep_control
* @brief Enter or exit sleep mode
* @param op - 0: Power up 1: Power down
*/
void ch390_sleep_control(int op);
#ifndef CH390_INTERFACE_16_BIT
/**
* @name ch390_gpio_config
* @brief Config the input/output direction of GPIO1~3
* Only GPIO1~3 can be defined as input, GPIO4~6 are output only
* @param GPIOx - CH390_GPIO1 ~ CH390_GPIO3
* dir - 0: Input 1: Output
*/
void ch390_gpio_config(uint8_t GPIOx, uint8_t dir);
/**
* @name ch390_gpio_write_bit
* @brief Sets or clears the selected gpio bit.
* @param GPIOx - CH390_GPIO1 ~ CH390_GPIO6
* level - 0: Clear pin 1: Set pin
*/
void ch390_gpio_write_bit(uint8_t GPIOx, uint8_t level);
/**
* @name ch390_gpio_read_bit
* @brief Read gpio input, only CH390_GPIO1 ~ 3 are available
* @param GPIOx - CH390_GPIO1 ~ CH390_GPIO3
* @return Input pin value
*/
uint8_t ch390_gpio_read_bit(uint8_t GPIOx);
#endif
/**
* @name ch390_int_pin_config
* @brief Configure INT pin output type and polarity
* @param type - INCR_TYPE_OD: Open drain output
* INCR_TYPE_PP: Push pull output
* pol - INCR_POL_L: Active low
* INCR_POL_H: Active high
*/
void ch390_int_pin_config(uint8_t type, uint8_t pol);
/**
* @name ch390_get_int_status
* @brief Get CH390 interrupt status and clear them
* @return Interrupt status
*/
uint8_t ch390_get_int_status(void);
/**
* @name ch390_runtime_poll
* @brief Poll runtime state, sample diagnostic registers, and clear ISR flags.
* @param status - Output runtime status snapshot
* @return Interrupt status snapshot
*/
uint8_t ch390_runtime_poll(struct ch390_runtime_status *status);
/**
* @name ch390_runtime_link_up_from_status
* @brief Get link state from a runtime status snapshot
* @param status - Runtime status snapshot
* @return 0: Link down 1: Link up
*/
int ch390_runtime_link_up_from_status(const struct ch390_runtime_status *status);
/**
* @name ch390_probe_rx_header
* @brief Diagnostic helper: read 4-byte RX header directly from RX SRAM.
* Caller must restore MRR if a non-destructive probe is required.
* @param head - Output buffer with at least 4 bytes.
*/
void ch390_probe_rx_header(uint8_t *head);
/**
* @name ch390_peek_packet
* @brief Peek current RX header without consuming the packet.
* @param rx_status - Output abnormal status while receiving packet
* @param rx_len - Output packet length from RX header
* @return 0: no packet pending 1: header sampled
*/
int ch390_peek_packet(uint8_t *rx_status, uint16_t *rx_len);
/**
* @name ch390_rx_reset
* @brief Repair RX datapath after overflow/corruption without full chip reset.
*/
void ch390_rx_reset(void);
/**
* @name ch390_runtime_receive_packet
* @brief Runtime RX entry point for packet receive
* @param buff - Size equal to CH390_PKT_MAX
* @param rx_status - Output abnormal status while receiving packet
* @return Packet length
*/
uint32_t ch390_runtime_receive_packet(uint8_t *buff, uint8_t *rx_status);
/**
* @name ch390_runtime_send_packet
* @brief Runtime TX entry point for packet transmit
* @param buff - Data to be sent
* @param length - Less than 3k bytes.
*/
int ch390_runtime_send_packet(uint8_t *buff, uint16_t length);
#endif /* __CH390_H */
+432
View File
@@ -0,0 +1,432 @@
/********************************** (C) COPYRIGHT *****************************
* File Name : CH390_Interface.c
* Author : WCH (Modified for STM32 HAL)
* Version : V1.1
* Date : 2024/08/20
* Description : CH390 interface for STM32 HAL Library (SPI mode)
******************************************************************************
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
* Attention: This software (modified or not) and binary are used for
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*
* Modified for STM32F103 HAL Library with FreeRTOS support.
******************************************************************************/
#include "stm32f1xx_hal.h"
#include "main.h"
#include "CH390.h"
#include "CH390_Interface.h"
/* FreeRTOS includes */
#ifdef USE_FREERTOS
#include "FreeRTOS.h"
#include "task.h"
#endif
/*
* This file defines CH390 operation interface using STM32 HAL Library.
* Only SPI mode is implemented for this project.
*
* Hardware connections:
* - PA4: SPI1_NSS (directly controlled as GPIO for CS)
* - PA5: SPI1_SCK
* - PA6: SPI1_MISO
* - PA7: SPI1_MOSI
* - PB0: CH390 INT (EXTI0)
* - PB1: CH390 RST
*/
#ifdef CH390_INTERFACE_SPI
/* GPIO Pin Definitions - matching CubeMX configuration */
#define CH390_CS_PORT GPIOA
#define CH390_CS_PIN GPIO_PIN_4
#define CH390_RST_PORT GPIOB
#define CH390_RST_PIN GPIO_PIN_1
#define CH390_INT_PORT GPIOB
#define CH390_INT_PIN GPIO_PIN_0
/* External SPI handle from spi.c */
extern SPI_HandleTypeDef hspi1;
/* Timeout for SPI operations (ms) */
#define SPI_TIMEOUT 100
#define CH390_SPI_CHUNK_SIZE 64u
#ifdef USE_FREERTOS
#define CH390_SPI_ATOMIC_ENTER() taskENTER_CRITICAL()
#define CH390_SPI_ATOMIC_EXIT() taskEXIT_CRITICAL()
#else
#define CH390_SPI_ATOMIC_ENTER() ((void)0)
#define CH390_SPI_ATOMIC_EXIT() ((void)0)
#endif
/*----------------------------------------------------------------------------
* Low-level GPIO operations
*---------------------------------------------------------------------------*/
/**
* @brief Set CS pin state
* @param state 0=low (select), 1=high (deselect)
*/
static inline void ch390_cs(uint8_t state)
{
HAL_GPIO_WritePin(CH390_CS_PORT, CH390_CS_PIN,
state ? GPIO_PIN_SET : GPIO_PIN_RESET);
ch390_delay_us(2);
}
/**
* @brief Set RST pin state
* @param state 0=low (reset), 1=high (normal)
*/
static inline void ch390_rst(uint8_t state)
{
HAL_GPIO_WritePin(CH390_RST_PORT, CH390_RST_PIN,
state ? GPIO_PIN_SET : GPIO_PIN_RESET);
}
/*----------------------------------------------------------------------------
* SPI Communication
*---------------------------------------------------------------------------*/
/**
* @brief Exchange one byte over SPI
* @param byte Byte to send
* @return Received byte
*/
static uint8_t ch390_spi_exchange_byte(uint8_t byte)
{
uint8_t rx_data = 0;
if (HAL_SPI_TransmitReceive(&hspi1, &byte, &rx_data, 1, SPI_TIMEOUT) != HAL_OK)
{
return 0;
}
return rx_data;
}
static int ch390_spi_read_bytes(uint8_t *data, uint16_t length)
{
static const uint8_t dummy_tx[CH390_SPI_CHUNK_SIZE] = {0};
while (length > 0u)
{
uint16_t chunk = (length > CH390_SPI_CHUNK_SIZE) ? CH390_SPI_CHUNK_SIZE : length;
if (HAL_SPI_TransmitReceive(&hspi1, (uint8_t *)dummy_tx, data, chunk, SPI_TIMEOUT) != HAL_OK)
{
return -1;
}
data += chunk;
length = (uint16_t)(length - chunk);
}
return 0;
}
static void ch390_spi_apply_mode(uint32_t polarity, uint32_t phase)
{
hspi1.Init.CLKPolarity = polarity;
hspi1.Init.CLKPhase = phase;
hspi1.Init.NSS = SPI_NSS_SOFT;
if (HAL_SPI_Init(&hspi1) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief Read a dummy byte (send 0x00)
* @return Received byte
*/
#define ch390_spi_dummy_read() ch390_spi_exchange_byte(0x00)
/*----------------------------------------------------------------------------
* Public Interface Functions
*---------------------------------------------------------------------------*/
/**
* @brief Initialize CH390 GPIO pins
* @note CS and RST pins are configured here. SPI pins are handled by CubeMX.
* INT pin (PB0) is configured as EXTI in CubeMX.
*/
void ch390_gpio_init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* Enable GPIO clocks */
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/* Configure CS pin (PA4) as GPIO output - we control it manually */
/* Note: CubeMX may configure PA4 as SPI1_NSS, we need to reconfigure it */
HAL_GPIO_WritePin(CH390_CS_PORT, CH390_CS_PIN, GPIO_PIN_SET); /* Deselect */
GPIO_InitStruct.Pin = CH390_CS_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(CH390_CS_PORT, &GPIO_InitStruct);
/* Configure RST pin (PB1) as output */
HAL_GPIO_WritePin(CH390_RST_PORT, CH390_RST_PIN, GPIO_PIN_SET); /* Not reset */
GPIO_InitStruct.Pin = CH390_RST_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(CH390_RST_PORT, &GPIO_InitStruct);
/* INT pin (PB0) is configured as EXTI input by CubeMX */
}
/**
* @brief Initialize CH390 interrupt (EXTI0 on PB0)
* @note EXTI and NVIC are configured in CubeMX. This function can enable/disable.
*/
void ch390_interrupt_init(void)
{
/* EXTI0 is configured in CubeMX for PB0 */
/* NVIC priority should be >= configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY */
/* for FreeRTOS compatibility */
HAL_NVIC_DisableIRQ(EXTI0_IRQn);
__HAL_GPIO_EXTI_CLEAR_IT(CH390_INT_PIN);
HAL_NVIC_SetPriority(EXTI0_IRQn, 6, 0);
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
}
/**
* @brief Initialize SPI for CH390
* @note SPI1 is already initialized by CubeMX. This function reconfigures
* for CH390 requirements (Mode 3: CPOL=High, CPHA=2Edge).
*/
void ch390_spi_init(void)
{
/* Reference CH390 SPI path uses mode 3. */
ch390_spi_apply_mode(SPI_POLARITY_HIGH, SPI_PHASE_2EDGE);
}
/**
* @brief Get CH390 interrupt pin state
* @return Non-zero if INT pin is high (active low interrupt)
*/
uint16_t ch390_get_int_pin(void)
{
return HAL_GPIO_ReadPin(CH390_INT_PORT, CH390_INT_PIN);
}
/**
* @brief Delay in microseconds
* @param time Delay time in microseconds
* @note Uses DWT cycle counter for accurate timing if available,
* otherwise falls back to simple loop delay.
*/
void ch390_delay_us(uint32_t time)
{
#ifdef USE_FREERTOS
/* For FreeRTOS, if delay is long enough, use vTaskDelay */
if (time >= 1000)
{
/* Convert to milliseconds and use FreeRTOS delay if in task context */
if (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING)
{
vTaskDelay(pdMS_TO_TICKS(time / 1000));
time = time % 1000;
}
}
#endif
/* Short delay using DWT or simple loop */
if (time > 0)
{
/* Simple delay loop - approximately calibrated for 72MHz */
/* Each iteration is roughly 1/9 us at 72MHz */
volatile uint32_t count = time * 9;
while (count--)
{
__NOP();
}
}
}
/**
* @brief Hardware reset CH390 by pulling RST pin low
*/
void ch390_hardware_reset(void)
{
ch390_delay_us(10000); /* Short delay before reset */
ch390_rst(0); /* Assert reset (low) */
ch390_delay_us(3000); /* Hold reset for 3ms to satisfy datasheet minimum */
ch390_rst(1); /* Release reset (high) */
ch390_delay_us(50000); /* Wait 50ms for CH390 to initialize reliably */
}
/*----------------------------------------------------------------------------
* CH390 Register/Memory Access Functions (SPI Mode)
*---------------------------------------------------------------------------*/
/**
* @brief Read a CH390 register
* @param reg Register address
* @return Register value
*/
uint8_t ch390_read_reg(uint8_t reg)
{
uint8_t value;
CH390_SPI_ATOMIC_ENTER();
ch390_cs(0); /* CS low - select */
ch390_spi_exchange_byte(reg | OPC_REG_R); /* Send read command */
value = ch390_spi_dummy_read(); /* Read register value */
ch390_cs(1); /* CS high - deselect */
CH390_SPI_ATOMIC_EXIT();
return value;
}
static uint8_t ch390_read_rx_reg(uint8_t reg)
{
uint8_t value;
CH390_SPI_ATOMIC_ENTER();
ch390_cs(0);
ch390_spi_exchange_byte(reg | OPC_REG_R);
value = ch390_spi_dummy_read();
ch390_cs(1);
CH390_SPI_ATOMIC_EXIT();
return value;
}
uint8_t ch390_read_mrcmdx(void)
{
return ch390_read_rx_reg(CH390_MRCMDX);
}
uint8_t ch390_read_mrcmdx1(void)
{
return ch390_read_rx_reg(CH390_MRCMDX1);
}
uint8_t ch390_read_mrrl(void)
{
return ch390_read_rx_reg(CH390_MRRL);
}
uint8_t ch390_read_mrrh(void)
{
return ch390_read_rx_reg(CH390_MRRH);
}
/**
* @brief Write a CH390 register
* @param reg Register address
* @param value Value to write
*/
void ch390_write_reg(uint8_t reg, uint8_t value)
{
CH390_SPI_ATOMIC_ENTER();
ch390_cs(0); /* CS low - select */
ch390_spi_exchange_byte(reg | OPC_REG_W); /* Send write command */
ch390_spi_exchange_byte(value); /* Write register value */
ch390_cs(1); /* CS high - deselect */
CH390_SPI_ATOMIC_EXIT();
}
/**
* @brief Read data from CH390 RX SRAM
* @param data Buffer to store received data
* @param length Number of bytes to read
*/
void ch390_read_mem(uint8_t *data, int length)
{
if ((data == NULL) || (length <= 0))
{
return;
}
CH390_SPI_ATOMIC_ENTER();
ch390_cs(0); /* CS low - select */
ch390_spi_exchange_byte(OPC_MEM_READ); /* Send memory read command */
(void)ch390_spi_read_bytes(data, (uint16_t)length);
ch390_cs(1); /* CS high - deselect */
CH390_SPI_ATOMIC_EXIT();
}
/**
* @brief Read data from CH390 RX SRAM using DMA (for larger transfers)
* @param data Buffer to store received data
* @param length Number of bytes to read
* @note Falls back to polling mode for small transfers
*/
void ch390_read_mem_dma(uint8_t *data, int length)
{
/* For small transfers, use polling mode */
if (length < 64)
{
ch390_read_mem(data, length);
return;
}
/* For larger transfers, could use DMA - currently using polling */
/* TODO: Implement DMA transfer if needed for performance */
ch390_read_mem(data, length);
}
/**
* @brief Write data to CH390 TX SRAM
* @param data Data buffer to send
* @param length Number of bytes to write
*/
void ch390_write_mem(uint8_t *data, int length)
{
int i;
if ((data == NULL) || (length <= 0))
{
return;
}
CH390_SPI_ATOMIC_ENTER();
ch390_cs(0); /* CS low - select */
ch390_spi_exchange_byte(OPC_MEM_WRITE); /* Send memory write command */
for (i = 0; i < length; ++i)
{
(void)ch390_spi_exchange_byte(data[i]);
}
ch390_cs(1); /* CS high - deselect */
CH390_SPI_ATOMIC_EXIT();
}
/**
* @brief Write data to CH390 TX SRAM using DMA (for larger transfers)
* @param data Data buffer to send
* @param length Number of bytes to write
* @note Falls back to polling mode for small transfers
*/
void ch390_write_mem_dma(uint8_t *data, int length)
{
/* For small transfers, use polling mode */
if (length < 64)
{
ch390_write_mem(data, length);
return;
}
/* For larger transfers, could use DMA - currently using polling */
/* TODO: Implement DMA transfer if needed for performance */
ch390_write_mem(data, length);
}
#else /* CH390_INTERFACE_SPI */
/*
* Non-SPI modes (8-bit/16-bit parallel) are not implemented for this project.
* This project uses SPI interface only.
*/
#error "This project only supports CH390 SPI interface. Please define CH390_INTERFACE_SPI in CH390.h"
#endif /* CH390_INTERFACE_SPI */
+83
View File
@@ -0,0 +1,83 @@
/********************************** (C) COPYRIGHT *******************************
* File Name : CH390.h
* Author : WCH
* Version : V1.0
* Date : 2024/08/20
* Description : CH390 interface header file
*******************************************************************************/
#ifndef __CH390_INTERFACE_H
#define __CH390_INTERFACE_H
#include <stdint.h>
#include "CH390.h"
void ch390_gpio_init(void);
void ch390_interrupt_init(void);
void ch390_spi_init(void);
uint16_t ch390_get_int_pin(void);
void ch390_delay_us(uint32_t time);
void ch390_hardware_reset(void);
/**
* @name ch390_read_reg
* @brief Read register
* @param reg - Target register address
* @return Register value
*/
uint8_t ch390_read_reg(uint8_t reg);
/**
* @name ch390_read_mrcmdx
* @brief Read MRCMDX receive-ready latch
* @return Register value
*/
uint8_t ch390_read_mrcmdx(void);
/**
* @name ch390_read_mrcmdx1
* @brief Read MRCMDX1 receive-ready latch
* @return Register value
*/
uint8_t ch390_read_mrcmdx1(void);
/**
* @name ch390_read_mrrl
* @brief Read MRRL receive memory pointer register
* @return Register value
*/
uint8_t ch390_read_mrrl(void);
/**
* @name ch390_read_mrrh
* @brief Read MRRH receive memory pointer register
* @return Register value
*/
uint8_t ch390_read_mrrh(void);
/**
* @name ch390_write_reg
* @brief Write register
* @param reg - Target register address
* @param value - Value to be written
*/
void ch390_write_reg(uint8_t reg, uint8_t value);
/**
* @name ch390_read_mem
* @brief Read data from RX SRAM
* @param data - Data buffer
* @param length - Length to read
*/
void ch390_read_mem(uint8_t *data, int length);
/**
* @name ch390_write_mem
* @brief Write data to TX SRAM
* @param data - Data buffer
* @param length - Length to write
*/
void ch390_write_mem(uint8_t *data, int length);
#endif /* __CH390_INTERFACE_H */
+380
View File
@@ -0,0 +1,380 @@
/**
* @file sys_arch.c
* @brief LwIP system architecture implementation for FreeRTOS
*/
#include "lwip/opt.h"
#include "lwip/mem.h"
#include "lwip/sys.h"
#include "lwip/stats.h"
#include "arch/sys_arch.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include <string.h>
int errno;
/* Timeout for infinite wait */
#define LWIP_ARCH_TICK_PER_MS (1000 / configTICK_RATE_HZ)
/*---------------------------------------------------------------------------
* System Initialization
*---------------------------------------------------------------------------*/
/**
* @brief Initialize the system architecture layer
*/
void sys_init(void)
{
/* Nothing to do here for FreeRTOS */
}
/**
* @brief Get current time in milliseconds
* @return Current time in milliseconds
*/
u32_t sys_now(void)
{
return xTaskGetTickCount() * portTICK_PERIOD_MS;
}
/*---------------------------------------------------------------------------
* Semaphore Functions
*---------------------------------------------------------------------------*/
/**
* @brief Create a new semaphore
* @param sem Pointer to the semaphore to create
* @param count Initial count of the semaphore
* @return ERR_OK if successful, ERR_MEM if out of memory
*/
err_t sys_sem_new(sys_sem_t *sem, u8_t count)
{
*sem = xSemaphoreCreateCounting(0xFF, count);
if (*sem == NULL)
{
SYS_STATS_INC(sem.err);
return ERR_MEM;
}
SYS_STATS_INC_USED(sem);
return ERR_OK;
}
/**
* @brief Free a semaphore
* @param sem Pointer to the semaphore to free
*/
void sys_sem_free(sys_sem_t *sem)
{
if (*sem != SYS_SEM_NULL)
{
vSemaphoreDelete(*sem);
*sem = SYS_SEM_NULL;
SYS_STATS_DEC(sem.used);
}
}
/**
* @brief Signal a semaphore
* @param sem Pointer to the semaphore to signal
*/
void sys_sem_signal(sys_sem_t *sem)
{
xSemaphoreGive(*sem);
}
/**
* @brief Wait for a semaphore
* @param sem Pointer to the semaphore to wait on
* @param timeout Timeout in milliseconds (0 = wait forever)
* @return Time waited in milliseconds, or SYS_ARCH_TIMEOUT on timeout
*/
u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout)
{
TickType_t start_time = xTaskGetTickCount();
TickType_t wait_ticks;
if (timeout == 0)
{
wait_ticks = portMAX_DELAY;
}
else
{
wait_ticks = pdMS_TO_TICKS(timeout);
}
if (xSemaphoreTake(*sem, wait_ticks) == pdTRUE)
{
u32_t elapsed = (xTaskGetTickCount() - start_time) * portTICK_PERIOD_MS;
return elapsed;
}
return SYS_ARCH_TIMEOUT;
}
/*---------------------------------------------------------------------------
* Mutex Functions
*---------------------------------------------------------------------------*/
/**
* @brief Create a new mutex
* @param mutex Pointer to the mutex to create
* @return ERR_OK if successful, ERR_MEM if out of memory
*/
err_t sys_mutex_new(sys_mutex_t *mutex)
{
*mutex = xSemaphoreCreateRecursiveMutex();
if (*mutex == NULL)
{
SYS_STATS_INC(mutex.err);
return ERR_MEM;
}
SYS_STATS_INC_USED(mutex);
return ERR_OK;
}
/**
* @brief Free a mutex
* @param mutex Pointer to the mutex to free
*/
void sys_mutex_free(sys_mutex_t *mutex)
{
if (*mutex != SYS_MUTEX_NULL)
{
vSemaphoreDelete(*mutex);
*mutex = SYS_MUTEX_NULL;
SYS_STATS_DEC(mutex.used);
}
}
/**
* @brief Lock a mutex
* @param mutex Pointer to the mutex to lock
*/
void sys_mutex_lock(sys_mutex_t *mutex)
{
xSemaphoreTakeRecursive(*mutex, portMAX_DELAY);
}
/**
* @brief Unlock a mutex
* @param mutex Pointer to the mutex to unlock
*/
void sys_mutex_unlock(sys_mutex_t *mutex)
{
xSemaphoreGiveRecursive(*mutex);
}
/*---------------------------------------------------------------------------
* Mailbox Functions
*---------------------------------------------------------------------------*/
/**
* @brief Create a new mailbox
* @param mbox Pointer to the mailbox to create
* @param size Size of the mailbox
* @return ERR_OK if successful, ERR_MEM if out of memory
*/
err_t sys_mbox_new(sys_mbox_t *mbox, int size)
{
*mbox = xQueueCreate(size, sizeof(void *));
if (*mbox == NULL)
{
SYS_STATS_INC(mbox.err);
return ERR_MEM;
}
SYS_STATS_INC_USED(mbox);
return ERR_OK;
}
/**
* @brief Free a mailbox
* @param mbox Pointer to the mailbox to free
*/
void sys_mbox_free(sys_mbox_t *mbox)
{
if (*mbox != SYS_MBOX_NULL)
{
/* Wait for mailbox to be empty */
while (uxQueueMessagesWaiting(*mbox) != 0)
{
vTaskDelay(1);
}
vQueueDelete(*mbox);
*mbox = SYS_MBOX_NULL;
SYS_STATS_DEC(mbox.used);
}
}
/**
* @brief Post a message to a mailbox
* @param mbox Pointer to the mailbox
* @param msg Message to post
*/
void sys_mbox_post(sys_mbox_t *mbox, void *msg)
{
while (xQueueSendToBack(*mbox, &msg, portMAX_DELAY) != pdTRUE);
}
/**
* @brief Try to post a message to a mailbox
* @param mbox Pointer to the mailbox
* @param msg Message to post
* @return ERR_OK if successful, ERR_MEM if mailbox is full
*/
err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
{
if (xQueueSendToBack(*mbox, &msg, 0) == pdTRUE)
{
return ERR_OK;
}
SYS_STATS_INC(mbox.err);
return ERR_MEM;
}
/**
* @brief Try to post a message to front of mailbox
* @param mbox Pointer to the mailbox
* @param msg Message to post
* @return ERR_OK if successful, ERR_MEM if mailbox is full
*/
err_t sys_mbox_trypost_fromisr(sys_mbox_t *mbox, void *msg)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if (xQueueSendToBackFromISR(*mbox, &msg, &xHigherPriorityTaskWoken) == pdTRUE)
{
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
return ERR_OK;
}
return ERR_MEM;
}
/**
* @brief Fetch a message from a mailbox
* @param mbox Pointer to the mailbox
* @param msg Pointer to store the received message
* @param timeout Timeout in milliseconds (0 = wait forever)
* @return Time waited in milliseconds, or SYS_ARCH_TIMEOUT on timeout
*/
u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout)
{
TickType_t start_time = xTaskGetTickCount();
TickType_t wait_ticks;
void *received_msg;
if (timeout == 0)
{
wait_ticks = portMAX_DELAY;
}
else
{
wait_ticks = pdMS_TO_TICKS(timeout);
}
if (xQueueReceive(*mbox, &received_msg, wait_ticks) == pdTRUE)
{
if (msg != NULL)
{
*msg = received_msg;
}
u32_t elapsed = (xTaskGetTickCount() - start_time) * portTICK_PERIOD_MS;
return elapsed;
}
if (msg != NULL)
{
*msg = NULL;
}
return SYS_ARCH_TIMEOUT;
}
/**
* @brief Try to fetch a message from a mailbox
* @param mbox Pointer to the mailbox
* @param msg Pointer to store the received message
* @return 0 if successful, SYS_MBOX_EMPTY if mailbox is empty
*/
u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg)
{
void *received_msg;
if (xQueueReceive(*mbox, &received_msg, 0) == pdTRUE)
{
if (msg != NULL)
{
*msg = received_msg;
}
return 0;
}
return SYS_MBOX_EMPTY;
}
/*---------------------------------------------------------------------------
* Thread Functions
*---------------------------------------------------------------------------*/
/**
* @brief Create a new thread
* @param name Thread name
* @param thread Thread function
* @param arg Argument to pass to thread function
* @param stacksize Stack size in words
* @param prio Thread priority
* @return Thread handle
*/
sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread,
void *arg, int stacksize, int prio)
{
TaskHandle_t task_handle = NULL;
BaseType_t result = xTaskCreate(
(TaskFunction_t)thread,
name,
stacksize,
arg,
prio,
&task_handle
);
if (result != pdPASS)
{
return NULL;
}
return task_handle;
}
/*---------------------------------------------------------------------------
* Critical Section Protection
*---------------------------------------------------------------------------*/
/**
* @brief Enter critical section
* @return Previous interrupt state
*/
sys_prot_t sys_arch_protect(void)
{
taskENTER_CRITICAL();
return 0;
}
/**
* @brief Leave critical section
* @param pval Previous interrupt state (unused in FreeRTOS)
*/
void sys_arch_unprotect(sys_prot_t pval)
{
(void)pval;
taskEXIT_CRITICAL();
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+115
View File
@@ -0,0 +1,115 @@
/**
* @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 <adam@sics.se>
*
*/
#include "lwip/err.h"
#include "lwip/def.h"
#include "lwip/sys.h"
#include "lwip/errno.h"
#if !NO_SYS
/** 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 */
+102
View File
@@ -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. <joel.cunningham@garmin.com>
* 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 <joel.cunningham@me.com>
*
*/
#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
* Returns 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 */
+250
View File
@@ -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.<br>
* Buffers must not be shared across 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 <adam@sics.se>
*
*/
#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 <string.h>
/**
* @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 */
+422
View File
@@ -0,0 +1,422 @@
/**
* @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 <string.h> /* memset */
#include <stdlib.h> /* 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 */
/** LWIP_DNS_API_HOSTENT_STORAGE: if set to 0 (default), lwip_gethostbyname()
* returns the same global variabe for all calls (in all threads).
* When set to 1, your port should provide a function
* struct hostent* sys_thread_hostent( struct hostent* h);
* which have to do a copy of "h" and return a pointer ont the "per-thread"
* copy.
*/
#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]-> == %s\n", idx, ipaddr_ntoa(s_phostent_addr[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 && (servname[0] != '0')) {
/* atoi failed - service was not numeric */
return EAI_SERVICE;
}
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 */
+380
View File
@@ -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 <string.h> /* 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 */
File diff suppressed because it is too large Load Diff
+706
View File
@@ -0,0 +1,706 @@
/**
* @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 <adam@sics.se>
*
*/
#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;
case TCPIP_MSG_CALLBACK_STATIC_WAIT:
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK WAIT message %p\n", (void *)msg));
msg->msg.cb_wait.function(msg->msg.cb_wait.ctx);
sys_sem_signal(msg->msg.cb_wait.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_MBOX_EMPTY) {
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);
}
/**
* Sends a message to TCPIP thread to call a function. Caller thread blocks
* until the function returns.
* 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 function the function to call
* @param ctx parameter passed to f
* @return ERR_OK if the function was called, another err_t if not
*/
err_t
tcpip_callback_wait(tcpip_callback_fn function, void *ctx)
{
#if LWIP_TCPIP_CORE_LOCKING
LOCK_TCPIP_CORE();
function(ctx);
UNLOCK_TCPIP_CORE();
return ERR_OK;
#else /* LWIP_TCPIP_CORE_LOCKING */
err_t err;
sys_sem_t sem;
struct tcpip_msg msg;
LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(tcpip_mbox));
err = sys_sem_new(&sem, 0);
if (err != ERR_OK) {
return err;
}
msg.type = TCPIP_MSG_CALLBACK_STATIC_WAIT;
msg.msg.cb_wait.function = function;
msg.msg.cb_wait.ctx = ctx;
msg.msg.cb_wait.sem = &sem;
sys_mbox_post(&tcpip_mbox, &msg);
sys_arch_sem_wait(&sem, 0);
sys_sem_free(&sem);
return ERR_OK;
#endif /* LWIP_TCPIP_CORE_LOCKING */
}
/**
* @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 */
+717
View File
@@ -0,0 +1,717 @@
/**
* @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 <goldsimon@gmx.de>
*
*/
#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 <string.h>
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;
}
#if LWIP_TCP_KEEPALIVE
void
altcp_keepalive_disable(struct altcp_pcb *conn)
{
if (conn && conn->fns && conn->fns->keepalive_disable) {
conn->fns->keepalive_disable(conn);
}
}
void
altcp_keepalive_enable(struct altcp_pcb *conn, u32_t idle, u32_t intvl, u32_t count)
{
if (conn && conn->fns && conn->fns->keepalive_enable) {
conn->fns->keepalive_enable(conn, idle, intvl, count);
}
}
#endif
#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;
}
#if LWIP_TCP_KEEPALIVE
void
altcp_default_keepalive_disable(struct altcp_pcb *conn)
{
if (conn && conn->inner_conn) {
altcp_keepalive_disable(conn->inner_conn);
}
}
void
altcp_default_keepalive_enable(struct altcp_pcb *conn, u32_t idle, u32_t intvl, u32_t count)
{
if (conn && conn->inner_conn) {
altcp_keepalive_enable(conn->inner_conn, idle, intvl, count);
}
}
#endif
#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 */
+87
View File
@@ -0,0 +1,87 @@
/**
* @file
* Application layered TCP connection API (to be used from TCPIP thread)<br>
* 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 <goldsimon@gmx.de>
*
*/
#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 <string.h>
#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 */
+578
View File
@@ -0,0 +1,578 @@
/**
* @file
* Application layered TCP connection API (to be used from TCPIP thread)
*
* 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 <goldsimon@gmx.de>
*
*/
#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/priv/tcp_priv.h"
#include "lwip/mem.h"
#include <string.h>
#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 (new_tpcb && 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);
if (tpcb->state != LISTEN) {
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);
/* this might be called for LISTN when close fails... */
if (tpcb->state != LISTEN) {
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 */
}
}
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);
}
}
#if LWIP_TCP_KEEPALIVE
static void
altcp_tcp_keepalive_disable(struct altcp_pcb *conn)
{
if (conn && conn->state) {
struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
ALTCP_TCP_ASSERT_CONN(conn);
ip_reset_option(pcb, SOF_KEEPALIVE);
}
}
static void
altcp_tcp_keepalive_enable(struct altcp_pcb *conn, u32_t idle, u32_t intvl, u32_t cnt)
{
if (conn && conn->state) {
struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
ALTCP_TCP_ASSERT_CONN(conn);
ip_set_option(pcb, SOF_KEEPALIVE);
pcb->keep_idle = idle ? idle : TCP_KEEPIDLE_DEFAULT;
pcb->keep_intvl = intvl ? intvl : TCP_KEEPINTVL_DEFAULT;
pcb->keep_cnt = cnt ? cnt : TCP_KEEPCNT_DEFAULT;
}
}
#endif
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
#if LWIP_TCP_KEEPALIVE
, altcp_tcp_keepalive_disable
, altcp_tcp_keepalive_enable
#endif
#ifdef LWIP_DEBUG
, altcp_tcp_dbg_get_tcp_state
#endif
};
#endif /* LWIP_ALTCP */
+263
View File
@@ -0,0 +1,263 @@
/**
* @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 <string.h>
#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_strnistr
/**
* @ingroup sys_nonstandard
* lwIP default implementation for strnistr() non-standard function.
* This can be \#defined to strnistr() depending on your platform port.
*/
char *
lwip_strnistr(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 (lwip_strnicmp(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
File diff suppressed because it is too large Load Diff
+608
View File
@@ -0,0 +1,608 @@
/**
* @file
* Internet checksum functions.
*
* 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 <adam@sics.se>
*
*/
#include "lwip/opt.h"
#include "lwip/inet_chksum.h"
#include "lwip/def.h"
#include "lwip/ip_addr.h"
#include <string.h>
#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 endianness 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) */
+387
View File
@@ -0,0 +1,387 @@
/**
* @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 <adam@sics.se>
*/
#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 accommodate 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) || (!LWIP_ACD)) && LWIP_DHCP_DOES_ACD_CHECK)
#error "If you want to use DHCP ACD checking, you have to define LWIP_DHCP=1, LWIP_ARP=1 and LWIP_ACD=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 PPP_SUPPORT && CCP_SUPPORT && !MPPE_SUPPORT
#error "CCP_SUPPORT needs MPPE_SUPPORT 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_NETCONN_FULLDUPLEX && !LWIP_NETCONN_SEM_PER_THREAD
#error "For LWIP_NETCONN_FULLDUPLEX to work, LWIP_NETCONN_SEM_PER_THREAD is required"
#endif
/* 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_MSS >= ((16 * 1024) - 1)
#error "lwip_sanity_check: WARNING: TCP_MSS must be <= 16382 to prevent u16_t underflow in TCP_SNDLOWAT calculation!"
#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 */
}
+167
View File
@@ -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 <adam@sics.se>
*
*/
#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 */
+557
View File
@@ -0,0 +1,557 @@
/**
* @file
*
* ACD IPv4 Address Conflict Detection
*
* This is an IPv4 address conflict detection implementation for the lwIP TCP/IP
* stack. It aims to be conform to RFC5227.
*
* @defgroup acd ACD
* @ingroup ip4
* ACD related functions
* USAGE:
*
* define @ref LWIP_ACD 1 in your lwipopts.h
* Options:
* ACD_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 ....
*
* For fixed IP:
* - call acd_start after selecting an IP address. The caller will be informed
* on conflict status via the callback function.
*
* With AUTOIP:
* - will be called from the autoip module. No extra's needed.
*
* With DHCP:
* - enable LWIP_DHCP_DOES_ACD_CHECK. Then it will be called from the dhcp module.
* No extra's needed.
*/
/*
*
* Copyright (c) 2007 Dominik Spies <kontakt@dspies.de>
* Copyright (c) 2018 Jasper Verschueren <jasper.verschueren@apart-audio.com>
* 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: Jasper Verschueren <jasper.verschueren@apart-audio.com>
* Author: Dominik Spies <kontakt@dspies.de>
*/
#include "lwip/opt.h"
/* don't build if not configured for use in lwipopts.h */
#if LWIP_IPV4 && LWIP_ACD
#include <string.h>
#include "lwip/acd.h"
#include "lwip/prot/acd.h"
#define ACD_FOREACH(acd, acd_list) for ((acd) = acd_list; (acd) != NULL; (acd) = (acd)->next)
#define ACD_TICKS_PER_SECOND (1000 / ACD_TMR_INTERVAL)
/* Define good random function (LWIP_RAND) in lwipopts.h */
#ifdef LWIP_RAND
#define LWIP_ACD_RAND(netif, acd) LWIP_RAND()
#else /* LWIP_RAND */
#ifdef LWIP_AUTOIP_RAND
#include "lwip/autoip.h"
#define LWIP_ACD_RAND(netif, acd) LWIP_AUTOIP_RAND(netif) /* for backwards compatibility */
#else
#define LWIP_ACD_RAND(netif, acd) ((((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))) + \
(acd->sent_num))
#endif /* LWIP_AUTOIP_RAND */
#endif /* LWIP_RAND */
#define ACD_RANDOM_PROBE_WAIT(netif, acd) (LWIP_ACD_RAND(netif, acd) % \
(PROBE_WAIT * ACD_TICKS_PER_SECOND))
#define ACD_RANDOM_PROBE_INTERVAL(netif, acd) ((LWIP_ACD_RAND(netif, acd) % \
((PROBE_MAX - PROBE_MIN) * ACD_TICKS_PER_SECOND)) + \
(PROBE_MIN * ACD_TICKS_PER_SECOND ))
/* Function definitions */
static void acd_restart(struct netif *netif, struct acd *acd);
static void acd_handle_arp_conflict(struct netif *netif, struct acd *acd);
static void acd_put_in_passive_mode(struct netif *netif, struct acd *acd);
/**
* @ingroup acd
* Add ACD client to the client list and initialize callback function
*
* @param netif network interface on which to start the acd
* client
* @param acd acd module to be added to the list
* @param acd_conflict_callback callback to be called when conflict information
* is available
*/
err_t
acd_add(struct netif *netif, struct acd *acd,
acd_conflict_callback_t acd_conflict_callback)
{
struct acd *acd2;
/* Set callback */
LWIP_ASSERT_CORE_LOCKED();
LWIP_ASSERT("acd_conflict_callback != NULL", acd_conflict_callback != NULL);
acd->acd_conflict_callback = acd_conflict_callback;
/* Check if the acd struct is already added */
for (acd2 = netif->acd_list; acd2 != NULL; acd2 = acd2->next) {
if (acd2 == acd) {
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
("acd_add(): acd already added to list\n"));
return ERR_OK;
}
}
/* add acd struct to the list */
acd->next = netif->acd_list;
netif->acd_list = acd;
return ERR_OK;
}
/**
* @ingroup acd
* Remvoe ACD client from the client list
*
* @param netif network interface from which to remove the acd client
* @param acd acd module to be removed from the list
*/
void
acd_remove(struct netif *netif, struct acd *acd)
{
struct acd *acd2, *prev = NULL;
LWIP_ASSERT_CORE_LOCKED();
for (acd2 = netif->acd_list; acd2 != NULL; acd2 = acd2->next) {
if (acd2 == acd) {
if (prev) {
prev->next = acd->next;
} else {
netif->acd_list = acd->next;
}
return;
}
prev = acd2;
}
LWIP_ASSERT(("acd_remove(): acd not on list\n"), 0);
}
/**
* @ingroup acd
* Start ACD client
*
* @param netif network interface on which to start the acd client
* @param acd acd module to start
* @param ipaddr ip address to perform acd on
*/
err_t
acd_start(struct netif *netif, struct acd *acd, ip4_addr_t ipaddr)
{
err_t result = ERR_OK;
LWIP_UNUSED_ARG(netif);
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
("acd_start(netif=%p) %c%c%"U16_F"\n",
(void *)netif, netif->name[0],
netif->name[1], (u16_t)netif->num));
/* init probing state */
acd->sent_num = 0;
acd->lastconflict = 0;
ip4_addr_copy(acd->ipaddr, ipaddr);
acd->state = ACD_STATE_PROBE_WAIT;
acd->ttw = (u16_t)(ACD_RANDOM_PROBE_WAIT(netif, acd));
return result;
}
/**
* @ingroup acd
* Stop ACD client
*
* @param acd acd module to stop
*/
err_t
acd_stop(struct acd *acd)
{
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("acd_stop\n"));
if (acd != NULL) {
acd->state = ACD_STATE_OFF;
}
return ERR_OK;
}
/**
* @ingroup acd
* Inform the ACD modules when the link goes down
*
* @param netif network interface on which to inform the ACD clients
*/
void
acd_network_changed_link_down(struct netif *netif)
{
struct acd *acd;
/* loop over the acd's*/
ACD_FOREACH(acd, netif->acd_list) {
acd_stop(acd);
}
}
/**
* Has to be called in loop every ACD_TMR_INTERVAL milliseconds
*/
void
acd_tmr(void)
{
struct netif *netif;
struct acd *acd;
/* loop through netif's */
NETIF_FOREACH(netif) {
ACD_FOREACH(acd, netif->acd_list) {
if (acd->lastconflict > 0) {
acd->lastconflict--;
}
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE,
("acd_tmr() ACD-State: %"U16_F", ttw=%"U16_F"\n",
(u16_t)(acd->state), acd->ttw));
if (acd->ttw > 0) {
acd->ttw--;
}
switch (acd->state) {
case ACD_STATE_PROBE_WAIT:
case ACD_STATE_PROBING:
if (acd->ttw == 0) {
acd->state = ACD_STATE_PROBING;
etharp_acd_probe(netif, &acd->ipaddr);
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE,
("acd_tmr() PROBING Sent Probe\n"));
acd->sent_num++;
if (acd->sent_num >= PROBE_NUM) {
/* Switch to ANNOUNCE_WAIT: last probe is sent*/
acd->state = ACD_STATE_ANNOUNCE_WAIT;
acd->sent_num = 0;
/* calculate time to wait before announcing */
acd->ttw = (u16_t)(ANNOUNCE_WAIT * ACD_TICKS_PER_SECOND);
} else {
/* calculate time to wait to next probe */
acd->ttw = (u16_t)(ACD_RANDOM_PROBE_INTERVAL(netif, acd));
}
}
break;
case ACD_STATE_ANNOUNCE_WAIT:
case ACD_STATE_ANNOUNCING:
if (acd->ttw == 0) {
if (acd->sent_num == 0) {
acd->state = ACD_STATE_ANNOUNCING;
/* reset conflict count to ensure fast re-probing after announcing */
acd->num_conflicts = 0;
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
("acd_tmr(): changing state to ANNOUNCING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
ip4_addr1_16(&acd->ipaddr), ip4_addr2_16(&acd->ipaddr),
ip4_addr3_16(&acd->ipaddr), ip4_addr4_16(&acd->ipaddr)));
}
etharp_acd_announce(netif, &acd->ipaddr);
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE,
("acd_tmr() ANNOUNCING Sent Announce\n"));
acd->ttw = ANNOUNCE_INTERVAL * ACD_TICKS_PER_SECOND;
acd->sent_num++;
if (acd->sent_num >= ANNOUNCE_NUM) {
acd->state = ACD_STATE_ONGOING;
acd->sent_num = 0;
acd->ttw = 0;
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
("acd_tmr(): changing state to ONGOING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
ip4_addr1_16(&acd->ipaddr), ip4_addr2_16(&acd->ipaddr),
ip4_addr3_16(&acd->ipaddr), ip4_addr4_16(&acd->ipaddr)));
/* finally, let acd user know that the address is good and can be used */
acd->acd_conflict_callback(netif, ACD_IP_OK);
}
}
break;
case ACD_STATE_RATE_LIMIT:
if (acd->ttw == 0) {
/* acd should be stopped because ipaddr isn't valid any more */
acd_stop(acd);
/* let the acd user (after rate limit interval) know that their is
* a conflict detected. So it can restart the address acquiring
* process.*/
acd->acd_conflict_callback(netif, ACD_RESTART_CLIENT);
}
break;
default:
/* nothing to do in other states */
break;
}
}
}
}
/**
* Restarts the acd module
*
* The number of conflicts is increased and the upper layer is informed.
*/
static void
acd_restart(struct netif *netif, struct acd *acd)
{
/* increase conflict counter. */
acd->num_conflicts++;
/* Decline the address */
acd->acd_conflict_callback(netif, ACD_DECLINE);
/* if we tried more then MAX_CONFLICTS we must limit our rate for
* acquiring and probing addresses. compliant to RFC 5227 Section 2.1.1 */
if (acd->num_conflicts >= MAX_CONFLICTS) {
acd->state = ACD_STATE_RATE_LIMIT;
acd->ttw = (u16_t)(RATE_LIMIT_INTERVAL * ACD_TICKS_PER_SECOND);
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
("acd_restart(): rate limiting initiated. too many conflicts\n"));
}
else {
/* acd should be stopped because ipaddr isn't valid any more */
acd_stop(acd);
/* let the acd user know right away that their is a conflict detected.
* So it can restart the address acquiring process. */
acd->acd_conflict_callback(netif, ACD_RESTART_CLIENT);
}
}
/**
* Handles every incoming ARP Packet, called by etharp_input().
*
* @param netif network interface to use for acd processing
* @param hdr Incoming ARP packet
*/
void
acd_arp_reply(struct netif *netif, struct etharp_hdr *hdr)
{
struct acd *acd;
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);
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE, ("acd_arp_reply()\n"));
/* loop over the acd's*/
ACD_FOREACH(acd, netif->acd_list) {
switch(acd->state) {
case ACD_STATE_OFF:
case ACD_STATE_RATE_LIMIT:
default:
/* do nothing */
break;
case ACD_STATE_PROBE_WAIT:
case ACD_STATE_PROBING:
case ACD_STATE_ANNOUNCE_WAIT:
/* RFC 5227 Section 2.1.1:
* from beginning to after ANNOUNCE_WAIT seconds we have a conflict if
* ip.src == ipaddr (someone is already using the address)
* OR
* ip.dst == ipaddr && hw.src != own hwaddr (someone else is probing it)
*/
if ((ip4_addr_eq(&sipaddr, &acd->ipaddr)) ||
(ip4_addr_isany_val(sipaddr) &&
ip4_addr_eq(&dipaddr, &acd->ipaddr) &&
!eth_addr_eq(&netifaddr, &hdr->shwaddr))) {
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
("acd_arp_reply(): Probe Conflict detected\n"));
acd_restart(netif, acd);
}
break;
case ACD_STATE_ANNOUNCING:
case ACD_STATE_ONGOING:
case ACD_STATE_PASSIVE_ONGOING:
/* RFC 5227 Section 2.4:
* in any state we have a conflict if
* ip.src == ipaddr && hw.src != own hwaddr (someone is using our address)
*/
if (ip4_addr_eq(&sipaddr, &acd->ipaddr) &&
!eth_addr_eq(&netifaddr, &hdr->shwaddr)) {
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
("acd_arp_reply(): Conflicting ARP-Packet detected\n"));
acd_handle_arp_conflict(netif, acd);
}
break;
}
}
}
/**
* Handle a IP address conflict after an ARP conflict detection
*/
static void
acd_handle_arp_conflict(struct netif *netif, struct acd *acd)
{
/* RFC5227, 2.4 "Ongoing Address Conflict Detection and Address Defense"
allows three options where:
a) means retreat on the first conflict,
b) allows to keep an already configured address when having only one
conflict in DEFEND_INTERVAL seconds and
c) the host will not give up it's address and defend it indefinitely
We use option b) when the acd module represents the netif address, since it
helps to improve the chance that one of the two conflicting hosts may be
able to retain its address. while we are flexible enough to help network
performance
We use option a) when the acd module does not represent the netif address,
since we cannot have the acd module announcing or restarting. This
situation occurs for the LL acd module when a routable address is used on
the netif but the LL address is still open in the background. */
if (acd->state == ACD_STATE_PASSIVE_ONGOING) {
/* Immediately back off on a conflict. */
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
("acd_handle_arp_conflict(): conflict when we are in passive mode -> back off\n"));
acd_stop(acd);
acd->acd_conflict_callback(netif, ACD_DECLINE);
}
else {
if (acd->lastconflict > 0) {
/* retreat, there was a conflicting ARP in the last DEFEND_INTERVAL seconds */
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
("acd_handle_arp_conflict(): conflict within DEFEND_INTERVAL -> retreating\n"));
/* Active TCP sessions are aborted when removing the ip address but a bad
* connection was inevitable anyway with conflicting hosts */
acd_restart(netif, acd);
} else {
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
("acd_handle_arp_conflict(): we are defending, send ARP Announce\n"));
etharp_acd_announce(netif, &acd->ipaddr);
acd->lastconflict = DEFEND_INTERVAL * ACD_TICKS_PER_SECOND;
}
}
}
/**
* Put the acd module in passive ongoing conflict detection.
*/
static void
acd_put_in_passive_mode(struct netif *netif, struct acd *acd)
{
switch(acd->state) {
case ACD_STATE_OFF:
case ACD_STATE_PASSIVE_ONGOING:
default:
/* do nothing */
break;
case ACD_STATE_PROBE_WAIT:
case ACD_STATE_PROBING:
case ACD_STATE_ANNOUNCE_WAIT:
case ACD_STATE_RATE_LIMIT:
acd_stop(acd);
acd->acd_conflict_callback(netif, ACD_DECLINE);
break;
case ACD_STATE_ANNOUNCING:
case ACD_STATE_ONGOING:
acd->state = ACD_STATE_PASSIVE_ONGOING;
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
("acd_put_in_passive_mode()\n"));
break;
}
}
/**
* @ingroup acd
* Inform the ACD modules of address changes
*
* @param netif network interface on which the address is changing
* @param old_addr old ip address
* @param new_addr new ip address
*/
void
acd_netif_ip_addr_changed(struct netif *netif, const ip_addr_t *old_addr,
const ip_addr_t *new_addr)
{
struct acd *acd;
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
("acd_netif_ip_addr_changed(): Address changed\n"));
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
("acd_netif_ip_addr_changed(): old address = %s\n", ipaddr_ntoa(old_addr)));
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
("acd_netif_ip_addr_changed(): new address = %s\n", ipaddr_ntoa(new_addr)));
/* If we change from ANY to an IP or from an IP to ANY we do nothing */
if (ip_addr_isany(old_addr) || ip_addr_isany(new_addr)) {
return;
}
ACD_FOREACH(acd, netif->acd_list) {
/* Find ACD module of old address */
if(ip4_addr_eq(&acd->ipaddr, ip_2_ip4(old_addr))) {
/* Did we change from a LL address to a routable address? */
if (ip_addr_islinklocal(old_addr) && !ip_addr_islinklocal(new_addr)) {
LWIP_DEBUGF(ACD_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
("acd_netif_ip_addr_changed(): changed from LL to routable address\n"));
/* Put the module in passive conflict detection mode */
acd_put_in_passive_mode(netif, acd);
}
}
}
}
#endif /* LWIP_IPV4 && LWIP_ACD */
+379
View File
@@ -0,0 +1,379 @@
/**
* @file
* AutoIP Automatic LinkLocal IP Configuration
*
* This is a AutoIP implementation for the lwIP TCP/IP stack. It aims to conform
* with RFC 3927. It uses IPv4 address conflict detection to evaluate the chosen
* address. The ACD module aims to be conform to RFC 5227.
* RFC 5227 is extracted out of RFC 3927 so the acd module fits nicely in autoip.
*
* @defgroup autoip AUTOIP
* @ingroup ip4
* AUTOIP related functions
* USAGE:
*
* define @ref LWIP_AUTOIP 1 in your lwipopts.h
*
* 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 <kontakt@dspies.de>
* 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 <kontakt@dspies.de>
*/
#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/acd.h"
#include "lwip/etharp.h"
#include "lwip/prot/autoip.h"
#include <string.h>
/**
* 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 */
/* Function definitions */
static void autoip_restart(struct netif *netif);
static void autoip_create_addr(struct netif *netif, ip4_addr_t *ipaddr);
static err_t autoip_bind(struct netif *netif);
static void autoip_conflict_callback(struct netif *netif,
acd_callback_enum_t state);
/**
* @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);
}
/**
* @ingroup autoip
* Remove a struct autoip previously set to the netif using autoip_set_struct()
*
* @param netif the netif for which to set the struct autoip
*/
void
autoip_remove_struct(struct netif *netif)
{
LWIP_ASSERT_CORE_LOCKED();
LWIP_ASSERT("netif != NULL", netif != NULL);
LWIP_ASSERT("netif has no struct autoip set",
netif_autoip_data(netif) != NULL);
netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_AUTOIP, NULL);
}
/** 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);
}
/**
* 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)));
}
/**
* 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;
autoip->state = AUTOIP_STATE_BOUND;
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
("autoip_bind(netif=%p) %c%c%"U16_F" %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
(void *)netif, netif->name[0], netif->name[1], (u16_t)netif->num,
ip4_addr1_16(&autoip->llipaddr), ip4_addr2_16(&autoip->llipaddr),
ip4_addr3_16(&autoip->llipaddr), ip4_addr4_16(&autoip->llipaddr)));
IP4_ADDR(&sn_mask, 255, 255, 0, 0);
IP4_ADDR(&gw_addr, 0, 0, 0, 0);
netif_set_addr(netif, &autoip->llipaddr, &sn_mask, &gw_addr);
/* interface is used by routing now that an address is set */
return ERR_OK;
}
/**
* Handle conflict information from ACD module
*
* @param netif network interface to handle conflict information on
* @param state acd_callback_enum_t
*/
static void
autoip_conflict_callback(struct netif *netif, acd_callback_enum_t state)
{
struct autoip *autoip = netif_autoip_data(netif);
switch (state) {
case ACD_IP_OK:
autoip_bind(netif);
break;
case ACD_RESTART_CLIENT:
autoip_restart(netif);
break;
case ACD_DECLINE:
/* "delete" conflicting address so a new one will be selected in
* autoip_start() */
ip4_addr_set_any(&autoip->llipaddr);
autoip_stop(netif);
break;
default:
break;
}
}
/**
* @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;);
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\n"));
}
if (autoip->state == AUTOIP_STATE_OFF) {
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
("autoip_start(netif=%p) %c%c%"U16_F"\n", (void *)netif, netif->name[0],
netif->name[1], (u16_t)netif->num));
/* add acd struct to list*/
acd_add(netif, &autoip->acd, autoip_conflict_callback);
/* In accordance to RFC3927 section 2.1:
* Keep using the same link local address as much as possible.
* Only when there is none or when there was a conflict, select a new one.
*/
if (!ip4_addr_islinklocal(&autoip->llipaddr)) {
autoip_create_addr(netif, &(autoip->llipaddr));
}
autoip->state = AUTOIP_STATE_CHECKING;
acd_start(netif, &autoip->acd, autoip->llipaddr);
} else {
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
("autoip_start(): already started on netif=%p %c%c%"U16_F"\n",
(void *)netif, netif->name[0],
netif->name[1], (u16_t)netif->num));
}
return result;
}
/**
* Handle a possible change in the network configuration: link up
*
* If there is an AutoIP address configured and AutoIP is not in cooperation
* with DHCP, start probing for previous address.
*/
void
autoip_network_changed_link_up(struct netif *netif)
{
struct autoip *autoip = netif_autoip_data(netif);
if (autoip && (autoip->state != AUTOIP_STATE_OFF) && !LWIP_DHCP_AUTOIP_COOP) {
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
("autoip_network_changed_link_up(): start acd\n"));
autoip->state = AUTOIP_STATE_CHECKING;
/* Start acd check again for the last used address */
acd_start(netif, &autoip->acd, autoip->llipaddr);
}
}
/**
* Handle a possible change in the network configuration: link down
*
* If there is an AutoIP address configured and AutoIP is in cooperation
* with DHCP, then stop the autoip module. When the link goes up, we do not want
* the autoip module to start again. DHCP will initiate autoip when needed.
*/
void
autoip_network_changed_link_down(struct netif *netif)
{
struct autoip *autoip = netif_autoip_data(netif);
if (autoip && (autoip->state != AUTOIP_STATE_OFF) && LWIP_DHCP_AUTOIP_COOP) {
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
("autoip_network_changed_link_down(): stop autoip\n"));
autoip_stop(netif);
}
}
/**
* @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);
}
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,("autoip_stop()\n"));
}
return ERR_OK;
}
/** check if AutoIP supplied netif->ip_addr
*
* @param netif the netif to check
* @return 1 if AutoIP supplied netif->ip_addr (state BOUND),
* 0 otherwise
*/
u8_t
autoip_supplied_address(struct netif *netif)
{
struct autoip *autoip = netif_autoip_data(netif);
return (autoip != NULL)
&& (ip4_addr_eq(netif_ip4_addr(netif), &(autoip->llipaddr)))
&& (autoip->state == AUTOIP_STATE_BOUND);
}
u8_t
autoip_accept_packet(struct netif *netif, const ip4_addr_t *addr)
{
struct autoip *autoip = netif_autoip_data(netif);
return (autoip != NULL)
&& (ip4_addr_eq(addr, &(autoip->llipaddr)))
&& (autoip->state == AUTOIP_STATE_BOUND);
}
#endif /* LWIP_IPV4 && LWIP_AUTOIP */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+407
View File
@@ -0,0 +1,407 @@
/**
* @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 <adam@sics.se>
*
*/
/* 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 <string.h>
#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 maximum 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\n"));
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", 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\n"));
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", 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\n"));
} 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;
struct icmp_hdr *icmphdr;
ip4_addr_t iphdr_src;
struct netif *netif;
u16_t response_pkt_len;
/* increase number of messages attempted to send */
MIB2_STATS_INC(mib2.icmpoutmsgs);
/* Keep IP header + up to 8 bytes */
response_pkt_len = IP_HLEN + ICMP_DEST_UNREACH_DATASIZE;
if (p->tot_len < response_pkt_len) {
response_pkt_len = p->tot_len;
}
/* ICMP header + part of original packet */
q = pbuf_alloc(PBUF_IP, sizeof(struct icmp_hdr) + response_pkt_len, PBUF_RAM);
if (q == NULL) {
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_send_response: 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_hdr) + response_pkt_len)));
iphdr = (struct ip_hdr *)p->payload;
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_send_response: Sending ICMP type %02X for packet from ", type));
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_hdr *)q->payload;
icmphdr->type = type;
icmphdr->code = code;
icmphdr->data = 0;
/* copy fields from original packet */
pbuf_copy_partial_pbuf(q, p, response_pkt_len, sizeof(struct icmp_hdr));
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 */
+801
View File
@@ -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 <string.h>
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_eq(&(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_eq(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_eq(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_eq(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_eq(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_eq(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_eq(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_eq(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_eq(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_eq(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_eq(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_eq(&(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 */
File diff suppressed because it is too large Load Diff
+323
View File
@@ -0,0 +1,323 @@
/**
* @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 <adam@sics.se>
*
*/
#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_net_eq(&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)) {
if((base == 8) && ((u32_t)(c - '0') >= 8))
break;
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 */
+894
View File
@@ -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 <jani@iv.ro>
* Simon Goldschmidt
* original reassembly code by Adam Dunkels <adam@sics.se>
*
*/
#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 <string.h>
#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_eq(&(iphdrA)->src, &(iphdrB)->src) && \
ip4_addr_eq(&(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 */
+821
View File
@@ -0,0 +1,821 @@
/**
* @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)<br>
* dhcp6_enable_stateless() enables stateless DHCPv6 for a netif (stateful disabled)<br>
* 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 <goldsimon@gmx.de>
*/
#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 <string.h>
#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\n", 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\n"));
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\n"));
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\n"));
}
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[] = {
#if LWIP_DHCP6_PROVIDE_DNS_SERVERS
DHCP6_OPTION_DNS_SERVERS,
DHCP6_OPTION_DOMAIN_LIST
#endif
#if LWIP_DHCP6_GET_NTP_SRV
, DHCP6_OPTION_SNTP_SERVERS
#endif
};
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;
ip_addr_set_zero_ip6(&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 message 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 across 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 */
+123
View File
@@ -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 <delamer@inicotech.com>
*
*
* Please coordinate changes and requests with Ivan Delamer
* <delamer@inicotech.com>
*/
#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 <string.h>
/**
* 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 */
+425
View File
@@ -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 <delamer@inicotech.com>
*
*
* Please coordinate changes and requests with Ivan Delamer
* <delamer@inicotech.com>
*/
#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 <string.h>
#if !LWIP_ICMP6_DATASIZE || (LWIP_ICMP6_DATASIZE > (IP6_MIN_MTU_LENGTH - IP6_HLEN - ICMP6_HLEN))
#undef LWIP_ICMP6_DATASIZE
#define LWIP_ICMP6_DATASIZE (IP6_MIN_MTU_LENGTH - IP6_HLEN - ICMP6_HLEN)
#endif
/* Forward declarations */
static void icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type);
static void icmp6_send_response_with_addrs(struct pbuf *p, u8_t code, u32_t data,
u8_t type, const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr);
static void icmp6_send_response_with_addrs_and_netif(struct pbuf *p, u8_t code, u32_t data,
u8_t type, const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr, struct netif *netif);
/**
* Process an input ICMPv6 message. Called by ip6_input.
*
* Will generate a reply for echo requests. Other messages are forwarded
* to nd6_input, or mld6_input.
*
* @param p the mld packet, p->payload pointing to the icmpv6 header
* @param inp the netif on which this packet was received
*/
void
icmp6_input(struct pbuf *p, struct netif *inp)
{
struct icmp6_hdr *icmp6hdr;
struct pbuf *r;
const ip6_addr_t *reply_src;
ICMP6_STATS_INC(icmp6.recv);
/* Check that ICMPv6 header fits in payload */
if (p->len < sizeof(struct icmp6_hdr)) {
/* drop short packets */
pbuf_free(p);
ICMP6_STATS_INC(icmp6.lenerr);
ICMP6_STATS_INC(icmp6.drop);
return;
}
icmp6hdr = (struct icmp6_hdr *)p->payload;
#if CHECKSUM_CHECK_ICMP6
IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_ICMP6) {
if (ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->tot_len, ip6_current_src_addr(),
ip6_current_dest_addr()) != 0) {
/* Checksum failed */
pbuf_free(p);
ICMP6_STATS_INC(icmp6.chkerr);
ICMP6_STATS_INC(icmp6.drop);
return;
}
}
#endif /* CHECKSUM_CHECK_ICMP6 */
switch (icmp6hdr->type) {
case ICMP6_TYPE_NA: /* Neighbor advertisement */
case ICMP6_TYPE_NS: /* Neighbor solicitation */
case ICMP6_TYPE_RA: /* Router advertisement */
case ICMP6_TYPE_RD: /* Redirect */
case ICMP6_TYPE_PTB: /* Packet too big */
nd6_input(p, inp);
return;
case ICMP6_TYPE_RS:
#if LWIP_IPV6_FORWARD
/* @todo implement router functionality */
#endif
break;
#if LWIP_IPV6_MLD
case ICMP6_TYPE_MLQ:
case ICMP6_TYPE_MLR:
case ICMP6_TYPE_MLD:
mld6_input(p, inp);
return;
#endif
case ICMP6_TYPE_EREQ:
#if !LWIP_MULTICAST_PING
/* multicast destination address? */
if (ip6_addr_ismulticast(ip6_current_dest_addr())) {
/* drop */
pbuf_free(p);
ICMP6_STATS_INC(icmp6.drop);
return;
}
#endif /* LWIP_MULTICAST_PING */
/* Allocate reply. */
r = pbuf_alloc(PBUF_IP, p->tot_len, PBUF_RAM);
if (r == NULL) {
/* drop */
pbuf_free(p);
ICMP6_STATS_INC(icmp6.memerr);
return;
}
/* Copy echo request. */
if (pbuf_copy(r, p) != ERR_OK) {
/* drop */
pbuf_free(p);
pbuf_free(r);
ICMP6_STATS_INC(icmp6.err);
return;
}
/* Determine reply source IPv6 address. */
#if LWIP_MULTICAST_PING
if (ip6_addr_ismulticast(ip6_current_dest_addr())) {
reply_src = ip_2_ip6(ip6_select_source_address(inp, ip6_current_src_addr()));
if (reply_src == NULL) {
/* drop */
pbuf_free(p);
pbuf_free(r);
ICMP6_STATS_INC(icmp6.rterr);
return;
}
}
else
#endif /* LWIP_MULTICAST_PING */
{
reply_src = ip6_current_dest_addr();
}
/* Set fields in reply. */
((struct icmp6_echo_hdr *)(r->payload))->type = ICMP6_TYPE_EREP;
((struct icmp6_echo_hdr *)(r->payload))->chksum = 0;
#if CHECKSUM_GEN_ICMP6
IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_GEN_ICMP6) {
((struct icmp6_echo_hdr *)(r->payload))->chksum = ip6_chksum_pseudo(r,
IP6_NEXTH_ICMP6, r->tot_len, reply_src, ip6_current_src_addr());
}
#endif /* CHECKSUM_GEN_ICMP6 */
/* Send reply. */
ICMP6_STATS_INC(icmp6.xmit);
ip6_output_if(r, reply_src, ip6_current_src_addr(),
LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, inp);
pbuf_free(r);
break;
default:
ICMP6_STATS_INC(icmp6.proterr);
ICMP6_STATS_INC(icmp6.drop);
break;
}
pbuf_free(p);
}
/**
* Send an icmpv6 'destination unreachable' packet.
*
* This function must be used only in direct response to a packet that is being
* received right now. Otherwise, address zones would be lost.
*
* @param p the input packet for which the 'unreachable' should be sent,
* p->payload pointing to the IPv6 header
* @param c ICMPv6 code for the unreachable type
*/
void
icmp6_dest_unreach(struct pbuf *p, enum icmp6_dur_code c)
{
icmp6_send_response(p, c, 0, ICMP6_TYPE_DUR);
}
/**
* Send an icmpv6 'packet too big' packet.
*
* This function must be used only in direct response to a packet that is being
* received right now. Otherwise, address zones would be lost.
*
* @param p the input packet for which the 'packet too big' should be sent,
* p->payload pointing to the IPv6 header
* @param mtu the maximum mtu that we can accept
*/
void
icmp6_packet_too_big(struct pbuf *p, u32_t mtu)
{
icmp6_send_response(p, 0, mtu, ICMP6_TYPE_PTB);
}
/**
* Send an icmpv6 'time exceeded' packet.
*
* This function must be used only in direct response to a packet that is being
* received right now. Otherwise, address zones would be lost.
*
* @param p the input packet for which the 'time exceeded' should be sent,
* p->payload pointing to the IPv6 header
* @param c ICMPv6 code for the time exceeded type
*/
void
icmp6_time_exceeded(struct pbuf *p, enum icmp6_te_code c)
{
icmp6_send_response(p, c, 0, ICMP6_TYPE_TE);
}
/**
* Send an icmpv6 'time exceeded' packet, with explicit source and destination
* addresses.
*
* This function may be used to send a response sometime after receiving the
* packet for which this response is meant. The provided source and destination
* addresses are used primarily to retain their zone information.
*
* @param p the input packet for which the 'time exceeded' should be sent,
* p->payload pointing to the IPv6 header
* @param c ICMPv6 code for the time exceeded type
* @param src_addr source address of the original packet, with zone information
* @param dest_addr destination address of the original packet, with zone
* information
*/
void
icmp6_time_exceeded_with_addrs(struct pbuf *p, enum icmp6_te_code c,
const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr)
{
icmp6_send_response_with_addrs(p, c, 0, ICMP6_TYPE_TE, src_addr, dest_addr);
}
/**
* Send an icmpv6 'parameter problem' packet.
*
* This function must be used only in direct response to a packet that is being
* received right now. Otherwise, address zones would be lost and the calculated
* offset would be wrong (calculated against ip6_current_header()).
*
* @param p the input packet for which the 'param problem' should be sent,
* p->payload pointing to the IP header
* @param c ICMPv6 code for the param problem type
* @param pointer the pointer to the byte where the parameter is found
*/
void
icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, const void *pointer)
{
u32_t pointer_u32 = (u32_t)((const u8_t *)pointer - (const u8_t *)ip6_current_header());
icmp6_send_response(p, c, pointer_u32, ICMP6_TYPE_PP);
}
/**
* Send an ICMPv6 packet in response to an incoming packet.
* The packet is sent *to* ip_current_src_addr() on ip_current_netif().
*
* @param p the input packet for which the response should be sent,
* p->payload pointing to the IPv6 header
* @param code Code of the ICMPv6 header
* @param data Additional 32-bit parameter in the ICMPv6 header
* @param type Type of the ICMPv6 header
*/
static void
icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type)
{
const struct ip6_addr *reply_src, *reply_dest;
struct netif *netif = ip_current_netif();
LWIP_ASSERT("icmpv6 packet not a direct response", netif != NULL);
reply_dest = ip6_current_src_addr();
/* Select an address to use as source. */
reply_src = ip_2_ip6(ip6_select_source_address(netif, reply_dest));
if (reply_src == NULL) {
ICMP6_STATS_INC(icmp6.rterr);
return;
}
icmp6_send_response_with_addrs_and_netif(p, code, data, type, reply_src, reply_dest, netif);
}
/**
* Send an ICMPv6 packet in response to an incoming packet.
*
* Call this function if the packet is NOT sent as a direct response to an
* incoming packet, but rather sometime later (e.g. for a fragment reassembly
* timeout). The caller must provide the zoned source and destination addresses
* from the original packet with the src_addr and dest_addr parameters. The
* reason for this approach is that while the addresses themselves are part of
* the original packet, their zone information is not, thus possibly resulting
* in a link-local response being sent over the wrong link.
*
* @param p the input packet for which the response should be sent,
* p->payload pointing to the IPv6 header
* @param code Code of the ICMPv6 header
* @param data Additional 32-bit parameter in the ICMPv6 header
* @param type Type of the ICMPv6 header
* @param src_addr original source address
* @param dest_addr original destination address
*/
static void
icmp6_send_response_with_addrs(struct pbuf *p, u8_t code, u32_t data, u8_t type,
const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr)
{
const struct ip6_addr *reply_src, *reply_dest;
struct netif *netif;
/* Get the destination address and netif for this ICMP message. */
LWIP_ASSERT("must provide both source and destination", src_addr != NULL);
LWIP_ASSERT("must provide both source and destination", dest_addr != NULL);
/* Special case, as ip6_current_xxx is either NULL, or points
to a different packet than the one that expired. */
IP6_ADDR_ZONECHECK(src_addr);
IP6_ADDR_ZONECHECK(dest_addr);
/* Swap source and destination for the reply. */
reply_dest = src_addr;
reply_src = dest_addr;
netif = ip6_route(reply_src, reply_dest);
if (netif == NULL) {
ICMP6_STATS_INC(icmp6.rterr);
return;
}
icmp6_send_response_with_addrs_and_netif(p, code, data, type, reply_src,
reply_dest, netif);
}
/**
* Send an ICMPv6 packet (with srd/dst address and netif given).
*
* @param p the input packet for which the response should be sent,
* p->payload pointing to the IPv6 header
* @param code Code of the ICMPv6 header
* @param data Additional 32-bit parameter in the ICMPv6 header
* @param type Type of the ICMPv6 header
* @param reply_src source address of the packet to send
* @param reply_dest destination address of the packet to send
* @param netif netif to send the packet
*/
static void
icmp6_send_response_with_addrs_and_netif(struct pbuf *p, u8_t code, u32_t data, u8_t type,
const ip6_addr_t *reply_src, const ip6_addr_t *reply_dest, struct netif *netif)
{
struct pbuf *q;
struct icmp6_hdr *icmp6hdr;
u16_t datalen = LWIP_MIN(p->tot_len, LWIP_ICMP6_DATASIZE);
/* ICMPv6 header + datalen (as much of the offending packet as possible) */
q = pbuf_alloc(PBUF_IP, sizeof(struct icmp6_hdr) + datalen,
PBUF_RAM);
if (q == NULL) {
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMPv6 packet.\n"));
ICMP6_STATS_INC(icmp6.memerr);
return;
}
LWIP_ASSERT("check that first pbuf can hold icmp6 header",
(q->len >= (sizeof(struct icmp6_hdr))));
icmp6hdr = (struct icmp6_hdr *)q->payload;
icmp6hdr->type = type;
icmp6hdr->code = code;
icmp6hdr->data = lwip_htonl(data);
/* copy fields from original packet */
pbuf_copy_partial_pbuf(q, p, datalen, sizeof(struct icmp6_hdr));
/* calculate checksum */
icmp6hdr->chksum = 0;
#if CHECKSUM_GEN_ICMP6
IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) {
icmp6hdr->chksum = ip6_chksum_pseudo(q, IP6_NEXTH_ICMP6, q->tot_len,
reply_src, reply_dest);
}
#endif /* CHECKSUM_GEN_ICMP6 */
ICMP6_STATS_INC(icmp6.xmit);
ip6_output_if(q, reply_src, reply_dest, LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif);
pbuf_free(q);
}
#endif /* LWIP_ICMP6 && LWIP_IPV6 */
+53
View File
@@ -0,0 +1,53 @@
/**
* @file
*
* INET v6 addresses.
*/
/*
* 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 <delamer@inicotech.com>
*
*
* Please coordinate changes and requests with Ivan Delamer
* <delamer@inicotech.com>
*/
#include "lwip/opt.h"
#if LWIP_IPV6 && LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
#include "lwip/def.h"
#include "lwip/inet.h"
/** This variable is initialized by the system to contain the wildcard IPv6 address.
*/
const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
#endif /* LWIP_IPV6 */
File diff suppressed because it is too large Load Diff
+355
View File
@@ -0,0 +1,355 @@
/**
* @file
*
* IPv6 addresses.
*/
/*
* 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 <delamer@inicotech.com>
*
* Functions for handling IPv6 addresses.
*
* Please coordinate changes and requests with Ivan Delamer
* <delamer@inicotech.com>
*/
#include "lwip/opt.h"
#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */
#include "lwip/ip_addr.h"
#include "lwip/def.h"
#include "lwip/netif.h"
#include <string.h>
#if LWIP_IPV4
#include "lwip/ip4_addr.h" /* for ip6addr_aton to handle IPv4-mapped addresses */
#endif /* LWIP_IPV4 */
/* used by IP6_ADDR_ANY(6) in ip6_addr.h */
const ip_addr_t ip6_addr_any = IPADDR6_INIT(0ul, 0ul, 0ul, 0ul);
#define lwip_xchar(i) ((char)((i) < 10 ? '0' + (i) : 'A' + (i) - 10))
/**
* Check whether "cp" is a valid ascii representation
* of an IPv6 address and convert to a binary address.
* Returns 1 if the address is valid, 0 if not.
*
* @param cp IPv6 address in ascii representation (e.g. "FF01::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
ip6addr_aton(const char *cp, ip6_addr_t *addr)
{
u32_t addr_index, zero_blocks, current_block_index, current_block_value;
const char *s;
#if LWIP_IPV4
int check_ipv4_mapped = 0;
#endif /* LWIP_IPV4 */
/* Count the number of colons, to count the number of blocks in a "::" sequence
zero_blocks may be 1 even if there are no :: sequences */
zero_blocks = 8;
for (s = cp; *s != 0; s++) {
if (*s == ':') {
zero_blocks--;
#if LWIP_IPV4
} else if (*s == '.') {
if ((zero_blocks == 5) ||(zero_blocks == 2)) {
check_ipv4_mapped = 1;
/* last block could be the start of an IPv4 address */
zero_blocks--;
} else {
/* invalid format */
return 0;
}
break;
#endif /* LWIP_IPV4 */
} else if (!lwip_isxdigit(*s)) {
break;
}
}
/* parse each block */
addr_index = 0;
current_block_index = 0;
current_block_value = 0;
for (s = cp; *s != 0; s++) {
if (*s == ':') {
if (addr) {
if (current_block_index & 0x1) {
addr->addr[addr_index++] |= current_block_value;
}
else {
addr->addr[addr_index] = current_block_value << 16;
}
}
current_block_index++;
#if LWIP_IPV4
if (check_ipv4_mapped) {
if (current_block_index == 6) {
ip4_addr_t ip4;
int ret = ip4addr_aton(s + 1, &ip4);
if (ret) {
if (addr) {
addr->addr[3] = lwip_htonl(ip4.addr);
current_block_index++;
goto fix_byte_order_and_return;
}
return 1;
}
}
}
#endif /* LWIP_IPV4 */
current_block_value = 0;
if (current_block_index > 7) {
/* address too long! */
return 0;
}
if (s[1] == ':') {
if (s[2] == ':') {
/* invalid format: three successive colons */
return 0;
}
s++;
/* "::" found, set zeros */
while (zero_blocks > 0) {
zero_blocks--;
if (current_block_index & 0x1) {
addr_index++;
} else {
if (addr) {
addr->addr[addr_index] = 0;
}
}
current_block_index++;
if (current_block_index > 7) {
/* address too long! */
return 0;
}
}
}
} else if (lwip_isxdigit(*s)) {
/* add current digit */
current_block_value = (current_block_value << 4) +
(lwip_isdigit(*s) ? (u32_t)(*s - '0') :
(u32_t)(10 + (lwip_islower(*s) ? *s - 'a' : *s - 'A')));
} else {
/* unexpected digit, space? CRLF? */
break;
}
}
if (addr) {
if (current_block_index & 0x1) {
addr->addr[addr_index++] |= current_block_value;
}
else {
addr->addr[addr_index] = current_block_value << 16;
}
#if LWIP_IPV4
fix_byte_order_and_return:
#endif
/* convert to network byte order. */
for (addr_index = 0; addr_index < 4; addr_index++) {
addr->addr[addr_index] = lwip_htonl(addr->addr[addr_index]);
}
ip6_addr_clear_zone(addr);
#if LWIP_IPV6_SCOPES
if (*s == '%') {
const char *scopestr = s + 1;
if (*scopestr) {
struct netif *netif = netif_find(scopestr);
if (netif) {
ip6_addr_assign_zone(addr, IP6_UNKNOWN, netif);
}
}
}
#endif
}
if (current_block_index != 7) {
return 0;
}
return 1;
}
/**
* Convert numeric IPv6 address into ASCII representation.
* returns ptr to static buffer; not reentrant!
*
* @param addr ip6 address in network order to convert
* @return pointer to a global static (!) buffer that holds the ASCII
* representation of addr
*/
char *
ip6addr_ntoa(const ip6_addr_t *addr)
{
static char str[40];
return ip6addr_ntoa_r(addr, str, 40);
}
/**
* Same as ipaddr_ntoa, but reentrant since a user-supplied buffer is used.
*
* @param addr ip6 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 *
ip6addr_ntoa_r(const ip6_addr_t *addr, char *buf, int buflen)
{
u32_t current_block_index, current_block_value, next_block_value;
s32_t i;
u8_t zero_flag, empty_block_flag;
#if LWIP_IPV4
if (ip6_addr_isipv4mappedipv6(addr)) {
/* This is an IPv4 mapped address */
ip4_addr_t addr4;
char *ret;
#define IP4MAPPED_HEADER "::FFFF:"
char *buf_ip4 = buf + sizeof(IP4MAPPED_HEADER) - 1;
int buflen_ip4 = buflen - sizeof(IP4MAPPED_HEADER) + 1;
if (buflen < (int)sizeof(IP4MAPPED_HEADER)) {
return NULL;
}
memcpy(buf, IP4MAPPED_HEADER, sizeof(IP4MAPPED_HEADER));
addr4.addr = addr->addr[3];
ret = ip4addr_ntoa_r(&addr4, buf_ip4, buflen_ip4);
if (ret != buf_ip4) {
return NULL;
}
return buf;
}
#endif /* LWIP_IPV4 */
i = 0;
empty_block_flag = 0; /* used to indicate a zero chain for "::' */
for (current_block_index = 0; current_block_index < 8; current_block_index++) {
/* get the current 16-bit block */
current_block_value = lwip_htonl(addr->addr[current_block_index >> 1]);
if ((current_block_index & 0x1) == 0) {
current_block_value = current_block_value >> 16;
}
current_block_value &= 0xffff;
/* Check for empty block. */
if (current_block_value == 0) {
if (current_block_index == 7 && empty_block_flag == 1) {
/* special case, we must render a ':' for the last block. */
buf[i++] = ':';
if (i >= buflen) {
return NULL;
}
break;
}
if (empty_block_flag == 0) {
/* generate empty block "::", but only if more than one contiguous zero block,
* according to current formatting suggestions RFC 5952. */
next_block_value = lwip_htonl(addr->addr[(current_block_index + 1) >> 1]);
if ((current_block_index & 0x1) == 0x01) {
next_block_value = next_block_value >> 16;
}
next_block_value &= 0xffff;
if (next_block_value == 0) {
empty_block_flag = 1;
buf[i++] = ':';
if (i >= buflen) {
return NULL;
}
continue; /* move on to next block. */
}
} else if (empty_block_flag == 1) {
/* move on to next block. */
continue;
}
} else if (empty_block_flag == 1) {
/* Set this flag value so we don't produce multiple empty blocks. */
empty_block_flag = 2;
}
if (current_block_index > 0) {
buf[i++] = ':';
if (i >= buflen) {
return NULL;
}
}
if ((current_block_value & 0xf000) == 0) {
zero_flag = 1;
} else {
buf[i++] = lwip_xchar(((current_block_value & 0xf000) >> 12));
zero_flag = 0;
if (i >= buflen) {
return NULL;
}
}
if (((current_block_value & 0xf00) == 0) && (zero_flag)) {
/* do nothing */
} else {
buf[i++] = lwip_xchar(((current_block_value & 0xf00) >> 8));
zero_flag = 0;
if (i >= buflen) {
return NULL;
}
}
if (((current_block_value & 0xf0) == 0) && (zero_flag)) {
/* do nothing */
}
else {
buf[i++] = lwip_xchar(((current_block_value & 0xf0) >> 4));
zero_flag = 0;
if (i >= buflen) {
return NULL;
}
}
buf[i++] = lwip_xchar((current_block_value & 0xf));
if (i >= buflen) {
return NULL;
}
}
buf[i] = 0;
return buf;
}
#endif /* LWIP_IPV6 */
+862
View File
@@ -0,0 +1,862 @@
/**
* @file
*
* IPv6 fragmentation and reassembly.
*/
/*
* 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 <delamer@inicotech.com>
*
*
* Please coordinate changes and requests with Ivan Delamer
* <delamer@inicotech.com>
*/
#include "lwip/opt.h"
#include "lwip/ip6_frag.h"
#include "lwip/ip6.h"
#include "lwip/icmp6.h"
#include "lwip/nd6.h"
#include "lwip/ip.h"
#include "lwip/pbuf.h"
#include "lwip/memp.h"
#include "lwip/stats.h"
#include <string.h>
#if LWIP_IPV6 && LWIP_IPV6_REASS /* don't build if not configured for use in lwipopts.h */
/** 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 */
#if IPV6_FRAG_COPYHEADER
/* The number of bytes we need to "borrow" from (i.e., overwrite in) the header
* that precedes the fragment header for reassembly pruposes. */
#define IPV6_FRAG_REQROOM ((s16_t)(sizeof(struct ip6_reass_helper) - IP6_FRAG_HLEN))
#endif
#define IP_REASS_FLAG_LASTFRAG 0x01
/** 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 IPv6 header, since it replaces
* the Fragment Header in memory in incoming fragments to keep
* track of the various fragments.
*/
#ifdef PACK_STRUCT_USE_INCLUDES
# include "arch/bpstruct.h"
#endif
PACK_STRUCT_BEGIN
struct ip6_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
/* static variables */
static struct ip6_reassdata *reassdatagrams;
static u16_t ip6_reass_pbufcount;
/* Forward declarations. */
static void ip6_reass_free_complete_datagram(struct ip6_reassdata *ipr);
#if IP_REASS_FREE_OLDEST
static void ip6_reass_remove_oldest_datagram(struct ip6_reassdata *ipr, int pbufs_needed);
#endif /* IP_REASS_FREE_OLDEST */
void
ip6_reass_tmr(void)
{
struct ip6_reassdata *r, *tmp;
#if !IPV6_FRAG_COPYHEADER
LWIP_ASSERT("sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN, set IPV6_FRAG_COPYHEADER to 1",
sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN);
#endif /* !IPV6_FRAG_COPYHEADER */
r = reassdatagrams;
while (r != NULL) {
/* Decrement the timer. Once it reaches 0,
* clean up the incomplete fragment assembly */
if (r->timer > 0) {
r->timer--;
r = r->next;
} else {
/* reassembly timed out */
tmp = r;
/* get the next pointer before freeing */
r = r->next;
/* free the helper struct and all enqueued pbufs */
ip6_reass_free_complete_datagram(tmp);
}
}
}
/**
* Free a datagram (struct ip6_reassdata) and all its pbufs.
* Updates the total count of enqueued pbufs (ip6_reass_pbufcount),
* sends an ICMP time exceeded packet.
*
* @param ipr datagram to free
*/
static void
ip6_reass_free_complete_datagram(struct ip6_reassdata *ipr)
{
struct ip6_reassdata *prev;
u16_t pbufs_freed = 0;
u16_t clen;
struct pbuf *p;
struct ip6_reass_helper *iprh;
#if LWIP_ICMP6
iprh = (struct ip6_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;
/* Restore the part that we've overwritten with our helper structure, or we
* might send garbage (and disclose a pointer) in the ICMPv6 reply. */
MEMCPY(p->payload, ipr->orig_hdr, sizeof(iprh));
/* Then, move back to the original ipv6 header (we are now pointing to Fragment header).
This cannot fail since we already checked when receiving this fragment. */
if (pbuf_header_force(p, (s16_t)((u8_t*)p->payload - (u8_t*)ipr->iphdr))) {
LWIP_ASSERT("ip6_reass_free: moving p->payload to ip6 header failed", 0);
}
else {
/* Reconstruct the zoned source and destination addresses, so that we do
* not end up sending the ICMP response over the wrong link. */
ip6_addr_t src_addr, dest_addr;
ip6_addr_copy_from_packed(src_addr, IPV6_FRAG_SRC(ipr));
ip6_addr_set_zone(&src_addr, ipr->src_zone);
ip6_addr_copy_from_packed(dest_addr, IPV6_FRAG_DEST(ipr));
ip6_addr_set_zone(&dest_addr, ipr->dest_zone);
/* Send the actual ICMP response. */
icmp6_time_exceeded_with_addrs(p, ICMP6_TE_FRAG, &src_addr, &dest_addr);
}
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_ICMP6 */
/* 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 ip6_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 ip6_reassdata from the list and free it. */
if (ipr == reassdatagrams) {
reassdatagrams = ipr->next;
} else {
prev = reassdatagrams;
while (prev != NULL) {
if (prev->next == ipr) {
break;
}
prev = prev->next;
}
if (prev != NULL) {
prev->next = ipr->next;
}
}
memp_free(MEMP_IP6_REASSDATA, ipr);
/* Finally, update number of pbufs in reassembly queue */
LWIP_ASSERT("ip_reass_pbufcount >= clen", ip6_reass_pbufcount >= pbufs_freed);
ip6_reass_pbufcount = (u16_t)(ip6_reass_pbufcount - pbufs_freed);
}
#if IP_REASS_FREE_OLDEST
/**
* Free the oldest datagram to make room for enqueueing new fragments.
* The datagram ipr is not freed!
*
* @param ipr ip6_reassdata for the current fragment
* @param pbufs_needed number of pbufs needed to enqueue
* (used for freeing other datagrams if not enough space)
*/
static void
ip6_reass_remove_oldest_datagram(struct ip6_reassdata *ipr, int pbufs_needed)
{
struct ip6_reassdata *r, *oldest;
/* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs,
* but don't free the current datagram! */
do {
r = oldest = reassdatagrams;
while (r != NULL) {
if (r != ipr) {
if (r->timer <= oldest->timer) {
/* older than the previous oldest */
oldest = r;
}
}
r = r->next;
}
if (oldest == ipr) {
/* nothing to free, ipr is the only element on the list */
return;
}
if (oldest != NULL) {
ip6_reass_free_complete_datagram(oldest);
}
} while (((ip6_reass_pbufcount + pbufs_needed) > IP_REASS_MAX_PBUFS) && (reassdatagrams != NULL));
}
#endif /* IP_REASS_FREE_OLDEST */
/**
* Reassembles incoming IPv6 fragments into an IPv6 datagram.
*
* @param p points to the IPv6 Fragment Header
* @return NULL if reassembly is incomplete, pbuf pointing to
* IPv6 Header if reassembly is complete
*/
struct pbuf *
ip6_reass(struct pbuf *p)
{
struct ip6_reassdata *ipr, *ipr_prev;
struct ip6_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL;
struct ip6_frag_hdr *frag_hdr;
u16_t offset, len, start, end;
ptrdiff_t hdrdiff;
u16_t clen;
u8_t valid = 1;
struct pbuf *q, *next_pbuf;
IP6_FRAG_STATS_INC(ip6_frag.recv);
/* ip6_frag_hdr must be in the first pbuf, not chained. Checked by caller. */
LWIP_ASSERT("IPv6 fragment header does not fit in first pbuf",
p->len >= sizeof(struct ip6_frag_hdr));
frag_hdr = (struct ip6_frag_hdr *) p->payload;
clen = pbuf_clen(p);
offset = lwip_ntohs(frag_hdr->_fragment_offset);
/* Calculate fragment length from IPv6 payload length.
* Adjust for headers before Fragment Header.
* And finally adjust by Fragment Header length. */
len = lwip_ntohs(ip6_current_header()->_plen);
hdrdiff = (u8_t*)p->payload - (const u8_t*)ip6_current_header();
LWIP_ASSERT("not a valid pbuf (ip6_input check missing?)", hdrdiff <= 0xFFFF);
LWIP_ASSERT("not a valid pbuf (ip6_input check missing?)", hdrdiff >= IP6_HLEN);
hdrdiff -= IP6_HLEN;
hdrdiff += IP6_FRAG_HLEN;
if (hdrdiff > len) {
IP6_FRAG_STATS_INC(ip6_frag.proterr);
goto nullreturn;
}
len = (u16_t)(len - hdrdiff);
start = (offset & IP6_FRAG_OFFSET_MASK);
if (start > (0xFFFF - len)) {
/* u16_t overflow, cannot handle this */
IP6_FRAG_STATS_INC(ip6_frag.proterr);
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_prev = NULL; 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 ((frag_hdr->_identification == ipr->identification) &&
ip6_addr_packed_eq(ip6_current_src_addr(), &(IPV6_FRAG_SRC(ipr)), ipr->src_zone) &&
ip6_addr_packed_eq(ip6_current_dest_addr(), &(IPV6_FRAG_DEST(ipr)), ipr->dest_zone)) {
IP6_FRAG_STATS_INC(ip6_frag.cachehit);
break;
}
ipr_prev = ipr;
}
if (ipr == NULL) {
/* Enqueue a new datagram into the datagram queue */
ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA);
if (ipr == NULL) {
#if IP_REASS_FREE_OLDEST
/* Make room and try again. */
ip6_reass_remove_oldest_datagram(ipr, clen);
ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA);
if (ipr != NULL) {
/* re-search ipr_prev since it might have been removed */
for (ipr_prev = reassdatagrams; ipr_prev != NULL; ipr_prev = ipr_prev->next) {
if (ipr_prev->next == ipr) {
break;
}
}
} else
#endif /* IP_REASS_FREE_OLDEST */
{
IP6_FRAG_STATS_INC(ip6_frag.memerr);
goto nullreturn;
}
}
memset(ipr, 0, sizeof(struct ip6_reassdata));
ipr->timer = IPV6_REASS_MAXAGE;
/* enqueue the new structure to the front of the list */
ipr->next = reassdatagrams;
reassdatagrams = ipr;
/* Use the current IPv6 header for src/dest address reference.
* Eventually, we will replace it when we get the first fragment
* (it might be this one, in any case, it is done later). */
/* need to use the none-const pointer here: */
ipr->iphdr = ip_data.current_ip6_header;
#if IPV6_FRAG_COPYHEADER
MEMCPY(&ipr->src, &ip6_current_header()->src, sizeof(ipr->src));
MEMCPY(&ipr->dest, &ip6_current_header()->dest, sizeof(ipr->dest));
#endif /* IPV6_FRAG_COPYHEADER */
#if LWIP_IPV6_SCOPES
/* Also store the address zone information.
* @todo It is possible that due to netif destruction and recreation, the
* stored zones end up resolving to a different interface. In that case, we
* risk sending a "time exceeded" ICMP response over the wrong link.
* Ideally, netif destruction would clean up matching pending reassembly
* structures, but custom zone mappings would make that non-trivial. */
ipr->src_zone = ip6_addr_zone(ip6_current_src_addr());
ipr->dest_zone = ip6_addr_zone(ip6_current_dest_addr());
#endif /* LWIP_IPV6_SCOPES */
/* copy the fragmented packet id. */
ipr->identification = frag_hdr->_identification;
/* copy the nexth field */
ipr->nexth = frag_hdr->_nexth;
}
/* Check if we are allowed to enqueue more datagrams. */
if ((ip6_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) {
#if IP_REASS_FREE_OLDEST
ip6_reass_remove_oldest_datagram(ipr, clen);
if ((ip6_reass_pbufcount + clen) <= IP_REASS_MAX_PBUFS) {
/* re-search ipr_prev since it might have been removed */
for (ipr_prev = reassdatagrams; ipr_prev != NULL; ipr_prev = ipr_prev->next) {
if (ipr_prev->next == ipr) {
break;
}
}
} else
#endif /* IP_REASS_FREE_OLDEST */
{
/* @todo: send ICMPv6 time exceeded here? */
/* drop this pbuf */
IP6_FRAG_STATS_INC(ip6_frag.memerr);
goto nullreturn;
}
}
/* Overwrite Fragment Header with our own helper struct. */
#if IPV6_FRAG_COPYHEADER
if (IPV6_FRAG_REQROOM > 0) {
/* Make room for struct ip6_reass_helper (only required if sizeof(void*) > 4).
This cannot fail since we already checked when receiving this fragment. */
u8_t hdrerr = pbuf_header_force(p, IPV6_FRAG_REQROOM);
LWIP_UNUSED_ARG(hdrerr); /* in case of LWIP_NOASSERT */
LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0);
}
#else /* IPV6_FRAG_COPYHEADER */
LWIP_ASSERT("sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN, set IPV6_FRAG_COPYHEADER to 1",
sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN);
#endif /* IPV6_FRAG_COPYHEADER */
/* Prepare the pointer to the helper structure, and its initial values.
* Do not yet write to the structure itself, as we still have to make a
* backup of the original data, and we should not do that until we know for
* sure that we are going to add this packet to the list. */
iprh = (struct ip6_reass_helper *)p->payload;
next_pbuf = NULL;
end = (u16_t)(start + len);
/* find the right place to insert this pbuf */
/* Iterate through until we either get to the end of the list (append),
* or we find on with a larger offset (insert). */
for (q = ipr->p; q != NULL;) {
iprh_tmp = (struct ip6_reass_helper*)q->payload;
if (start < iprh_tmp->start) {
#if IP_REASS_CHECK_OVERLAP
if (end > iprh_tmp->start) {
/* fragment overlaps with following, throw away */
IP6_FRAG_STATS_INC(ip6_frag.proterr);
goto nullreturn;
}
if (iprh_prev != NULL) {
if (start < iprh_prev->end) {
/* fragment overlaps with previous, throw away */
IP6_FRAG_STATS_INC(ip6_frag.proterr);
goto nullreturn;
}
}
#endif /* IP_REASS_CHECK_OVERLAP */
/* the new pbuf should be inserted before this */
next_pbuf = q;
if (iprh_prev != NULL) {
/* not the fragment with the lowest offset */
iprh_prev->next_pbuf = p;
} else {
/* fragment with the lowest offset */
ipr->p = p;
}
break;
} else if (start == iprh_tmp->start) {
/* received the same datagram twice: no need to keep the datagram */
goto nullreturn;
#if IP_REASS_CHECK_OVERLAP
} else if (start < iprh_tmp->end) {
/* overlap: no need to keep the new datagram */
IP6_FRAG_STATS_INC(ip6_frag.proterr);
goto nullreturn;
#endif /* IP_REASS_CHECK_OVERLAP */
} else {
/* Check if the fragments received so far have no gaps. */
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 <= start);
#endif /* IP_REASS_CHECK_OVERLAP */
iprh_prev->next_pbuf = p;
if (iprh_prev->end != 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 = p;
}
}
/* 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 */
ip6_reass_pbufcount = (u16_t)(ip6_reass_pbufcount + clen);
/* Remember IPv6 header if this is the first fragment. */
if (start == 0) {
/* need to use the none-const pointer here: */
ipr->iphdr = ip_data.current_ip6_header;
/* Make a backup of the part of the packet data that we are about to
* overwrite, so that we can restore the original later. */
MEMCPY(ipr->orig_hdr, p->payload, sizeof(*iprh));
/* For IPV6_FRAG_COPYHEADER there is no need to copy src/dst again, as they
* will be the same as they were. With LWIP_IPV6_SCOPES, the same applies
* to the source/destination zones. */
}
/* Only after the backup do we get to fill in the actual helper structure. */
iprh->next_pbuf = next_pbuf;
iprh->start = start;
iprh->end = end;
/* If this is the last fragment, calculate total packet length. */
if ((offset & IP6_FRAG_MORE_FLAG) == 0) {
ipr->datagram_len = iprh->end;
}
/* Additional validity tests: we have received first and last fragment. */
iprh_tmp = (struct ip6_reass_helper*)ipr->p->payload;
if (iprh_tmp->start != 0) {
valid = 0;
}
if (ipr->datagram_len == 0) {
valid = 0;
}
/* Final validity test: no gaps between current and last fragment. */
iprh_prev = iprh;
q = iprh->next_pbuf;
while ((q != NULL) && valid) {
iprh = (struct ip6_reass_helper*)q->payload;
if (iprh_prev->end != iprh->start) {
valid = 0;
break;
}
iprh_prev = iprh;
q = iprh->next_pbuf;
}
if (valid) {
/* All fragments have been received */
struct ip6_hdr* iphdr_ptr;
/* chain together the pbufs contained within the ip6_reassdata list. */
iprh = (struct ip6_reass_helper*) ipr->p->payload;
while (iprh != NULL) {
next_pbuf = iprh->next_pbuf;
if (next_pbuf != NULL) {
/* Save next helper struct (will be hidden in next step). */
iprh_tmp = (struct ip6_reass_helper*)next_pbuf->payload;
/* hide the fragment header for every succeeding fragment */
pbuf_remove_header(next_pbuf, IP6_FRAG_HLEN);
#if IPV6_FRAG_COPYHEADER
if (IPV6_FRAG_REQROOM > 0) {
/* hide the extra bytes borrowed from ip6_hdr for struct ip6_reass_helper */
u8_t hdrerr = pbuf_remove_header(next_pbuf, IPV6_FRAG_REQROOM);
LWIP_UNUSED_ARG(hdrerr); /* in case of LWIP_NOASSERT */
LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0);
}
#endif
pbuf_cat(ipr->p, next_pbuf);
}
else {
iprh_tmp = NULL;
}
iprh = iprh_tmp;
}
/* Get the first pbuf. */
p = ipr->p;
#if IPV6_FRAG_COPYHEADER
if (IPV6_FRAG_REQROOM > 0) {
u8_t hdrerr;
/* Restore (only) the bytes that we overwrote beyond the fragment header.
* Those bytes may belong to either the IPv6 header or an extension
* header placed before the fragment header. */
MEMCPY(p->payload, ipr->orig_hdr, IPV6_FRAG_REQROOM);
/* get back room for struct ip6_reass_helper (only required if sizeof(void*) > 4) */
hdrerr = pbuf_remove_header(p, IPV6_FRAG_REQROOM);
LWIP_UNUSED_ARG(hdrerr); /* in case of LWIP_NOASSERT */
LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0);
}
#endif
/* We need to get rid of the fragment header itself, which is somewhere in
* the middle of the packet (but still in the first pbuf of the chain).
* Getting rid of the header is required by RFC 2460 Sec. 4.5 and necessary
* in order to be able to reassemble packets that are close to full size
* (i.e., around 65535 bytes). We simply move up all the headers before the
* fragment header, including the IPv6 header, and adjust the payload start
* accordingly. This works because all these headers are in the first pbuf
* of the chain, and because the caller adjusts all its pointers on
* successful reassembly. */
MEMMOVE((u8_t*)ipr->iphdr + sizeof(struct ip6_frag_hdr), ipr->iphdr,
(size_t)((u8_t*)p->payload - (u8_t*)ipr->iphdr));
/* This is where the IPv6 header is now. */
iphdr_ptr = (struct ip6_hdr*)((u8_t*)ipr->iphdr +
sizeof(struct ip6_frag_hdr));
/* Adjust datagram length by adding header lengths. */
ipr->datagram_len = (u16_t)(ipr->datagram_len + ((u8_t*)p->payload - (u8_t*)iphdr_ptr)
- IP6_HLEN);
/* Set payload length in ip header. */
iphdr_ptr->_plen = lwip_htons(ipr->datagram_len);
/* With the fragment header gone, we now need to adjust the next-header
* field of whatever header was originally before it. Since the packet made
* it through the original header processing routines at least up to the
* fragment header, we do not need any further sanity checks here. */
if (IP6H_NEXTH(iphdr_ptr) == IP6_NEXTH_FRAGMENT) {
iphdr_ptr->_nexth = ipr->nexth;
} else {
u8_t *ptr = (u8_t *)iphdr_ptr + IP6_HLEN;
while (*ptr != IP6_NEXTH_FRAGMENT) {
ptr += 8 * (1 + ptr[1]);
}
*ptr = ipr->nexth;
}
/* release the resources allocated for the fragment queue entry */
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", ipr_prev != NULL);
ipr_prev->next = ipr->next;
}
memp_free(MEMP_IP6_REASSDATA, ipr);
/* adjust the number of pbufs currently queued for reassembly. */
clen = pbuf_clen(p);
LWIP_ASSERT("ip6_reass_pbufcount >= clen", ip6_reass_pbufcount >= clen);
ip6_reass_pbufcount = (u16_t)(ip6_reass_pbufcount - clen);
/* Move pbuf back to IPv6 header. This should never fail. */
if (pbuf_header_force(p, (s16_t)((u8_t*)p->payload - (u8_t*)iphdr_ptr))) {
LWIP_ASSERT("ip6_reass: moving p->payload to ip6 header failed", 0);
pbuf_free(p);
return NULL;
}
/* Return the pbuf chain */
return p;
}
/* the datagram is not (yet?) reassembled completely */
return NULL;
nullreturn:
IP6_FRAG_STATS_INC(ip6_frag.drop);
pbuf_free(p);
return NULL;
}
#endif /* LWIP_IPV6 && LWIP_IPV6_REASS */
#if LWIP_IPV6 && LWIP_IPV6_FRAG
#if !LWIP_NETIF_TX_SINGLE_PBUF
/** Allocate a new struct pbuf_custom_ref */
static struct pbuf_custom_ref*
ip6_frag_alloc_pbuf_custom_ref(void)
{
return (struct pbuf_custom_ref*)memp_malloc(MEMP_FRAG_PBUF);
}
/** Free a struct pbuf_custom_ref */
static void
ip6_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
ip6_frag_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);
}
ip6_frag_free_pbuf_custom_ref(pcr);
}
#endif /* !LWIP_NETIF_TX_SINGLE_PBUF */
/**
* Fragment an IPv6 datagram if too large for the netif or path MTU.
*
* Chop the datagram in MTU sized chunks and send them in order
* by pointing PBUF_REFs into p
*
* @param p ipv6 packet to send
* @param netif the netif on which to send
* @param dest destination ipv6 address to which to send
*
* @return ERR_OK if sent successfully, err_t otherwise
*/
err_t
ip6_frag(struct pbuf *p, struct netif *netif, const ip6_addr_t *dest)
{
struct ip6_hdr *original_ip6hdr;
struct ip6_hdr *ip6hdr;
struct ip6_frag_hdr *frag_hdr;
struct pbuf *rambuf;
#if !LWIP_NETIF_TX_SINGLE_PBUF
struct pbuf *newpbuf;
u16_t newpbuflen = 0;
u16_t left_to_copy;
#endif
static u32_t identification;
u16_t left, cop;
const u16_t mtu = nd6_get_destination_mtu(dest, netif);
const u16_t nfb = (u16_t)((mtu - (IP6_HLEN + IP6_FRAG_HLEN)) & IP6_FRAG_OFFSET_MASK);
u16_t fragment_offset = 0;
u16_t last;
u16_t poff = IP6_HLEN;
identification++;
original_ip6hdr = (struct ip6_hdr *)p->payload;
/* @todo we assume there are no options in the unfragmentable part (IPv6 header). */
LWIP_ASSERT("p->tot_len >= IP6_HLEN", p->tot_len >= IP6_HLEN);
left = (u16_t)(p->tot_len - IP6_HLEN);
while (left) {
last = (left <= nfb);
/* Fill this fragment */
cop = last ? left : nfb;
#if LWIP_NETIF_TX_SINGLE_PBUF
rambuf = pbuf_alloc(PBUF_IP, cop + IP6_FRAG_HLEN, PBUF_RAM);
if (rambuf == NULL) {
IP6_FRAG_STATS_INC(ip6_frag.memerr);
return ERR_MEM;
}
LWIP_ASSERT("this needs a pbuf in one piece!",
(rambuf->len == rambuf->tot_len) && (rambuf->next == NULL));
poff += pbuf_copy_partial(p, (u8_t*)rambuf->payload + IP6_FRAG_HLEN, cop, poff);
/* make room for the IP header */
if (pbuf_add_header(rambuf, IP6_HLEN)) {
pbuf_free(rambuf);
IP6_FRAG_STATS_INC(ip6_frag.memerr);
return ERR_MEM;
}
/* fill in the IP header */
SMEMCPY(rambuf->payload, original_ip6hdr, IP6_HLEN);
ip6hdr = (struct ip6_hdr *)rambuf->payload;
frag_hdr = (struct ip6_frag_hdr *)((u8_t*)rambuf->payload + IP6_HLEN);
#else
/* When not using a static buffer, create a chain of pbufs.
* The first will be a PBUF_RAM holding the link, IPv6, and Fragment 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, IP6_HLEN + IP6_FRAG_HLEN, PBUF_RAM);
if (rambuf == NULL) {
IP6_FRAG_STATS_INC(ip6_frag.memerr);
return ERR_MEM;
}
LWIP_ASSERT("this needs a pbuf in one piece!",
(rambuf->len >= (IP6_HLEN)));
SMEMCPY(rambuf->payload, original_ip6hdr, IP6_HLEN);
ip6hdr = (struct ip6_hdr *)rambuf->payload;
frag_hdr = (struct ip6_frag_hdr *)((u8_t*)rambuf->payload + IP6_HLEN);
/* Can just adjust p directly for needed offset. */
p->payload = (u8_t *)p->payload + poff;
p->len = (u16_t)(p->len - poff);
p->tot_len = (u16_t)(p->tot_len - poff);
left_to_copy = cop;
while (left_to_copy) {
struct pbuf_custom_ref *pcr;
newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len;
/* Is this pbuf already empty? */
if (!newpbuflen) {
p = p->next;
continue;
}
pcr = ip6_frag_alloc_pbuf_custom_ref();
if (pcr == NULL) {
pbuf_free(rambuf);
IP6_FRAG_STATS_INC(ip6_frag.memerr);
return ERR_MEM;
}
/* Mirror this pbuf, although we might not need all of it. */
newpbuf = pbuf_alloced_custom(PBUF_RAW, newpbuflen, PBUF_REF, &pcr->pc, p->payload, newpbuflen);
if (newpbuf == NULL) {
ip6_frag_free_pbuf_custom_ref(pcr);
pbuf_free(rambuf);
IP6_FRAG_STATS_INC(ip6_frag.memerr);
return ERR_MEM;
}
pbuf_ref(p);
pcr->original = p;
pcr->pc.custom_free_function = ip6_frag_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) {
p = p->next;
}
}
poff = newpbuflen;
#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
/* Set headers */
frag_hdr->_nexth = original_ip6hdr->_nexth;
frag_hdr->reserved = 0;
frag_hdr->_fragment_offset = lwip_htons((u16_t)((fragment_offset & IP6_FRAG_OFFSET_MASK) | (last ? 0 : IP6_FRAG_MORE_FLAG)));
frag_hdr->_identification = lwip_htonl(identification);
IP6H_NEXTH_SET(ip6hdr, IP6_NEXTH_FRAGMENT);
IP6H_PLEN_SET(ip6hdr, (u16_t)(cop + IP6_FRAG_HLEN));
/* No need for separate header pbuf - we allowed room for it in rambuf
* when allocated.
*/
IP6_FRAG_STATS_INC(ip6_frag.xmit);
netif->output_ip6(netif, rambuf, dest);
/* 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 - cop);
fragment_offset = (u16_t)(fragment_offset + cop);
}
return ERR_OK;
}
#endif /* LWIP_IPV6 && LWIP_IPV6_FRAG */
+626
View File
@@ -0,0 +1,626 @@
/**
* @file
* Multicast listener discovery
*
* @defgroup mld6 MLD6
* @ingroup ip6
* Multicast listener discovery for IPv6. Aims to be compliant with RFC 2710.
* No support for MLDv2.<br>
* Note: The allnodes (ff01::1, ff02::1) group is assumed be received by your
* netif since it must always be received for correct IPv6 operation (e.g. SLAAC).
* Ensure the netif filters are configured accordingly!<br>
* The netif flags also need NETIF_FLAG_MLD6 flag set to enable MLD6 on a
* netif ("netif->flags |= NETIF_FLAG_MLD6;").<br>
* To be called from TCPIP thread.
*/
/*
* 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 <delamer@inicotech.com>
*
*
* Please coordinate changes and requests with Ivan Delamer
* <delamer@inicotech.com>
*/
/* Based on igmp.c implementation of igmp v2 protocol */
#include "lwip/opt.h"
#if LWIP_IPV6 && LWIP_IPV6_MLD /* don't build if not configured for use in lwipopts.h */
#include "lwip/mld6.h"
#include "lwip/prot/mld6.h"
#include "lwip/icmp6.h"
#include "lwip/ip6.h"
#include "lwip/ip6_addr.h"
#include "lwip/ip.h"
#include "lwip/inet_chksum.h"
#include "lwip/pbuf.h"
#include "lwip/netif.h"
#include "lwip/memp.h"
#include "lwip/stats.h"
#include <string.h>
/*
* MLD constants
*/
#define MLD6_HL 1
#define MLD6_JOIN_DELAYING_MEMBER_TMR_MS (500)
#define MLD6_GROUP_NON_MEMBER 0
#define MLD6_GROUP_DELAYING_MEMBER 1
#define MLD6_GROUP_IDLE_MEMBER 2
/* Forward declarations. */
static struct mld_group *mld6_new_group(struct netif *ifp, const ip6_addr_t *addr);
static err_t mld6_remove_group(struct netif *netif, struct mld_group *group);
static void mld6_delayed_report(struct mld_group *group, u16_t maxresp);
static void mld6_send(struct netif *netif, struct mld_group *group, u8_t type);
/**
* Stop MLD processing on interface
*
* @param netif network interface on which stop MLD processing
*/
err_t
mld6_stop(struct netif *netif)
{
struct mld_group *group = netif_mld6_data(netif);
netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_MLD6, NULL);
while (group != NULL) {
struct mld_group *next = group->next; /* avoid use-after-free below */
/* disable the group at the MAC level */
if (netif->mld_mac_filter != NULL) {
netif->mld_mac_filter(netif, &(group->group_address), NETIF_DEL_MAC_FILTER);
}
/* free group */
memp_free(MEMP_MLD6_GROUP, group);
/* move to "next" */
group = next;
}
return ERR_OK;
}
/**
* Report MLD memberships for this interface
*
* @param netif network interface on which report MLD memberships
*/
void
mld6_report_groups(struct netif *netif)
{
struct mld_group *group = netif_mld6_data(netif);
while (group != NULL) {
mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS);
group = group->next;
}
}
/**
* Search for a group that is joined on a netif
*
* @param ifp the network interface for which to look
* @param addr the group ipv6 address to search for
* @return a struct mld_group* if the group has been found,
* NULL if the group wasn't found.
*/
struct mld_group *
mld6_lookfor_group(struct netif *ifp, const ip6_addr_t *addr)
{
struct mld_group *group = netif_mld6_data(ifp);
while (group != NULL) {
if (ip6_addr_eq(&(group->group_address), addr)) {
return group;
}
group = group->next;
}
return NULL;
}
/**
* create a new group
*
* @param ifp the network interface for which to create
* @param addr the new group ipv6
* @return a struct mld_group*,
* NULL on memory error.
*/
static struct mld_group *
mld6_new_group(struct netif *ifp, const ip6_addr_t *addr)
{
struct mld_group *group;
group = (struct mld_group *)memp_malloc(MEMP_MLD6_GROUP);
if (group != NULL) {
ip6_addr_set(&(group->group_address), addr);
group->timer = 0; /* Not running */
group->group_state = MLD6_GROUP_IDLE_MEMBER;
group->last_reporter_flag = 0;
group->use = 0;
group->next = netif_mld6_data(ifp);
netif_set_client_data(ifp, LWIP_NETIF_CLIENT_DATA_INDEX_MLD6, group);
}
return group;
}
/**
* Remove a group from the mld_group_list, but do not free it yet
*
* @param group the group to remove
* @return ERR_OK if group was removed from the list, an err_t otherwise
*/
static err_t
mld6_remove_group(struct netif *netif, struct mld_group *group)
{
err_t err = ERR_OK;
/* Is it the first group? */
if (netif_mld6_data(netif) == group) {
netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_MLD6, group->next);
} else {
/* look for group further down the list */
struct mld_group *tmpGroup;
for (tmpGroup = netif_mld6_data(netif); tmpGroup != NULL; tmpGroup = tmpGroup->next) {
if (tmpGroup->next == group) {
tmpGroup->next = group->next;
break;
}
}
/* Group not find group */
if (tmpGroup == NULL) {
err = ERR_ARG;
}
}
return err;
}
/**
* Process an input MLD message. Called by icmp6_input.
*
* @param p the mld packet, p->payload pointing to the icmpv6 header
* @param inp the netif on which this packet was received
*/
void
mld6_input(struct pbuf *p, struct netif *inp)
{
struct mld_header *mld_hdr;
struct mld_group *group;
MLD6_STATS_INC(mld6.recv);
/* Check that mld header fits in packet. */
if (p->len < sizeof(struct mld_header)) {
/* @todo debug message */
pbuf_free(p);
MLD6_STATS_INC(mld6.lenerr);
MLD6_STATS_INC(mld6.drop);
return;
}
mld_hdr = (struct mld_header *)p->payload;
switch (mld_hdr->type) {
case ICMP6_TYPE_MLQ: /* Multicast listener query. */
/* Is it a general query? */
if (ip6_addr_isallnodes_linklocal(ip6_current_dest_addr()) &&
ip6_addr_isany(&(mld_hdr->multicast_address))) {
MLD6_STATS_INC(mld6.rx_general);
/* Report all groups, except all nodes group, and if-local groups. */
group = netif_mld6_data(inp);
while (group != NULL) {
if ((!(ip6_addr_ismulticast_iflocal(&(group->group_address)))) &&
(!(ip6_addr_isallnodes_linklocal(&(group->group_address))))) {
mld6_delayed_report(group, lwip_ntohs(mld_hdr->max_resp_delay));
}
group = group->next;
}
} else {
/* Have we joined this group?
* We use IP6 destination address to have a memory aligned copy.
* mld_hdr->multicast_address should be the same. */
MLD6_STATS_INC(mld6.rx_group);
group = mld6_lookfor_group(inp, ip6_current_dest_addr());
if (group != NULL) {
/* Schedule a report. */
mld6_delayed_report(group, lwip_ntohs(mld_hdr->max_resp_delay));
}
}
break; /* ICMP6_TYPE_MLQ */
case ICMP6_TYPE_MLR: /* Multicast listener report. */
/* Have we joined this group?
* We use IP6 destination address to have a memory aligned copy.
* mld_hdr->multicast_address should be the same. */
MLD6_STATS_INC(mld6.rx_report);
group = mld6_lookfor_group(inp, ip6_current_dest_addr());
if (group != NULL) {
/* If we are waiting to report, cancel it. */
if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) {
group->timer = 0; /* stopped */
group->group_state = MLD6_GROUP_IDLE_MEMBER;
group->last_reporter_flag = 0;
}
}
break; /* ICMP6_TYPE_MLR */
case ICMP6_TYPE_MLD: /* Multicast listener done. */
/* Do nothing, router will query us. */
break; /* ICMP6_TYPE_MLD */
default:
MLD6_STATS_INC(mld6.proterr);
MLD6_STATS_INC(mld6.drop);
break;
}
pbuf_free(p);
}
/**
* @ingroup mld6
* Join a group on one or all network interfaces.
*
* If the group is to be joined on all interfaces, the given group address must
* not have a zone set (i.e., it must have its zone index set to IP6_NO_ZONE).
* If the group is to be joined on one particular interface, the given group
* address may or may not have a zone set.
*
* @param srcaddr ipv6 address (zoned) of the network interface which should
* join a new group. If IP6_ADDR_ANY6, join on all netifs
* @param groupaddr the ipv6 address of the group to join (possibly but not
* necessarily zoned)
* @return ERR_OK if group was joined on the netif(s), an err_t otherwise
*/
err_t
mld6_joingroup(const ip6_addr_t *srcaddr, const ip6_addr_t *groupaddr)
{
err_t err = ERR_VAL; /* no matching interface */
struct netif *netif;
LWIP_ASSERT_CORE_LOCKED();
/* loop through netif's */
NETIF_FOREACH(netif) {
/* Should we join this interface ? */
if (ip6_addr_isany(srcaddr) ||
netif_get_ip6_addr_match(netif, srcaddr) >= 0) {
err = mld6_joingroup_netif(netif, groupaddr);
if (err != ERR_OK) {
return err;
}
}
}
return err;
}
/**
* @ingroup mld6
* Join a group on a network interface.
*
* @param netif the network interface which should join a new group.
* @param groupaddr the ipv6 address of the group to join (possibly but not
* necessarily zoned)
* @return ERR_OK if group was joined on the netif, an err_t otherwise
*/
err_t
mld6_joingroup_netif(struct netif *netif, const ip6_addr_t *groupaddr)
{
struct mld_group *group;
#if LWIP_IPV6_SCOPES
ip6_addr_t ip6addr;
/* If the address has a particular scope but no zone set, use the netif to
* set one now. Within the mld6 module, all addresses are properly zoned. */
if (ip6_addr_lacks_zone(groupaddr, IP6_MULTICAST)) {
ip6_addr_set(&ip6addr, groupaddr);
ip6_addr_assign_zone(&ip6addr, IP6_MULTICAST, netif);
groupaddr = &ip6addr;
}
IP6_ADDR_ZONECHECK_NETIF(groupaddr, netif);
#endif /* LWIP_IPV6_SCOPES */
LWIP_ASSERT_CORE_LOCKED();
/* find group or create a new one if not found */
group = mld6_lookfor_group(netif, groupaddr);
if (group == NULL) {
/* Joining a new group. Create a new group entry. */
group = mld6_new_group(netif, groupaddr);
if (group == NULL) {
return ERR_MEM;
}
/* Activate this address on the MAC layer. */
if (netif->mld_mac_filter != NULL) {
netif->mld_mac_filter(netif, groupaddr, NETIF_ADD_MAC_FILTER);
}
/* Report our membership. */
MLD6_STATS_INC(mld6.tx_report);
mld6_send(netif, group, ICMP6_TYPE_MLR);
mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS);
}
/* Increment group use */
group->use++;
return ERR_OK;
}
/**
* @ingroup mld6
* Leave a group on a network interface.
*
* Zoning of address follows the same rules as @ref mld6_joingroup.
*
* @param srcaddr ipv6 address (zoned) of the network interface which should
* leave the group. If IP6_ADDR_ANY6, leave on all netifs
* @param groupaddr the ipv6 address of the group to leave (possibly, but not
* necessarily zoned)
* @return ERR_OK if group was left on the netif(s), an err_t otherwise
*/
err_t
mld6_leavegroup(const ip6_addr_t *srcaddr, const ip6_addr_t *groupaddr)
{
err_t err = ERR_VAL; /* no matching interface */
struct netif *netif;
LWIP_ASSERT_CORE_LOCKED();
/* loop through netif's */
NETIF_FOREACH(netif) {
/* Should we leave this interface ? */
if (ip6_addr_isany(srcaddr) ||
netif_get_ip6_addr_match(netif, srcaddr) >= 0) {
err_t res = mld6_leavegroup_netif(netif, groupaddr);
if (err != ERR_OK) {
/* Store this result if we have not yet gotten a success */
err = res;
}
}
}
return err;
}
/**
* @ingroup mld6
* Leave a group on a network interface.
*
* @param netif the network interface which should leave the group.
* @param groupaddr the ipv6 address of the group to leave (possibly, but not
* necessarily zoned)
* @return ERR_OK if group was left on the netif, an err_t otherwise
*/
err_t
mld6_leavegroup_netif(struct netif *netif, const ip6_addr_t *groupaddr)
{
struct mld_group *group;
#if LWIP_IPV6_SCOPES
ip6_addr_t ip6addr;
if (ip6_addr_lacks_zone(groupaddr, IP6_MULTICAST)) {
ip6_addr_set(&ip6addr, groupaddr);
ip6_addr_assign_zone(&ip6addr, IP6_MULTICAST, netif);
groupaddr = &ip6addr;
}
IP6_ADDR_ZONECHECK_NETIF(groupaddr, netif);
#endif /* LWIP_IPV6_SCOPES */
LWIP_ASSERT_CORE_LOCKED();
/* find group */
group = mld6_lookfor_group(netif, groupaddr);
if (group != NULL) {
/* Leave if there is no other use of the group */
if (group->use <= 1) {
/* Remove the group from the list */
mld6_remove_group(netif, group);
/* If we are the last reporter for this group */
if (group->last_reporter_flag) {
MLD6_STATS_INC(mld6.tx_leave);
mld6_send(netif, group, ICMP6_TYPE_MLD);
}
/* Disable the group at the MAC level */
if (netif->mld_mac_filter != NULL) {
netif->mld_mac_filter(netif, groupaddr, NETIF_DEL_MAC_FILTER);
}
/* free group struct */
memp_free(MEMP_MLD6_GROUP, group);
} else {
/* Decrement group use */
group->use--;
}
/* Left group */
return ERR_OK;
}
/* Group not found */
return ERR_VAL;
}
/**
* Periodic timer for mld processing. Must be called every
* MLD6_TMR_INTERVAL milliseconds (100).
*
* When a delaying member expires, a membership report is sent.
*/
void
mld6_tmr(void)
{
struct netif *netif;
NETIF_FOREACH(netif) {
struct mld_group *group = netif_mld6_data(netif);
while (group != NULL) {
if (group->timer > 0) {
group->timer--;
if (group->timer == 0) {
/* If the state is MLD6_GROUP_DELAYING_MEMBER then we send a report for this group */
if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) {
MLD6_STATS_INC(mld6.tx_report);
mld6_send(netif, group, ICMP6_TYPE_MLR);
group->group_state = MLD6_GROUP_IDLE_MEMBER;
}
}
}
group = group->next;
}
}
}
/**
* Schedule a delayed membership report for a group
*
* @param group the mld_group for which "delaying" membership report
* should be sent
* @param maxresp_in the max resp delay provided in the query
*/
static void
mld6_delayed_report(struct mld_group *group, u16_t maxresp_in)
{
/* Convert maxresp from milliseconds to tmr ticks */
u16_t maxresp = maxresp_in / MLD6_TMR_INTERVAL;
if (maxresp == 0) {
maxresp = 1;
}
#ifdef LWIP_RAND
/* Randomize maxresp. (if LWIP_RAND is supported) */
maxresp = (u16_t)(LWIP_RAND() % maxresp);
if (maxresp == 0) {
maxresp = 1;
}
#endif /* LWIP_RAND */
/* Apply timer value if no report has been scheduled already. */
if ((group->group_state == MLD6_GROUP_IDLE_MEMBER) ||
((group->group_state == MLD6_GROUP_DELAYING_MEMBER) &&
((group->timer == 0) || (maxresp < group->timer)))) {
group->timer = maxresp;
group->group_state = MLD6_GROUP_DELAYING_MEMBER;
}
}
/**
* Send a MLD message (report or done).
*
* An IPv6 hop-by-hop options header with a router alert option
* is prepended.
*
* @param group the group to report or quit
* @param type ICMP6_TYPE_MLR (report) or ICMP6_TYPE_MLD (done)
*/
static void
mld6_send(struct netif *netif, struct mld_group *group, u8_t type)
{
struct mld_header *mld_hdr;
struct pbuf *p;
const ip6_addr_t *src_addr;
/* Allocate a packet. Size is MLD header + IPv6 Hop-by-hop options header. */
p = pbuf_alloc(PBUF_IP, sizeof(struct mld_header) + MLD6_HBH_HLEN, PBUF_RAM);
if (p == NULL) {
MLD6_STATS_INC(mld6.memerr);
return;
}
/* Move to make room for Hop-by-hop options header. */
if (pbuf_remove_header(p, MLD6_HBH_HLEN)) {
pbuf_free(p);
MLD6_STATS_INC(mld6.lenerr);
return;
}
/* Select our source address. */
if (!ip6_addr_isvalid(netif_ip6_addr_state(netif, 0))) {
/* This is a special case, when we are performing duplicate address detection.
* We must join the multicast group, but we don't have a valid address yet. */
src_addr = IP6_ADDR_ANY6;
} else {
/* Use link-local address as source address. */
src_addr = netif_ip6_addr(netif, 0);
}
/* MLD message header pointer. */
mld_hdr = (struct mld_header *)p->payload;
/* Set fields. */
mld_hdr->type = type;
mld_hdr->code = 0;
mld_hdr->chksum = 0;
mld_hdr->max_resp_delay = 0;
mld_hdr->reserved = 0;
ip6_addr_copy_to_packed(mld_hdr->multicast_address, group->group_address);
#if CHECKSUM_GEN_ICMP6
IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) {
mld_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len,
src_addr, &(group->group_address));
}
#endif /* CHECKSUM_GEN_ICMP6 */
/* Add hop-by-hop headers options: router alert with MLD value. */
ip6_options_add_hbh_ra(p, IP6_NEXTH_ICMP6, IP6_ROUTER_ALERT_VALUE_MLD);
if (type == ICMP6_TYPE_MLR) {
/* Remember we were the last to report */
group->last_reporter_flag = 1;
}
/* Send the packet out. */
MLD6_STATS_INC(mld6.xmit);
ip6_output_if(p, (ip6_addr_isany(src_addr)) ? NULL : src_addr, &(group->group_address),
MLD6_HL, 0, IP6_NEXTH_HOPBYHOP, netif);
pbuf_free(p);
}
#endif /* LWIP_IPV6 */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+447
View File
@@ -0,0 +1,447 @@
/**
* @file
* Dynamic pool memory manager
*
* lwIP has dedicated pools for many structures (netconn, protocol control blocks,
* packet buffers, ...). All these pools are managed here.
*
* @defgroup mempool Memory pools
* @ingroup infrastructure
* Custom memory pools
*/
/*
* 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 <adam@sics.se>
*
*/
#include "lwip/opt.h"
#include "lwip/memp.h"
#include "lwip/sys.h"
#include "lwip/stats.h"
#include <string.h>
/* Make sure we include everything we need for size calculation required by memp_std.h */
#include "lwip/pbuf.h"
#include "lwip/raw.h"
#include "lwip/udp.h"
#include "lwip/tcp.h"
#include "lwip/priv/tcp_priv.h"
#include "lwip/altcp.h"
#include "lwip/ip4_frag.h"
#include "lwip/netbuf.h"
#include "lwip/api.h"
#include "lwip/priv/tcpip_priv.h"
#include "lwip/priv/api_msg.h"
#include "lwip/priv/sockets_priv.h"
#include "lwip/etharp.h"
#include "lwip/igmp.h"
#include "lwip/timeouts.h"
/* needed by default MEMP_NUM_SYS_TIMEOUT */
#include "netif/ppp/ppp_opts.h"
#include "lwip/netdb.h"
#include "lwip/dns.h"
#include "lwip/priv/nd6_priv.h"
#include "lwip/ip6_frag.h"
#include "lwip/mld6.h"
#define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEMPOOL_DECLARE(name,num,size,desc)
#include "lwip/priv/memp_std.h"
const struct memp_desc *const memp_pools[MEMP_MAX] = {
#define LWIP_MEMPOOL(name,num,size,desc) &memp_ ## name,
#include "lwip/priv/memp_std.h"
};
#ifdef LWIP_HOOK_FILENAME
#include LWIP_HOOK_FILENAME
#endif
#if MEMP_MEM_MALLOC && MEMP_OVERFLOW_CHECK >= 2
#undef MEMP_OVERFLOW_CHECK
/* MEMP_OVERFLOW_CHECK >= 2 does not work with MEMP_MEM_MALLOC, use 1 instead */
#define MEMP_OVERFLOW_CHECK 1
#endif
#if MEMP_SANITY_CHECK && !MEMP_MEM_MALLOC
/**
* Check that memp-lists don't form a circle, using "Floyd's cycle-finding algorithm".
*/
static int
memp_sanity(const struct memp_desc *desc)
{
struct memp *t, *h;
t = *desc->tab;
if (t != NULL) {
for (h = t->next; (t != NULL) && (h != NULL); t = t->next,
h = ((h->next != NULL) ? h->next->next : NULL)) {
if (t == h) {
return 0;
}
}
}
return 1;
}
#endif /* MEMP_SANITY_CHECK && !MEMP_MEM_MALLOC */
#if MEMP_OVERFLOW_CHECK
/**
* Check if a memp element was victim of an overflow or underflow
* (e.g. the restricted area after/before it has been altered)
*
* @param p the memp element to check
* @param desc the pool p comes from
*/
static void
memp_overflow_check_element(struct memp *p, const struct memp_desc *desc)
{
mem_overflow_check_raw((u8_t *)p + MEMP_SIZE, desc->size, "pool ", desc->desc);
}
/**
* Initialize the restricted area of on memp element.
*/
static void
memp_overflow_init_element(struct memp *p, const struct memp_desc *desc)
{
mem_overflow_init_raw((u8_t *)p + MEMP_SIZE, desc->size);
}
#if MEMP_OVERFLOW_CHECK >= 2
/**
* Do an overflow check for all elements in every pool.
*
* @see memp_overflow_check_element for a description of the check
*/
static void
memp_overflow_check_all(void)
{
u16_t i, j;
struct memp *p;
SYS_ARCH_DECL_PROTECT(old_level);
SYS_ARCH_PROTECT(old_level);
for (i = 0; i < MEMP_MAX; ++i) {
p = (struct memp *)LWIP_MEM_ALIGN(memp_pools[i]->base);
for (j = 0; j < memp_pools[i]->num; ++j) {
memp_overflow_check_element(p, memp_pools[i]);
p = LWIP_ALIGNMENT_CAST(struct memp *, ((u8_t *)p + MEMP_SIZE + memp_pools[i]->size + MEM_SANITY_REGION_AFTER_ALIGNED));
}
}
SYS_ARCH_UNPROTECT(old_level);
}
#endif /* MEMP_OVERFLOW_CHECK >= 2 */
#endif /* MEMP_OVERFLOW_CHECK */
/**
* Initialize custom memory pool.
* Related functions: memp_malloc_pool, memp_free_pool
*
* @param desc pool to initialize
*/
void
memp_init_pool(const struct memp_desc *desc)
{
#if MEMP_MEM_MALLOC
LWIP_UNUSED_ARG(desc);
#else
int i;
struct memp *memp;
*desc->tab = NULL;
memp = (struct memp *)LWIP_MEM_ALIGN(desc->base);
#if MEMP_MEM_INIT
/* force memset on pool memory */
memset(memp, 0, (size_t)desc->num * (MEMP_SIZE + desc->size
#if MEMP_OVERFLOW_CHECK
+ MEM_SANITY_REGION_AFTER_ALIGNED
#endif
));
#endif
/* create a linked list of memp elements */
for (i = 0; i < desc->num; ++i) {
memp->next = *desc->tab;
*desc->tab = memp;
#if MEMP_OVERFLOW_CHECK
memp_overflow_init_element(memp, desc);
#endif /* MEMP_OVERFLOW_CHECK */
/* cast through void* to get rid of alignment warnings */
memp = (struct memp *)(void *)((u8_t *)memp + MEMP_SIZE + desc->size
#if MEMP_OVERFLOW_CHECK
+ MEM_SANITY_REGION_AFTER_ALIGNED
#endif
);
}
#if MEMP_STATS
desc->stats->avail = desc->num;
#endif /* MEMP_STATS */
#endif /* !MEMP_MEM_MALLOC */
#if MEMP_STATS && (defined(LWIP_DEBUG) || LWIP_STATS_DISPLAY)
desc->stats->name = desc->desc;
#endif /* MEMP_STATS && (defined(LWIP_DEBUG) || LWIP_STATS_DISPLAY) */
}
/**
* Initializes lwIP built-in pools.
* Related functions: memp_malloc, memp_free
*
* Carves out memp_memory into linked lists for each pool-type.
*/
void
memp_init(void)
{
u16_t i;
/* for every pool: */
for (i = 0; i < LWIP_ARRAYSIZE(memp_pools); i++) {
memp_init_pool(memp_pools[i]);
#if LWIP_STATS && MEMP_STATS
lwip_stats.memp[i] = memp_pools[i]->stats;
#endif
}
#if MEMP_OVERFLOW_CHECK >= 2
/* check everything a first time to see if it worked */
memp_overflow_check_all();
#endif /* MEMP_OVERFLOW_CHECK >= 2 */
}
static void *
#if !MEMP_OVERFLOW_CHECK
do_memp_malloc_pool(const struct memp_desc *desc)
#else
do_memp_malloc_pool_fn(const struct memp_desc *desc, const char *file, const int line)
#endif
{
struct memp *memp;
SYS_ARCH_DECL_PROTECT(old_level);
#if MEMP_MEM_MALLOC
memp = (struct memp *)mem_malloc(MEMP_SIZE + MEMP_ALIGN_SIZE(desc->size));
SYS_ARCH_PROTECT(old_level);
#else /* MEMP_MEM_MALLOC */
SYS_ARCH_PROTECT(old_level);
memp = *desc->tab;
#endif /* MEMP_MEM_MALLOC */
if (memp != NULL) {
#if !MEMP_MEM_MALLOC
#if MEMP_OVERFLOW_CHECK == 1
memp_overflow_check_element(memp, desc);
#endif /* MEMP_OVERFLOW_CHECK */
*desc->tab = memp->next;
#if MEMP_OVERFLOW_CHECK
memp->next = NULL;
#endif /* MEMP_OVERFLOW_CHECK */
#endif /* !MEMP_MEM_MALLOC */
#if MEMP_OVERFLOW_CHECK
memp->file = file;
memp->line = line;
#if MEMP_MEM_MALLOC
memp_overflow_init_element(memp, desc);
#endif /* MEMP_MEM_MALLOC */
#endif /* MEMP_OVERFLOW_CHECK */
LWIP_ASSERT("memp_malloc: memp properly aligned",
((mem_ptr_t)memp % MEM_ALIGNMENT) == 0);
#if MEMP_STATS
desc->stats->used++;
if (desc->stats->used > desc->stats->max) {
desc->stats->max = desc->stats->used;
}
#endif
SYS_ARCH_UNPROTECT(old_level);
/* cast through u8_t* to get rid of alignment warnings */
return ((u8_t *)memp + MEMP_SIZE);
} else {
#if MEMP_STATS
desc->stats->err++;
#endif
SYS_ARCH_UNPROTECT(old_level);
LWIP_DEBUGF(MEMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("memp_malloc: out of memory in pool %s\n", desc->desc));
}
return NULL;
}
/**
* Get an element from a custom pool.
*
* @param desc the pool to get an element from
*
* @return a pointer to the allocated memory or a NULL pointer on error
*/
void *
#if !MEMP_OVERFLOW_CHECK
memp_malloc_pool(const struct memp_desc *desc)
#else
memp_malloc_pool_fn(const struct memp_desc *desc, const char *file, const int line)
#endif
{
LWIP_ASSERT("invalid pool desc", desc != NULL);
if (desc == NULL) {
return NULL;
}
#if !MEMP_OVERFLOW_CHECK
return do_memp_malloc_pool(desc);
#else
return do_memp_malloc_pool_fn(desc, file, line);
#endif
}
/**
* Get an element from a specific pool.
*
* @param type the pool to get an element from
*
* @return a pointer to the allocated memory or a NULL pointer on error
*/
void *
#if !MEMP_OVERFLOW_CHECK
memp_malloc(memp_t type)
#else
memp_malloc_fn(memp_t type, const char *file, const int line)
#endif
{
void *memp;
LWIP_ERROR("memp_malloc: type < MEMP_MAX", (type < MEMP_MAX), return NULL;);
#if MEMP_OVERFLOW_CHECK >= 2
memp_overflow_check_all();
#endif /* MEMP_OVERFLOW_CHECK >= 2 */
#if !MEMP_OVERFLOW_CHECK
memp = do_memp_malloc_pool(memp_pools[type]);
#else
memp = do_memp_malloc_pool_fn(memp_pools[type], file, line);
#endif
return memp;
}
static void
do_memp_free_pool(const struct memp_desc *desc, void *mem)
{
struct memp *memp;
SYS_ARCH_DECL_PROTECT(old_level);
LWIP_ASSERT("memp_free: mem properly aligned",
((mem_ptr_t)mem % MEM_ALIGNMENT) == 0);
/* cast through void* to get rid of alignment warnings */
memp = (struct memp *)(void *)((u8_t *)mem - MEMP_SIZE);
SYS_ARCH_PROTECT(old_level);
#if MEMP_OVERFLOW_CHECK == 1
memp_overflow_check_element(memp, desc);
#endif /* MEMP_OVERFLOW_CHECK */
#if MEMP_STATS
desc->stats->used--;
#endif
#if MEMP_MEM_MALLOC
LWIP_UNUSED_ARG(desc);
SYS_ARCH_UNPROTECT(old_level);
mem_free(memp);
#else /* MEMP_MEM_MALLOC */
memp->next = *desc->tab;
*desc->tab = memp;
#if MEMP_SANITY_CHECK
LWIP_ASSERT("memp sanity", memp_sanity(desc));
#endif /* MEMP_SANITY_CHECK */
SYS_ARCH_UNPROTECT(old_level);
#endif /* !MEMP_MEM_MALLOC */
}
/**
* Put a custom pool element back into its pool.
*
* @param desc the pool where to put mem
* @param mem the memp element to free
*/
void
memp_free_pool(const struct memp_desc *desc, void *mem)
{
LWIP_ASSERT("invalid pool desc", desc != NULL);
if ((desc == NULL) || (mem == NULL)) {
return;
}
do_memp_free_pool(desc, mem);
}
/**
* Put an element back into its pool.
*
* @param type the pool where to put mem
* @param mem the memp element to free
*/
void
memp_free(memp_t type, void *mem)
{
#ifdef LWIP_HOOK_MEMP_AVAILABLE
struct memp *old_first;
#endif
LWIP_ERROR("memp_free: type < MEMP_MAX", (type < MEMP_MAX), return;);
if (mem == NULL) {
return;
}
#if MEMP_OVERFLOW_CHECK >= 2
memp_overflow_check_all();
#endif /* MEMP_OVERFLOW_CHECK >= 2 */
#ifdef LWIP_HOOK_MEMP_AVAILABLE
old_first = *memp_pools[type]->tab;
#endif
do_memp_free_pool(memp_pools[type], mem);
#ifdef LWIP_HOOK_MEMP_AVAILABLE
if (old_first == NULL) {
LWIP_HOOK_MEMP_AVAILABLE(type);
}
#endif
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+673
View File
@@ -0,0 +1,673 @@
/**
* @file
* Implementation of raw protocol PCBs for low-level handling of
* different types of protocols besides (or overriding) those
* already available in lwIP.<br>
* See also @ref raw_raw
*
* @defgroup raw_raw RAW
* @ingroup callbackstyle_api
* Implementation of raw protocol PCBs for low-level handling of
* different types of protocols besides (or overriding) those
* already available in lwIP.<br>
* @see @ref api
*/
/*
* 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 <adam@sics.se>
*
*/
#include "lwip/opt.h"
#if LWIP_RAW /* don't build if not configured for use in lwipopts.h */
#include "lwip/def.h"
#include "lwip/memp.h"
#include "lwip/ip_addr.h"
#include "lwip/netif.h"
#include "lwip/raw.h"
#include "lwip/priv/raw_priv.h"
#include "lwip/stats.h"
#include "lwip/ip6.h"
#include "lwip/ip6_addr.h"
#include "lwip/inet_chksum.h"
#include <string.h>
/** The list of RAW PCBs */
static struct raw_pcb *raw_pcbs;
static u8_t
raw_input_local_match(struct raw_pcb *pcb, u8_t broadcast)
{
LWIP_UNUSED_ARG(broadcast); /* in IPv6 only case */
/* check if PCB is bound to specific netif */
if ((pcb->netif_idx != NETIF_NO_INDEX) &&
(pcb->netif_idx != netif_get_index(ip_data.current_input_netif))) {
return 0;
}
#if LWIP_IPV4 && LWIP_IPV6
/* Dual-stack: PCBs listening to any IP type also listen to any IP address */
if (IP_IS_ANY_TYPE_VAL(pcb->local_ip)) {
#if IP_SOF_BROADCAST_RECV
if ((broadcast != 0) && !ip_get_option(pcb, SOF_BROADCAST)) {
return 0;
}
#endif /* IP_SOF_BROADCAST_RECV */
return 1;
}
#endif /* LWIP_IPV4 && LWIP_IPV6 */
/* Only need to check PCB if incoming IP version matches PCB IP version */
if (IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ip_current_dest_addr())) {
#if LWIP_IPV4
/* Special case: IPv4 broadcast: receive all broadcasts
* Note: broadcast variable can only be 1 if it is an IPv4 broadcast */
if (broadcast != 0) {
#if IP_SOF_BROADCAST_RECV
if (ip_get_option(pcb, SOF_BROADCAST))
#endif /* IP_SOF_BROADCAST_RECV */
{
if (ip4_addr_isany(ip_2_ip4(&pcb->local_ip))) {
return 1;
}
}
} else
#endif /* LWIP_IPV4 */
/* Handle IPv4 and IPv6: catch all or exact match */
if (ip_addr_isany(&pcb->local_ip) ||
ip_addr_eq(&pcb->local_ip, ip_current_dest_addr())) {
return 1;
}
}
return 0;
}
/**
* Determine if in incoming IP packet is covered by a RAW PCB
* and if so, pass it to a user-provided receive callback function.
*
* Given an incoming IP datagram (as a chain of pbufs) this function
* finds a corresponding RAW PCB and calls the corresponding receive
* callback function.
*
* @param p pbuf to be demultiplexed to a RAW PCB.
* @param inp network interface on which the datagram was received.
* @return - 1 if the packet has been eaten by a RAW PCB receive
* callback function. The caller MAY NOT not reference the
* packet any longer, and MAY NOT call pbuf_free().
* @return - 0 if packet is not eaten (pbuf is still referenced by the
* caller).
*
*/
raw_input_state_t
raw_input(struct pbuf *p, struct netif *inp)
{
struct raw_pcb *pcb, *prev;
s16_t proto;
raw_input_state_t ret = RAW_INPUT_NONE;
u8_t broadcast = ip_addr_isbroadcast(ip_current_dest_addr(), ip_current_netif());
LWIP_UNUSED_ARG(inp);
#if LWIP_IPV6
#if LWIP_IPV4
if (IP_HDR_GET_VERSION(p->payload) == 6)
#endif /* LWIP_IPV4 */
{
struct ip6_hdr *ip6hdr = (struct ip6_hdr *)p->payload;
proto = IP6H_NEXTH(ip6hdr);
}
#if LWIP_IPV4
else
#endif /* LWIP_IPV4 */
#endif /* LWIP_IPV6 */
#if LWIP_IPV4
{
proto = IPH_PROTO((struct ip_hdr *)p->payload);
}
#endif /* LWIP_IPV4 */
prev = NULL;
pcb = raw_pcbs;
/* loop through all raw pcbs until the packet is eaten by one */
/* this allows multiple pcbs to match against the packet by design */
while (pcb != NULL) {
if ((pcb->protocol == proto) && raw_input_local_match(pcb, broadcast) &&
(((pcb->flags & RAW_FLAGS_CONNECTED) == 0) ||
ip_addr_eq(&pcb->remote_ip, ip_current_src_addr()))) {
/* receive callback function available? */
if (pcb->recv != NULL) {
u8_t eaten;
#ifndef LWIP_NOASSERT
void *old_payload = p->payload;
#endif
ret = RAW_INPUT_DELIVERED;
/* the receive callback function did not eat the packet? */
eaten = pcb->recv(pcb->recv_arg, pcb, p, ip_current_src_addr());
if (eaten != 0) {
/* receive function ate the packet */
p = NULL;
if (prev != NULL) {
/* move the pcb to the front of raw_pcbs so that is
found faster next time */
prev->next = pcb->next;
pcb->next = raw_pcbs;
raw_pcbs = pcb;
}
return RAW_INPUT_EATEN;
} else {
/* sanity-check that the receive callback did not alter the pbuf */
LWIP_ASSERT("raw pcb recv callback altered pbuf payload pointer without eating packet",
p->payload == old_payload);
}
}
/* no receive callback function was set for this raw PCB */
}
/* drop the packet */
prev = pcb;
pcb = pcb->next;
}
return ret;
}
/**
* @ingroup raw_raw
* Bind a RAW PCB.
*
* @param pcb RAW PCB to be bound with a local address ipaddr.
* @param ipaddr local IP address to bind with. Use IP4_ADDR_ANY to
* bind to all local interfaces.
*
* @return lwIP error code.
* - ERR_OK. Successful. No error occurred.
* - ERR_USE. The specified IP address is already bound to by
* another RAW PCB.
*
* @see raw_disconnect()
*/
err_t
raw_bind(struct raw_pcb *pcb, const ip_addr_t *ipaddr)
{
LWIP_ASSERT_CORE_LOCKED();
if ((pcb == NULL) || (ipaddr == NULL)) {
return ERR_VAL;
}
ip_addr_set_ipaddr(&pcb->local_ip, ipaddr);
#if LWIP_IPV6 && LWIP_IPV6_SCOPES
/* If the given IP address should have a zone but doesn't, assign one now.
* This is legacy support: scope-aware callers should always provide properly
* zoned source addresses. */
if (IP_IS_V6(&pcb->local_ip) &&
ip6_addr_lacks_zone(ip_2_ip6(&pcb->local_ip), IP6_UNKNOWN)) {
ip6_addr_select_zone(ip_2_ip6(&pcb->local_ip), ip_2_ip6(&pcb->local_ip));
}
#endif /* LWIP_IPV6 && LWIP_IPV6_SCOPES */
return ERR_OK;
}
/**
* @ingroup raw_raw
* Bind an RAW PCB to a specific netif.
* After calling this function, all packets received via this PCB
* are guaranteed to have come in via the specified netif, and all
* outgoing packets will go out via the specified netif.
*
* @param pcb RAW PCB to be bound with netif.
* @param netif netif to bind to. Can be NULL.
*
* @see raw_disconnect()
*/
void
raw_bind_netif(struct raw_pcb *pcb, const struct netif *netif)
{
LWIP_ASSERT_CORE_LOCKED();
if (netif != NULL) {
pcb->netif_idx = netif_get_index(netif);
} else {
pcb->netif_idx = NETIF_NO_INDEX;
}
}
/**
* @ingroup raw_raw
* Connect an RAW PCB. This function is required by upper layers
* of lwip. Using the raw api you could use raw_sendto() instead
*
* This will associate the RAW PCB with the remote address.
*
* @param pcb RAW PCB to be connected with remote address ipaddr and port.
* @param ipaddr remote IP address to connect with.
*
* @return lwIP error code
*
* @see raw_disconnect() and raw_sendto()
*/
err_t
raw_connect(struct raw_pcb *pcb, const ip_addr_t *ipaddr)
{
LWIP_ASSERT_CORE_LOCKED();
if ((pcb == NULL) || (ipaddr == NULL)) {
return ERR_VAL;
}
ip_addr_set_ipaddr(&pcb->remote_ip, ipaddr);
#if LWIP_IPV6 && LWIP_IPV6_SCOPES
/* If the given IP address should have a zone but doesn't, assign one now,
* using the bound address to make a more informed decision when possible. */
if (IP_IS_V6(&pcb->remote_ip) &&
ip6_addr_lacks_zone(ip_2_ip6(&pcb->remote_ip), IP6_UNKNOWN)) {
ip6_addr_select_zone(ip_2_ip6(&pcb->remote_ip), ip_2_ip6(&pcb->local_ip));
}
#endif /* LWIP_IPV6 && LWIP_IPV6_SCOPES */
raw_set_flags(pcb, RAW_FLAGS_CONNECTED);
return ERR_OK;
}
/**
* @ingroup raw_raw
* Disconnect a RAW PCB.
*
* @param pcb the raw pcb to disconnect.
*/
void
raw_disconnect(struct raw_pcb *pcb)
{
LWIP_ASSERT_CORE_LOCKED();
/* reset remote address association */
#if LWIP_IPV4 && LWIP_IPV6
if (IP_IS_ANY_TYPE_VAL(pcb->local_ip)) {
ip_addr_copy(pcb->remote_ip, *IP_ANY_TYPE);
} else {
#endif
ip_addr_set_any(IP_IS_V6_VAL(pcb->remote_ip), &pcb->remote_ip);
#if LWIP_IPV4 && LWIP_IPV6
}
#endif
pcb->netif_idx = NETIF_NO_INDEX;
/* mark PCB as unconnected */
raw_clear_flags(pcb, RAW_FLAGS_CONNECTED);
}
/**
* @ingroup raw_raw
* Set the callback function for received packets that match the
* raw PCB's protocol and binding.
*
* The callback function MUST either
* - eat the packet by calling pbuf_free() and returning non-zero. The
* packet will not be passed to other raw PCBs or other protocol layers.
* - not free the packet, and return zero. The packet will be matched
* against further PCBs and/or forwarded to another protocol layers.
*/
void
raw_recv(struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg)
{
LWIP_ASSERT_CORE_LOCKED();
/* remember recv() callback and user data */
pcb->recv = recv;
pcb->recv_arg = recv_arg;
}
/**
* @ingroup raw_raw
* Send the raw IP packet to the given address. An IP header will be prepended
* to the packet, unless the RAW_FLAGS_HDRINCL flag is set on the PCB. In that
* case, the packet must include an IP header, which will then be sent as is.
*
* @param pcb the raw pcb which to send
* @param p the IP payload to send
* @param ipaddr the destination address of the IP packet
*
*/
err_t
raw_sendto(struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *ipaddr)
{
struct netif *netif;
const ip_addr_t *src_ip;
if ((pcb == NULL) || (ipaddr == NULL) || !IP_ADDR_PCB_VERSION_MATCH(pcb, ipaddr)) {
return ERR_VAL;
}
LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_sendto\n"));
if (pcb->netif_idx != NETIF_NO_INDEX) {
netif = netif_get_by_index(pcb->netif_idx);
} else {
#if LWIP_MULTICAST_TX_OPTIONS
netif = NULL;
if (ip_addr_ismulticast(ipaddr)) {
/* For multicast-destined packets, use the user-provided interface index to
* determine the outgoing interface, if an interface index is set and a
* matching netif can be found. Otherwise, fall back to regular routing. */
netif = netif_get_by_index(pcb->mcast_ifindex);
}
if (netif == NULL)
#endif /* LWIP_MULTICAST_TX_OPTIONS */
{
netif = ip_route(&pcb->local_ip, ipaddr);
}
}
if (netif == NULL) {
LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: No route to "));
ip_addr_debug_print(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ipaddr);
LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("\n"));
return ERR_RTE;
}
if (ip_addr_isany(&pcb->local_ip) || ip_addr_ismulticast(&pcb->local_ip)) {
/* use outgoing network interface IP address as source address */
src_ip = ip_netif_get_local_ip(netif, ipaddr);
#if LWIP_IPV6
if (src_ip == NULL) {
return ERR_RTE;
}
#endif /* LWIP_IPV6 */
} else {
/* use RAW PCB local IP address as source address */
src_ip = &pcb->local_ip;
}
return raw_sendto_if_src(pcb, p, ipaddr, netif, src_ip);
}
/**
* @ingroup raw_raw
* Send the raw IP packet to the given address, using a particular outgoing
* netif and source IP address. An IP header will be prepended to the packet,
* unless the RAW_FLAGS_HDRINCL flag is set on the PCB. In that case, the
* packet must include an IP header, which will then be sent as is.
*
* @param pcb RAW PCB used to send the data
* @param p chain of pbufs to be sent
* @param dst_ip destination IP address
* @param netif the netif used for sending
* @param src_ip source IP address
*/
err_t
raw_sendto_if_src(struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip,
struct netif *netif, const ip_addr_t *src_ip)
{
err_t err;
struct pbuf *q; /* q will be sent down the stack */
u16_t header_size;
u8_t ttl;
LWIP_ASSERT_CORE_LOCKED();
if ((pcb == NULL) || (dst_ip == NULL) || (netif == NULL) || (src_ip == NULL) ||
!IP_ADDR_PCB_VERSION_MATCH(pcb, src_ip) || !IP_ADDR_PCB_VERSION_MATCH(pcb, dst_ip)) {
return ERR_VAL;
}
header_size = (
#if LWIP_IPV4 && LWIP_IPV6
IP_IS_V6(dst_ip) ? IP6_HLEN : IP_HLEN);
#elif LWIP_IPV4
IP_HLEN);
#else
IP6_HLEN);
#endif
/* Handle the HDRINCL option as an exception: none of the code below applies
* to this case, and sending the packet needs to be done differently too. */
if (pcb->flags & RAW_FLAGS_HDRINCL) {
/* A full header *must* be present in the first pbuf of the chain, as the
* output routines may access its fields directly. */
if (p->len < header_size) {
return ERR_VAL;
}
/* @todo multicast loop support, if at all desired for this scenario.. */
NETIF_SET_HINTS(netif, &pcb->netif_hints);
err = ip_output_if_hdrincl(p, src_ip, dst_ip, netif);
NETIF_RESET_HINTS(netif);
return err;
}
/* packet too large to add an IP header without causing an overflow? */
if ((u16_t)(p->tot_len + header_size) < p->tot_len) {
return ERR_MEM;
}
/* not enough space to add an IP header to first pbuf in given p chain? */
if (pbuf_add_header(p, header_size)) {
/* allocate header in new pbuf */
q = pbuf_alloc(PBUF_IP, 0, PBUF_RAM);
/* new header pbuf could not be allocated? */
if (q == NULL) {
LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("raw_sendto: could not allocate header\n"));
return ERR_MEM;
}
if (p->tot_len != 0) {
/* chain header q in front of given pbuf p */
pbuf_chain(q, p);
}
/* { first pbuf q points to header pbuf } */
LWIP_DEBUGF(RAW_DEBUG, ("raw_sendto: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p));
} else {
/* first pbuf q equals given pbuf */
q = p;
if (pbuf_remove_header(q, header_size)) {
LWIP_ASSERT("Can't restore header we just removed!", 0);
return ERR_MEM;
}
}
#if IP_SOF_BROADCAST
if (IP_IS_V4(dst_ip)) {
/* broadcast filter? */
if (!ip_get_option(pcb, SOF_BROADCAST) && ip_addr_isbroadcast(dst_ip, netif)) {
LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb));
/* free any temporary header pbuf allocated by pbuf_header() */
if (q != p) {
pbuf_free(q);
}
return ERR_VAL;
}
}
#endif /* IP_SOF_BROADCAST */
/* Multicast Loop? */
#if LWIP_MULTICAST_TX_OPTIONS
if (((pcb->flags & RAW_FLAGS_MULTICAST_LOOP) != 0) && ip_addr_ismulticast(dst_ip)) {
q->flags |= PBUF_FLAG_MCASTLOOP;
}
#endif /* LWIP_MULTICAST_TX_OPTIONS */
#if LWIP_IPV6
/* If requested, based on the IPV6_CHECKSUM socket option per RFC3542,
compute the checksum and update the checksum in the payload. */
if (IP_IS_V6(dst_ip) && pcb->chksum_reqd) {
u16_t chksum = ip6_chksum_pseudo(p, pcb->protocol, p->tot_len, ip_2_ip6(src_ip), ip_2_ip6(dst_ip));
LWIP_ASSERT("Checksum must fit into first pbuf", p->len >= (pcb->chksum_offset + 2));
SMEMCPY(((u8_t *)p->payload) + pcb->chksum_offset, &chksum, sizeof(u16_t));
}
#endif
/* Determine TTL to use */
#if LWIP_MULTICAST_TX_OPTIONS
ttl = (ip_addr_ismulticast(dst_ip) ? raw_get_multicast_ttl(pcb) : pcb->ttl);
#else /* LWIP_MULTICAST_TX_OPTIONS */
ttl = pcb->ttl;
#endif /* LWIP_MULTICAST_TX_OPTIONS */
NETIF_SET_HINTS(netif, &pcb->netif_hints);
err = ip_output_if(q, src_ip, dst_ip, ttl, pcb->tos, pcb->protocol, netif);
NETIF_RESET_HINTS(netif);
/* did we chain a header earlier? */
if (q != p) {
/* free the header */
pbuf_free(q);
}
return err;
}
/**
* @ingroup raw_raw
* Send the raw IP packet to the address given by raw_connect()
*
* @param pcb the raw pcb which to send
* @param p the IP payload to send
*
*/
err_t
raw_send(struct raw_pcb *pcb, struct pbuf *p)
{
return raw_sendto(pcb, p, &pcb->remote_ip);
}
/**
* @ingroup raw_raw
* Remove an RAW PCB.
*
* @param pcb RAW PCB to be removed. The PCB is removed from the list of
* RAW PCB's and the data structure is freed from memory.
*
* @see raw_new()
*/
void
raw_remove(struct raw_pcb *pcb)
{
struct raw_pcb *pcb2;
LWIP_ASSERT_CORE_LOCKED();
/* pcb to be removed is first in list? */
if (raw_pcbs == pcb) {
/* make list start at 2nd pcb */
raw_pcbs = raw_pcbs->next;
/* pcb not 1st in list */
} else {
for (pcb2 = raw_pcbs; pcb2 != NULL; pcb2 = pcb2->next) {
/* find pcb in raw_pcbs list */
if (pcb2->next != NULL && pcb2->next == pcb) {
/* remove pcb from list */
pcb2->next = pcb->next;
break;
}
}
}
memp_free(MEMP_RAW_PCB, pcb);
}
/**
* @ingroup raw_raw
* Create a RAW PCB.
*
* @return The RAW PCB which was created. NULL if the PCB data structure
* could not be allocated.
*
* @param proto the protocol number of the IPs payload (e.g. IP_PROTO_ICMP)
*
* @see raw_remove()
*/
struct raw_pcb *
raw_new(u8_t proto)
{
struct raw_pcb *pcb;
LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_new\n"));
LWIP_ASSERT_CORE_LOCKED();
pcb = (struct raw_pcb *)memp_malloc(MEMP_RAW_PCB);
/* could allocate RAW PCB? */
if (pcb != NULL) {
/* initialize PCB to all zeroes */
memset(pcb, 0, sizeof(struct raw_pcb));
pcb->protocol = proto;
pcb->ttl = RAW_TTL;
#if LWIP_MULTICAST_TX_OPTIONS
raw_set_multicast_ttl(pcb, RAW_TTL);
#endif /* LWIP_MULTICAST_TX_OPTIONS */
pcb_tci_init(pcb);
pcb->next = raw_pcbs;
raw_pcbs = pcb;
}
return pcb;
}
/**
* @ingroup raw_raw
* Create a RAW PCB for specific IP type.
*
* @return The RAW PCB which was created. NULL if the PCB data structure
* could not be allocated.
*
* @param type IP address type, see @ref lwip_ip_addr_type definitions.
* If you want to listen to IPv4 and IPv6 (dual-stack) packets,
* supply @ref IPADDR_TYPE_ANY as argument and bind to @ref IP_ANY_TYPE.
* @param proto the protocol number (next header) of the IPv6 packet payload
* (e.g. IP6_NEXTH_ICMP6)
*
* @see raw_remove()
*/
struct raw_pcb *
raw_new_ip_type(u8_t type, u8_t proto)
{
struct raw_pcb *pcb;
LWIP_ASSERT_CORE_LOCKED();
pcb = raw_new(proto);
#if LWIP_IPV4 && LWIP_IPV6
if (pcb != NULL) {
IP_SET_TYPE_VAL(pcb->local_ip, type);
IP_SET_TYPE_VAL(pcb->remote_ip, type);
}
#else /* LWIP_IPV4 && LWIP_IPV6 */
LWIP_UNUSED_ARG(type);
#endif /* LWIP_IPV4 && LWIP_IPV6 */
return pcb;
}
/** This function is called from netif.c when address is changed
*
* @param old_addr IP address of the netif before change
* @param new_addr IP address of the netif after change
*/
void raw_netif_ip_addr_changed(const ip_addr_t *old_addr, const ip_addr_t *new_addr)
{
struct raw_pcb *rpcb;
if (!ip_addr_isany(old_addr) && !ip_addr_isany(new_addr)) {
for (rpcb = raw_pcbs; rpcb != NULL; rpcb = rpcb->next) {
/* PCB bound to current local interface address? */
if (ip_addr_eq(&rpcb->local_ip, old_addr)) {
/* The PCB is bound to the old ipaddr and
* is set to bound to the new one instead */
ip_addr_copy(rpcb->local_ip, *new_addr);
}
}
}
}
#endif /* LWIP_RAW */
+168
View File
@@ -0,0 +1,168 @@
/**
* @file
* Statistics 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 <adam@sics.se>
*
*/
#include "lwip/opt.h"
#if LWIP_STATS /* don't build if not configured for use in lwipopts.h */
#include "lwip/def.h"
#include "lwip/stats.h"
#include "lwip/mem.h"
#include "lwip/debug.h"
#include <string.h>
struct stats_ lwip_stats;
void
stats_init(void)
{
#ifdef LWIP_DEBUG
#if MEM_STATS
lwip_stats.mem.name = "MEM";
#endif /* MEM_STATS */
#endif /* LWIP_DEBUG */
}
#if LWIP_STATS_DISPLAY
void
stats_display_proto(struct stats_proto *proto, const char *name)
{
LWIP_PLATFORM_DIAG(("\n%s\n\t", name));
LWIP_PLATFORM_DIAG(("xmit: %"STAT_COUNTER_F"\n\t", proto->xmit));
LWIP_PLATFORM_DIAG(("recv: %"STAT_COUNTER_F"\n\t", proto->recv));
LWIP_PLATFORM_DIAG(("fw: %"STAT_COUNTER_F"\n\t", proto->fw));
LWIP_PLATFORM_DIAG(("drop: %"STAT_COUNTER_F"\n\t", proto->drop));
LWIP_PLATFORM_DIAG(("chkerr: %"STAT_COUNTER_F"\n\t", proto->chkerr));
LWIP_PLATFORM_DIAG(("lenerr: %"STAT_COUNTER_F"\n\t", proto->lenerr));
LWIP_PLATFORM_DIAG(("memerr: %"STAT_COUNTER_F"\n\t", proto->memerr));
LWIP_PLATFORM_DIAG(("rterr: %"STAT_COUNTER_F"\n\t", proto->rterr));
LWIP_PLATFORM_DIAG(("proterr: %"STAT_COUNTER_F"\n\t", proto->proterr));
LWIP_PLATFORM_DIAG(("opterr: %"STAT_COUNTER_F"\n\t", proto->opterr));
LWIP_PLATFORM_DIAG(("err: %"STAT_COUNTER_F"\n\t", proto->err));
LWIP_PLATFORM_DIAG(("cachehit: %"STAT_COUNTER_F"\n", proto->cachehit));
}
#if IGMP_STATS || MLD6_STATS
void
stats_display_igmp(struct stats_igmp *igmp, const char *name)
{
LWIP_PLATFORM_DIAG(("\n%s\n\t", name));
LWIP_PLATFORM_DIAG(("xmit: %"STAT_COUNTER_F"\n\t", igmp->xmit));
LWIP_PLATFORM_DIAG(("recv: %"STAT_COUNTER_F"\n\t", igmp->recv));
LWIP_PLATFORM_DIAG(("drop: %"STAT_COUNTER_F"\n\t", igmp->drop));
LWIP_PLATFORM_DIAG(("chkerr: %"STAT_COUNTER_F"\n\t", igmp->chkerr));
LWIP_PLATFORM_DIAG(("lenerr: %"STAT_COUNTER_F"\n\t", igmp->lenerr));
LWIP_PLATFORM_DIAG(("memerr: %"STAT_COUNTER_F"\n\t", igmp->memerr));
LWIP_PLATFORM_DIAG(("proterr: %"STAT_COUNTER_F"\n\t", igmp->proterr));
LWIP_PLATFORM_DIAG(("rx_v1: %"STAT_COUNTER_F"\n\t", igmp->rx_v1));
LWIP_PLATFORM_DIAG(("rx_group: %"STAT_COUNTER_F"\n\t", igmp->rx_group));
LWIP_PLATFORM_DIAG(("rx_general: %"STAT_COUNTER_F"\n\t", igmp->rx_general));
LWIP_PLATFORM_DIAG(("rx_report: %"STAT_COUNTER_F"\n\t", igmp->rx_report));
LWIP_PLATFORM_DIAG(("tx_join: %"STAT_COUNTER_F"\n\t", igmp->tx_join));
LWIP_PLATFORM_DIAG(("tx_leave: %"STAT_COUNTER_F"\n\t", igmp->tx_leave));
LWIP_PLATFORM_DIAG(("tx_report: %"STAT_COUNTER_F"\n", igmp->tx_report));
}
#endif /* IGMP_STATS || MLD6_STATS */
#if MEM_STATS || MEMP_STATS
void
stats_display_mem(struct stats_mem *mem, const char *name)
{
LWIP_PLATFORM_DIAG(("\nMEM %s\n\t", name));
LWIP_PLATFORM_DIAG(("avail: %"MEM_SIZE_F"\n\t", mem->avail));
LWIP_PLATFORM_DIAG(("used: %"MEM_SIZE_F"\n\t", mem->used));
LWIP_PLATFORM_DIAG(("max: %"MEM_SIZE_F"\n\t", mem->max));
LWIP_PLATFORM_DIAG(("err: %"STAT_COUNTER_F"\n", mem->err));
}
#if MEMP_STATS
void
stats_display_memp(struct stats_mem *mem, int idx)
{
if (idx < MEMP_MAX) {
stats_display_mem(mem, mem->name);
}
}
#endif /* MEMP_STATS */
#endif /* MEM_STATS || MEMP_STATS */
#if SYS_STATS
void
stats_display_sys(struct stats_sys *sys)
{
LWIP_PLATFORM_DIAG(("\nSYS\n\t"));
LWIP_PLATFORM_DIAG(("sem.used: %"STAT_COUNTER_F"\n\t", sys->sem.used));
LWIP_PLATFORM_DIAG(("sem.max: %"STAT_COUNTER_F"\n\t", sys->sem.max));
LWIP_PLATFORM_DIAG(("sem.err: %"STAT_COUNTER_F"\n\t", sys->sem.err));
LWIP_PLATFORM_DIAG(("mutex.used: %"STAT_COUNTER_F"\n\t", sys->mutex.used));
LWIP_PLATFORM_DIAG(("mutex.max: %"STAT_COUNTER_F"\n\t", sys->mutex.max));
LWIP_PLATFORM_DIAG(("mutex.err: %"STAT_COUNTER_F"\n\t", sys->mutex.err));
LWIP_PLATFORM_DIAG(("mbox.used: %"STAT_COUNTER_F"\n\t", sys->mbox.used));
LWIP_PLATFORM_DIAG(("mbox.max: %"STAT_COUNTER_F"\n\t", sys->mbox.max));
LWIP_PLATFORM_DIAG(("mbox.err: %"STAT_COUNTER_F"\n", sys->mbox.err));
}
#endif /* SYS_STATS */
void
stats_display(void)
{
s16_t i;
LINK_STATS_DISPLAY();
ETHARP_STATS_DISPLAY();
IPFRAG_STATS_DISPLAY();
IP6_FRAG_STATS_DISPLAY();
IP_STATS_DISPLAY();
ND6_STATS_DISPLAY();
IP6_STATS_DISPLAY();
IGMP_STATS_DISPLAY();
MLD6_STATS_DISPLAY();
ICMP_STATS_DISPLAY();
ICMP6_STATS_DISPLAY();
UDP_STATS_DISPLAY();
TCP_STATS_DISPLAY();
MEM_STATS_DISPLAY();
for (i = 0; i < MEMP_MAX; i++) {
MEMP_STATS_DISPLAY(i);
}
SYS_STATS_DISPLAY();
}
#endif /* LWIP_STATS_DISPLAY */
#endif /* LWIP_STATS */
+148
View File
@@ -0,0 +1,148 @@
/**
* @file
* lwIP Operating System abstraction
*
*/
/*
* 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 <adam@sics.se>
*
*/
/**
* @defgroup sys_layer Porting (system abstraction layer)
* @ingroup lwip
*
* @defgroup sys_os OS abstraction layer
* @ingroup sys_layer
* No need to implement functions in this section in NO_SYS mode.
* The OS-specific code should be implemented in arch/sys_arch.h
* and sys_arch.c of your port.
*
* The operating system emulation layer provides a common interface
* between the lwIP code and the underlying operating system kernel. The
* general idea is that porting lwIP to new architectures requires only
* small changes to a few header files and a new sys_arch
* implementation. It is also possible to do a sys_arch implementation
* that does not rely on any underlying operating system.
*
* The sys_arch provides semaphores, mailboxes and mutexes to lwIP. For the full
* lwIP functionality, multiple threads support can be implemented in the
* sys_arch, but this is not required for the basic lwIP
* functionality. Timer scheduling is implemented in lwIP, but can be implemented
* by the sys_arch port (LWIP_TIMERS_CUSTOM==1).
*
* In addition to the source file providing the functionality of sys_arch,
* the OS emulation layer must provide several header files defining
* macros used throughout lwip. The files required and the macros they
* must define are listed below the sys_arch description.
*
* Since lwIP 1.4.0, semaphore, mutexes and mailbox functions are prototyped in a way that
* allows both using pointers or actual OS structures to be used. This way, memory
* required for such types can be either allocated in place (globally or on the
* stack) or on the heap (allocated internally in the "*_new()" functions).
*
* Note:
* -----
* Be careful with using mem_malloc() in sys_arch. When malloc() refers to
* mem_malloc() you can run into a circular function call problem. In mem.c
* mem_init() tries to allocate a semaphore using mem_malloc, which of course
* can't be performed when sys_arch uses mem_malloc.
*
* @defgroup sys_sem Semaphores
* @ingroup sys_os
* Semaphores can be either counting or binary - lwIP works with both
* kinds.
* Semaphores are represented by the type "sys_sem_t" which is typedef'd
* in the sys_arch.h file. Mailboxes are equivalently represented by the
* type "sys_mbox_t". Mutexes are represented by the type "sys_mutex_t".
* lwIP does not place any restrictions on how these types are represented
* internally.
*
* @defgroup sys_mutex Mutexes
* @ingroup sys_os
* Mutexes are recommended to correctly handle priority inversion,
* especially if you use LWIP_CORE_LOCKING .
*
* @defgroup sys_mbox Mailboxes
* @ingroup sys_os
* Mailboxes should be implemented as a queue which allows multiple messages
* to be posted (implementing as a rendez-vous point where only one message can be
* posted at a time can have a highly negative impact on performance). A message
* in a mailbox is just a pointer, nothing more.
*
* @defgroup sys_time Time
* @ingroup sys_layer
*
* @defgroup sys_prot Critical sections
* @ingroup sys_layer
* Used to protect short regions of code against concurrent access.
* - Your system is a bare-metal system (probably with an RTOS)
* and interrupts are under your control:
* Implement this as LockInterrupts() / UnlockInterrupts()
* - Your system uses an RTOS with deferred interrupt handling from a
* worker thread: Implement as a global mutex or lock/unlock scheduler
* - Your system uses a high-level OS with e.g. POSIX signals:
* Implement as a global mutex
*
* @defgroup sys_misc Misc
* @ingroup sys_os
*/
#include "lwip/opt.h"
#include "lwip/sys.h"
/* Most of the functions defined in sys.h must be implemented in the
* architecture-dependent file sys_arch.c */
#if !NO_SYS
#ifndef sys_msleep
/**
* Sleep for some ms. Timeouts are NOT processed while sleeping.
*
* @param ms number of milliseconds to sleep
*/
void
sys_msleep(u32_t ms)
{
if (ms > 0) {
sys_sem_t delaysem;
err_t err = sys_sem_new(&delaysem, 0);
if (err == ERR_OK) {
sys_arch_sem_wait(&delaysem, ms);
sys_sem_free(&delaysem);
}
}
}
#endif /* sys_msleep */
#endif /* !NO_SYS */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+451
View File
@@ -0,0 +1,451 @@
/**
* @file
* Stack-internal timers implementation.
* This file includes timer callbacks for stack-internal timers as well as
* functions to set up or stop timers and check for expired timers.
*
*/
/*
* 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 <adam@sics.se>
* Simon Goldschmidt
*
*/
#include "lwip/opt.h"
#include "lwip/timeouts.h"
#include "lwip/priv/tcp_priv.h"
#include "lwip/def.h"
#include "lwip/memp.h"
#include "lwip/priv/tcpip_priv.h"
#include "lwip/ip4_frag.h"
#include "lwip/etharp.h"
#include "lwip/dhcp.h"
#include "lwip/acd.h"
#include "lwip/igmp.h"
#include "lwip/dns.h"
#include "lwip/nd6.h"
#include "lwip/ip6_frag.h"
#include "lwip/mld6.h"
#include "lwip/dhcp6.h"
#include "lwip/sys.h"
#include "lwip/pbuf.h"
#if LWIP_DEBUG_TIMERNAMES
#define HANDLER(x) x, #x
#else /* LWIP_DEBUG_TIMERNAMES */
#define HANDLER(x) x
#endif /* LWIP_DEBUG_TIMERNAMES */
#define LWIP_MAX_TIMEOUT 0x7fffffff
/* Check if timer's expiry time is greater than time and care about u32_t wraparounds */
#define TIME_LESS_THAN(t, compare_to) ( (((u32_t)((t)-(compare_to))) > LWIP_MAX_TIMEOUT) ? 1 : 0 )
/** This array contains all stack-internal cyclic timers. To get the number of
* timers, use LWIP_ARRAYSIZE() */
const struct lwip_cyclic_timer lwip_cyclic_timers[] = {
#if LWIP_TCP
/* The TCP timer is a special case: it does not have to run always and
is triggered to start from TCP using tcp_timer_needed() */
{TCP_TMR_INTERVAL, HANDLER(tcp_tmr)},
#endif /* LWIP_TCP */
#if LWIP_IPV4
#if IP_REASSEMBLY
{IP_TMR_INTERVAL, HANDLER(ip_reass_tmr)},
#endif /* IP_REASSEMBLY */
#if LWIP_ARP
{ARP_TMR_INTERVAL, HANDLER(etharp_tmr)},
#endif /* LWIP_ARP */
#if LWIP_DHCP
{DHCP_COARSE_TIMER_MSECS, HANDLER(dhcp_coarse_tmr)},
{DHCP_FINE_TIMER_MSECS, HANDLER(dhcp_fine_tmr)},
#endif /* LWIP_DHCP */
#if LWIP_ACD
{ACD_TMR_INTERVAL, HANDLER(acd_tmr)},
#endif /* LWIP_ACD */
#if LWIP_IGMP
{IGMP_TMR_INTERVAL, HANDLER(igmp_tmr)},
#endif /* LWIP_IGMP */
#endif /* LWIP_IPV4 */
#if LWIP_DNS
{DNS_TMR_INTERVAL, HANDLER(dns_tmr)},
#endif /* LWIP_DNS */
#if LWIP_IPV6
{ND6_TMR_INTERVAL, HANDLER(nd6_tmr)},
#if LWIP_IPV6_REASS
{IP6_REASS_TMR_INTERVAL, HANDLER(ip6_reass_tmr)},
#endif /* LWIP_IPV6_REASS */
#if LWIP_IPV6_MLD
{MLD6_TMR_INTERVAL, HANDLER(mld6_tmr)},
#endif /* LWIP_IPV6_MLD */
#if LWIP_IPV6_DHCP6
{DHCP6_TIMER_MSECS, HANDLER(dhcp6_tmr)},
#endif /* LWIP_IPV6_DHCP6 */
#endif /* LWIP_IPV6 */
};
const int lwip_num_cyclic_timers = LWIP_ARRAYSIZE(lwip_cyclic_timers);
#if LWIP_TIMERS && !LWIP_TIMERS_CUSTOM
/** The one and only timeout list */
static struct sys_timeo *next_timeout;
static u32_t current_timeout_due_time;
#if LWIP_TESTMODE
struct sys_timeo**
sys_timeouts_get_next_timeout(void)
{
return &next_timeout;
}
#endif
#if LWIP_TCP
/** global variable that shows if the tcp timer is currently scheduled or not */
static int tcpip_tcp_timer_active;
/**
* Timer callback function that calls tcp_tmr() and reschedules itself.
*
* @param arg unused argument
*/
static void
tcpip_tcp_timer(void *arg)
{
LWIP_UNUSED_ARG(arg);
/* call TCP timer handler */
tcp_tmr();
/* timer still needed? */
if (tcp_active_pcbs || tcp_tw_pcbs) {
/* restart timer */
sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL);
} else {
/* disable timer */
tcpip_tcp_timer_active = 0;
}
}
/**
* Called from TCP_REG when registering a new PCB:
* the reason is to have the TCP timer only running when
* there are active (or time-wait) PCBs.
*/
void
tcp_timer_needed(void)
{
LWIP_ASSERT_CORE_LOCKED();
/* timer is off but needed again? */
if (!tcpip_tcp_timer_active && (tcp_active_pcbs || tcp_tw_pcbs)) {
/* enable and start timer */
tcpip_tcp_timer_active = 1;
sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL);
}
}
#endif /* LWIP_TCP */
static void
#if LWIP_DEBUG_TIMERNAMES
sys_timeout_abs(u32_t abs_time, sys_timeout_handler handler, void *arg, const char *handler_name)
#else /* LWIP_DEBUG_TIMERNAMES */
sys_timeout_abs(u32_t abs_time, sys_timeout_handler handler, void *arg)
#endif
{
struct sys_timeo *timeout, *t;
timeout = (struct sys_timeo *)memp_malloc(MEMP_SYS_TIMEOUT);
if (timeout == NULL) {
LWIP_ASSERT("sys_timeout: timeout != NULL, pool MEMP_SYS_TIMEOUT is empty", timeout != NULL);
return;
}
timeout->next = NULL;
timeout->h = handler;
timeout->arg = arg;
timeout->time = abs_time;
#if LWIP_DEBUG_TIMERNAMES
timeout->handler_name = handler_name;
LWIP_DEBUGF(TIMERS_DEBUG, ("sys_timeout: %p abs_time=%"U32_F" handler=%s arg=%p\n",
(void *)timeout, abs_time, handler_name, (void *)arg));
#endif /* LWIP_DEBUG_TIMERNAMES */
if (next_timeout == NULL) {
next_timeout = timeout;
return;
}
if (TIME_LESS_THAN(timeout->time, next_timeout->time)) {
timeout->next = next_timeout;
next_timeout = timeout;
} else {
for (t = next_timeout; t != NULL; t = t->next) {
if ((t->next == NULL) || TIME_LESS_THAN(timeout->time, t->next->time)) {
timeout->next = t->next;
t->next = timeout;
break;
}
}
}
}
/**
* Timer callback function that calls cyclic->handler() and reschedules itself.
*
* @param arg unused argument
*/
#if !LWIP_TESTMODE
static
#endif
void
lwip_cyclic_timer(void *arg)
{
u32_t now;
u32_t next_timeout_time;
const struct lwip_cyclic_timer *cyclic = (const struct lwip_cyclic_timer *)arg;
#if LWIP_DEBUG_TIMERNAMES
LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: %s()\n", cyclic->handler_name));
#endif
cyclic->handler();
now = sys_now();
next_timeout_time = (u32_t)(current_timeout_due_time + cyclic->interval_ms); /* overflow handled by TIME_LESS_THAN macro */
if (TIME_LESS_THAN(next_timeout_time, now)) {
/* timer would immediately expire again -> "overload" -> restart without any correction */
#if LWIP_DEBUG_TIMERNAMES
sys_timeout_abs((u32_t)(now + cyclic->interval_ms), lwip_cyclic_timer, arg, cyclic->handler_name);
#else
sys_timeout_abs((u32_t)(now + cyclic->interval_ms), lwip_cyclic_timer, arg);
#endif
} else {
/* correct cyclic interval with handler execution delay and sys_check_timeouts jitter */
#if LWIP_DEBUG_TIMERNAMES
sys_timeout_abs(next_timeout_time, lwip_cyclic_timer, arg, cyclic->handler_name);
#else
sys_timeout_abs(next_timeout_time, lwip_cyclic_timer, arg);
#endif
}
}
/** Initialize this module */
void sys_timeouts_init(void)
{
size_t i;
/* tcp_tmr() at index 0 is started on demand */
for (i = (LWIP_TCP ? 1 : 0); i < LWIP_ARRAYSIZE(lwip_cyclic_timers); i++) {
/* we have to cast via size_t to get rid of const warning
(this is OK as cyclic_timer() casts back to const* */
sys_timeout(lwip_cyclic_timers[i].interval_ms, lwip_cyclic_timer, LWIP_CONST_CAST(void *, &lwip_cyclic_timers[i]));
}
}
/**
* Create a one-shot timer (aka timeout). Timeouts are processed in the
* following cases:
* - while waiting for a message using sys_timeouts_mbox_fetch()
* - by calling sys_check_timeouts() (NO_SYS==1 only)
*
* @param msecs time in milliseconds after that the timer should expire
* @param handler callback function to call when msecs have elapsed
* @param arg argument to pass to the callback function
*/
#if LWIP_DEBUG_TIMERNAMES
void
sys_timeout_debug(u32_t msecs, sys_timeout_handler handler, void *arg, const char *handler_name)
#else /* LWIP_DEBUG_TIMERNAMES */
void
sys_timeout(u32_t msecs, sys_timeout_handler handler, void *arg)
#endif /* LWIP_DEBUG_TIMERNAMES */
{
u32_t next_timeout_time;
LWIP_ASSERT_CORE_LOCKED();
LWIP_ASSERT("Timeout time too long, max is LWIP_UINT32_MAX/4 msecs", msecs <= (LWIP_UINT32_MAX / 4));
next_timeout_time = (u32_t)(sys_now() + msecs); /* overflow handled by TIME_LESS_THAN macro */
#if LWIP_DEBUG_TIMERNAMES
sys_timeout_abs(next_timeout_time, handler, arg, handler_name);
#else
sys_timeout_abs(next_timeout_time, handler, arg);
#endif
}
/**
* Go through timeout list (for this task only) and remove the first matching
* entry (subsequent entries remain untouched), even though the timeout has not
* triggered yet.
*
* @param handler callback function that would be called by the timeout
* @param arg callback argument that would be passed to handler
*/
void
sys_untimeout(sys_timeout_handler handler, void *arg)
{
struct sys_timeo *prev_t, *t;
LWIP_ASSERT_CORE_LOCKED();
if (next_timeout == NULL) {
return;
}
for (t = next_timeout, prev_t = NULL; t != NULL; prev_t = t, t = t->next) {
if ((t->h == handler) && (t->arg == arg)) {
/* We have a match */
/* Unlink from previous in list */
if (prev_t == NULL) {
next_timeout = t->next;
} else {
prev_t->next = t->next;
}
memp_free(MEMP_SYS_TIMEOUT, t);
return;
}
}
return;
}
/**
* @ingroup lwip_nosys
* Handle timeouts for NO_SYS==1 (i.e. without using
* tcpip_thread/sys_timeouts_mbox_fetch(). Uses sys_now() to call timeout
* handler functions when timeouts expire.
*
* Must be called periodically from your main loop.
*/
void
sys_check_timeouts(void)
{
u32_t now;
LWIP_ASSERT_CORE_LOCKED();
/* Process only timers expired at the start of the function. */
now = sys_now();
do {
struct sys_timeo *tmptimeout;
sys_timeout_handler handler;
void *arg;
PBUF_CHECK_FREE_OOSEQ();
tmptimeout = next_timeout;
if (tmptimeout == NULL) {
return;
}
if (TIME_LESS_THAN(now, tmptimeout->time)) {
return;
}
/* Timeout has expired */
next_timeout = tmptimeout->next;
handler = tmptimeout->h;
arg = tmptimeout->arg;
current_timeout_due_time = tmptimeout->time;
#if LWIP_DEBUG_TIMERNAMES
if (handler != NULL) {
LWIP_DEBUGF(TIMERS_DEBUG, ("sct calling h=%s t=%"U32_F" arg=%p\n",
tmptimeout->handler_name, sys_now() - tmptimeout->time, arg));
}
#endif /* LWIP_DEBUG_TIMERNAMES */
memp_free(MEMP_SYS_TIMEOUT, tmptimeout);
if (handler != NULL) {
handler(arg);
}
LWIP_TCPIP_THREAD_ALIVE();
/* Repeat until all expired timers have been called */
} while (1);
}
/** Rebase the timeout times to the current time.
* This is necessary if sys_check_timeouts() hasn't been called for a long
* time (e.g. while saving energy) to prevent all timer functions of that
* period being called.
*/
void
sys_restart_timeouts(void)
{
u32_t now;
u32_t base;
struct sys_timeo *t;
if (next_timeout == NULL) {
return;
}
now = sys_now();
base = next_timeout->time;
for (t = next_timeout; t != NULL; t = t->next) {
t->time = (t->time - base) + now;
}
}
/** Return the time left before the next timeout is due. If no timeouts are
* enqueued, returns 0xffffffff
*/
u32_t
sys_timeouts_sleeptime(void)
{
u32_t now;
LWIP_ASSERT_CORE_LOCKED();
if (next_timeout == NULL) {
return SYS_TIMEOUTS_SLEEPTIME_INFINITE;
}
now = sys_now();
if (TIME_LESS_THAN(next_timeout->time, now)) {
return 0;
} else {
u32_t ret = (u32_t)(next_timeout->time - now);
LWIP_ASSERT("invalid sleeptime", ret <= LWIP_MAX_TIMEOUT);
return ret;
}
}
#else /* LWIP_TIMERS && !LWIP_TIMERS_CUSTOM */
/* Satisfy the TCP code which calls this function */
void
tcp_timer_needed(void)
{
}
#endif /* LWIP_TIMERS && !LWIP_TIMERS_CUSTOM */
File diff suppressed because it is too large Load Diff
+100
View File
@@ -0,0 +1,100 @@
/**
* @file cc.h
* @brief Compiler/Platform specific definitions for LwIP on STM32 + FreeRTOS
*/
#ifndef __CC_H__
#define __CC_H__
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "lwip/errno.h"
#ifdef __cplusplus
extern "C" {
#endif
void lwip_platform_assert(const char *msg, const char *file, int line);
#ifdef __cplusplus
}
#endif
/*
* FreeRTOSConfig.h injects a global sys_now() macro. App sources that include
* FreeRTOS before lwIP would otherwise corrupt this declaration and the type
* aliases below. Remove the macro so lwIP uses the real port function.
*/
#ifdef sys_now
#undef sys_now
#endif
/* Use standard integer types from stdint.h */
#define LWIP_NO_STDINT_H 0
/* Define basic types for LwIP */
typedef uint8_t u8_t;
typedef int8_t s8_t;
typedef uint16_t u16_t;
typedef int16_t s16_t;
typedef uint32_t u32_t;
typedef int32_t s32_t;
typedef uintptr_t mem_ptr_t;
/* Byte order - ARM Cortex-M is little endian */
#ifndef BYTE_ORDER
#define BYTE_ORDER LITTLE_ENDIAN
#endif
/* Compiler specific structure packing */
#if defined (__ICCARM__)
/* IAR Compiler */
#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) || defined (__ARMCC_VERSION)
/* ARM Compiler (Keil MDK) */
#define PACK_STRUCT_BEGIN __packed
#define PACK_STRUCT_STRUCT
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x
#elif defined (__GNUC__)
/* GNU Compiler */
#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_STRUCT __attribute__ ((packed))
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x
#else
/* Default */
#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_STRUCT
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x
#endif
/* Platform specific diagnostic output */
#ifndef LWIP_PLATFORM_DIAG
#define LWIP_PLATFORM_DIAG(x) do { printf x; } while(0)
#endif
/* Platform specific assertion handling */
#ifndef LWIP_PLATFORM_ASSERT
#define LWIP_PLATFORM_ASSERT(x) do { \
lwip_platform_assert((x), __FILE__, __LINE__); \
} while(0)
#endif
/* Get current time in milliseconds (provided by sys_arch.c) */
extern u32_t sys_now(void);
/* Random number generator */
#ifndef LWIP_RAND
#define LWIP_RAND() ((u32_t)rand())
#endif
#endif /* __CC_H__ */
+327
View File
@@ -0,0 +1,327 @@
/**
* @file lwipopts.h
* @brief LwIP configuration for STM32F103RCT6 + FreeRTOS + CH390 Ethernet
*
* Path A: NO_SYS=0, netconn API, multi-task TCP architecture.
* Optimized for STM32F103RCT6 (48KB SRAM) with pin-to-pin backup STM32F103RDT6 (64KB SRAM).
*
* Key design decisions:
* - netconn API for thread-safe multi-connection TCP
* - tcpip_thread handles netconn API and timers
* - core locking lets the poll task process RX packets synchronously
* - Conservative memory footprint: target ~16KB for lwIP
*/
#ifndef LWIP_LWIPOPTS_H
#define LWIP_LWIPOPTS_H
/*-----------------------------------------------------------------------------
* Platform and OS Options
*---------------------------------------------------------------------------*/
/* Use FreeRTOS - this enables the sequential API (netconn, sockets) */
#define NO_SYS 0
/* Enable netconn API (primary), disable socket API to save RAM */
#define LWIP_SOCKET 0
#define LWIP_NETCONN 1
#define LWIP_NETIF_API 1
/* Core locking: process netif RX synchronously instead of consuming TCPIP_MSG_INPKT slots. */
#define LWIP_TCPIP_CORE_LOCKING 1
#define LWIP_TCPIP_CORE_LOCKING_INPUT 1
/* Critical section protection */
#define SYS_LIGHTWEIGHT_PROT 1
/* Use FreeRTOS memory allocation */
#define MEM_LIBC_MALLOC 0
#define MEMP_MEM_MALLOC 0
/* Let lwIP provide the errno values used by sockets/netconn. */
#define LWIP_PROVIDE_ERRNO 1
/*-----------------------------------------------------------------------------
* Memory Configuration (optimized for STM32F103RCT6 with 48KB SRAM)
*---------------------------------------------------------------------------*/
/* Memory alignment (ARM Cortex-M3 = 4 byte alignment) */
#define MEM_ALIGNMENT 4
/* Heap size for dynamic memory allocation.
* With netconn: larger heap needed for netbuf allocation and connection management.
* 8KB provides headroom for 4 concurrent TCP connections. */
#define MEM_SIZE (7 * 1024)
/* Number of pbufs in pool.
* RX is processed synchronously under the core lock, so a small pool is sufficient. */
#define PBUF_POOL_SIZE 8
/* Size of each pbuf in pool (must hold one Ethernet frame) */
#define PBUF_POOL_BUFSIZE LWIP_MEM_ALIGN_SIZE(TCP_MSS + 40 + PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN)
/* Number of memp struct pbufs */
#define MEMP_NUM_PBUF 8
/* Number of raw PCBs */
#define MEMP_NUM_RAW_PCB 2
/* Number of UDP PCBs */
#define MEMP_NUM_UDP_PCB 4
/* Number of simultaneously active TCP connections */
#define MEMP_NUM_TCP_PCB 4
/* Number of listening TCP connections */
#define MEMP_NUM_TCP_PCB_LISTEN 2
/* Number of simultaneously queued TCP segments
* Increased for 4 concurrent connections */
#define MEMP_NUM_TCP_SEG 12
/* Number of simultaneously active timeouts */
#define MEMP_NUM_SYS_TIMEOUT 12
/* Number of netbufs (for netconn API, one per pending recv) */
#define MEMP_NUM_NETBUF 8
/* Number of netconns: 2 listeners + 2 accepted + 2 clients + 2 margin = 8 */
#define MEMP_NUM_NETCONN 8
/* TCPIP message queue size (must be >= max simultaneous API calls) */
#define MEMP_NUM_TCPIP_MSG_API 8
#define MEMP_NUM_TCPIP_MSG_INPKT 8
/*-----------------------------------------------------------------------------
* IP Configuration
*---------------------------------------------------------------------------*/
#define LWIP_IPV4 1
#define LWIP_IPV6 0
/* No IP forwarding (single interface device) */
#define IP_FORWARD 0
/* IP fragment reassembly */
#define IP_REASSEMBLY 0
#define IP_FRAG 0
/* IP options processing */
#define IP_OPTIONS_ALLOWED 1
/*-----------------------------------------------------------------------------
* ICMP Configuration
*---------------------------------------------------------------------------*/
#define LWIP_ICMP 1
#define ICMP_TTL 255
/*-----------------------------------------------------------------------------
* ARP Configuration
*---------------------------------------------------------------------------*/
#define LWIP_ARP 1
#define ARP_TABLE_SIZE 10
#define ARP_QUEUEING 1
#define ETHARP_SUPPORT_STATIC_ENTRIES 1
/*-----------------------------------------------------------------------------
* DHCP Configuration
*---------------------------------------------------------------------------*/
#define LWIP_DHCP 0 /* Static IP only */
#define DHCP_DOES_ARP_CHECK 0
/*-----------------------------------------------------------------------------
* UDP Configuration
*---------------------------------------------------------------------------*/
#define LWIP_UDP 0 /* UDP not used in this project */
/*-----------------------------------------------------------------------------
* TCP Configuration (optimized for transparent transmission)
*---------------------------------------------------------------------------*/
#define LWIP_TCP 1
#define TCP_TTL 255
/* TCP Maximum Segment Size */
#define TCP_MSS 536 /* Conservative value for compatibility */
/* TCP sender buffer space - increased for bridge throughput */
#define TCP_SND_BUF (2 * TCP_MSS)
/* TCP sender buffer space (pbufs) */
#define TCP_SND_QUEUELEN ((4 * (TCP_SND_BUF) + (TCP_MSS - 1)) / (TCP_MSS))
/*
* Temporary phase-1 exception: current TCP queue sizing trips lwIP's compile-time
* sanity guard on this memory-constrained target. Keep the bypass in project
* configuration instead of patching lwIP core source logic.
*/
#define LWIP_DISABLE_TCP_SANITY_CHECKS 1
/* TCP receive window - increased for bridge throughput */
#define TCP_WND (2 * TCP_MSS)
/* TCP writable space threshold */
#define TCP_SNDLOWAT LWIP_MIN(LWIP_MAX(((TCP_SND_BUF)/2), (2 * TCP_MSS) + 1), (TCP_SND_BUF) - 1)
/* Enable TCP keepalive */
#define LWIP_TCP_KEEPALIVE 1
/* TCP segment queue handling */
#define TCP_QUEUE_OOSEQ 0 /* Disable out-of-order segment queuing to save RAM */
/* Maximum number of retransmissions */
#define TCP_MAXRTX 12
#define TCP_SYNMAXRTX 6
/* TCP listen backlog */
#define TCP_LISTEN_BACKLOG 1
/* TCP timestamp option */
#define LWIP_TCP_TIMESTAMPS 0
/*-----------------------------------------------------------------------------
* RAW API (used for ping, etc.)
*---------------------------------------------------------------------------*/
#define LWIP_RAW 1
/*-----------------------------------------------------------------------------
* DNS Configuration
*---------------------------------------------------------------------------*/
#define LWIP_DNS 0 /* Disable DNS to save RAM */
/*-----------------------------------------------------------------------------
* IGMP Configuration
*---------------------------------------------------------------------------*/
#define LWIP_IGMP 0 /* Disable IGMP to save RAM */
/*-----------------------------------------------------------------------------
* Callback Configuration
*---------------------------------------------------------------------------*/
#define LWIP_NETIF_STATUS_CALLBACK 0
#define LWIP_NETIF_LINK_CALLBACK 0
/*-----------------------------------------------------------------------------
* Checksum Configuration
*---------------------------------------------------------------------------*/
/* Use software checksums (CH390 doesn't have checksum offload) */
#define CHECKSUM_GEN_IP 1
#define CHECKSUM_GEN_UDP 1
#define CHECKSUM_GEN_TCP 1
#define CHECKSUM_GEN_ICMP 1
#define CHECKSUM_CHECK_IP 1
#define CHECKSUM_CHECK_UDP 1
#define CHECKSUM_CHECK_TCP 1
#define CHECKSUM_CHECK_ICMP 1
/*-----------------------------------------------------------------------------
* Statistics (disabled to save RAM)
*---------------------------------------------------------------------------*/
#define LWIP_STATS 0
#define LWIP_STATS_DISPLAY 0
/*-----------------------------------------------------------------------------
* Debug Options (disabled for production)
*---------------------------------------------------------------------------*/
#define LWIP_DEBUG 0
#if LWIP_DEBUG
#define LWIP_DBG_MIN_LEVEL LWIP_DBG_LEVEL_ALL
#define LWIP_DBG_TYPES_ON LWIP_DBG_ON
#define ETHARP_DEBUG LWIP_DBG_OFF
#define NETIF_DEBUG LWIP_DBG_OFF
#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 TIMERS_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 DHCP_DEBUG LWIP_DBG_OFF
#endif /* LWIP_DEBUG */
/*-----------------------------------------------------------------------------
* FreeRTOS Specific Options
*---------------------------------------------------------------------------*/
/* Task stack sizes */
#define TCPIP_THREAD_STACKSIZE 512
#define TCPIP_THREAD_PRIO (configMAX_PRIORITIES - 2)
#define DEFAULT_THREAD_STACKSIZE 256
#define DEFAULT_THREAD_PRIO (configMAX_PRIORITIES - 3)
/* Mailbox sizes */
#define TCPIP_MBOX_SIZE 12
#define DEFAULT_RAW_RECVMBOX_SIZE 4
#define DEFAULT_UDP_RECVMBOX_SIZE 4
#define DEFAULT_TCP_RECVMBOX_SIZE 6
#define DEFAULT_ACCEPTMBOX_SIZE 6
/* Thread name length */
#define LWIP_NETCONN_SEM_PER_THREAD 1
/*-----------------------------------------------------------------------------
* Ethernet Specific
*---------------------------------------------------------------------------*/
/* Ethernet MTU */
#define LWIP_ETHERNET 1
/* Link layer header overhead */
#define PBUF_LINK_HLEN 14 /* Ethernet header size */
#define PBUF_LINK_ENCAPSULATION_HLEN 0
/* Use static Ethernet address (configured at runtime) */
#define LWIP_NETIF_HOSTNAME 1
/*-----------------------------------------------------------------------------
* Socket Options
*---------------------------------------------------------------------------*/
#define LWIP_SO_SNDTIMEO 1
#define LWIP_SO_RCVTIMEO 1
#define LWIP_SO_RCVBUF 0
#define SO_REUSE 1
/*-----------------------------------------------------------------------------
* Additional Options
*---------------------------------------------------------------------------*/
/* Enable loop interface for testing */
#define LWIP_HAVE_LOOPIF 0
#define LWIP_NETIF_LOOPBACK 0
/* Random number generator (required for some TCP operations) */
#define LWIP_RAND() ((uint32_t)rand())
#endif /* LWIP_LWIPOPTS_H */
+120
View File
@@ -0,0 +1,120 @@
/**
* @file sys_arch.h
* @brief LwIP system architecture for FreeRTOS
*/
#ifndef __SYS_ARCH_H__
#define __SYS_ARCH_H__
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "lwip/arch.h"
#include <stdlib.h>
/*
* FreeRTOSConfig.h currently injects lwIP-related helper macros globally.
* Those overrides break the normal lwIP sys.h/sys_arch contract by replacing
* function declarations such as sys_now() with object-like macros.
* Undefine them here so lwIP uses the port's real sys_arch implementation.
*/
#ifdef sys_now
#undef sys_now
#endif
#ifdef sys_arch_protect
#undef sys_arch_protect
#endif
#ifdef sys_arch_unprotect
#undef sys_arch_unprotect
#endif
#ifdef SYS_ARCH_DECL_PROTECT
#undef SYS_ARCH_DECL_PROTECT
#endif
#ifdef SYS_ARCH_PROTECT
#undef SYS_ARCH_PROTECT
#endif
#ifdef SYS_ARCH_UNPROTECT
#undef SYS_ARCH_UNPROTECT
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* Semaphore type */
typedef SemaphoreHandle_t sys_sem_t;
/* Mutex type */
typedef SemaphoreHandle_t sys_mutex_t;
/* Mailbox (message queue) type */
typedef QueueHandle_t sys_mbox_t;
/* Thread type */
typedef TaskHandle_t sys_thread_t;
/* Protection level type */
typedef u32_t sys_prot_t;
/* Null values */
#define SYS_SEM_NULL ((sys_sem_t)NULL)
#define SYS_MBOX_NULL ((sys_mbox_t)NULL)
#define SYS_MUTEX_NULL ((sys_mutex_t)NULL)
/* Use one per-thread semaphore for lwIP netconn/socket API calls. */
#define LWIP_NETCONN_THREAD_SEM_TLS_INDEX 0
#define LWIP_NETCONN_THREAD_SEM_GET() \
((sys_sem_t *)pvTaskGetThreadLocalStoragePointer(NULL, LWIP_NETCONN_THREAD_SEM_TLS_INDEX))
#define LWIP_NETCONN_THREAD_SEM_ALLOC() \
do { \
sys_sem_t *sem = (sys_sem_t *)mem_malloc(sizeof(sys_sem_t)); \
if (sem != NULL) { \
*sem = SYS_SEM_NULL; \
if (sys_sem_new(sem, 0) == ERR_OK) { \
vTaskSetThreadLocalStoragePointer(NULL, \
LWIP_NETCONN_THREAD_SEM_TLS_INDEX,\
sem); \
} else { \
mem_free(sem); \
} \
} \
} while (0)
#define LWIP_NETCONN_THREAD_SEM_FREE() \
do { \
sys_sem_t *sem = LWIP_NETCONN_THREAD_SEM_GET(); \
if (sem != NULL) { \
sys_sem_free(sem); \
mem_free(sem); \
vTaskSetThreadLocalStoragePointer(NULL, \
LWIP_NETCONN_THREAD_SEM_TLS_INDEX, \
NULL); \
} \
} while (0)
/* Check if semaphore/mbox is valid */
#define sys_sem_valid(sem) ((sem) != NULL && (*(sem)) != SYS_SEM_NULL)
#define sys_sem_set_invalid(sem) do { if ((sem) != NULL) { *(sem) = SYS_SEM_NULL; } } while(0)
#define sys_mbox_valid(mbox) ((mbox) != NULL && (*(mbox)) != SYS_MBOX_NULL)
#define sys_mbox_set_invalid(mbox) do { if ((mbox) != NULL) { *(mbox) = SYS_MBOX_NULL; } } while(0)
#define sys_mutex_valid(mutex) ((mutex) != NULL && (*(mutex)) != SYS_MUTEX_NULL)
#define sys_mutex_set_invalid(mutex) do { if ((mutex) != NULL) { *(mutex) = SYS_MUTEX_NULL; } } while(0)
/* System initialization */
void sys_init(void);
/* Get current time in milliseconds */
u32_t sys_now(void);
#ifdef __cplusplus
}
#endif
#endif /* __SYS_ARCH_H__ */
+109
View File
@@ -0,0 +1,109 @@
/**
* @file
*
* ACD IPv4 Address Conflict Detection
*/
/*
*
* Copyright (c) 2007 Dominik Spies <kontakt@dspies.de>
* Copyright (c) 2018 Jasper Verschueren <jasper.verschueren@apart-audio.com>
* 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: Jasper Verschueren <jasper.verschueren@apart-audio.com>
* Author: Dominik Spies <kontakt@dspies.de>
*/
#ifndef LWIP_HDR_ACD_H
#define LWIP_HDR_ACD_H
#include "lwip/opt.h"
/* don't build if not configured for use in lwipopts.h */
#if LWIP_IPV4 && LWIP_ACD
#include "lwip/netif.h"
#include "lwip/etharp.h"
#include "lwip/prot/acd.h"
#ifdef __cplusplus
extern "C" {
#endif
/** ACD Timing
* ACD_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 ....
*/
#define ACD_TMR_INTERVAL 100
/**
* Callback function: Handle conflict information from ACD module
*
* @param netif network interface to handle conflict information on
* @param state acd_callback_enum_t
*/
typedef void (*acd_conflict_callback_t)(struct netif *netif, acd_callback_enum_t state);
/** ACD state information per netif */
struct acd
{
/** next acd module */
struct acd *next;
/** the currently selected, probed, announced or used IP-Address */
ip4_addr_t ipaddr;
/** current ACD state machine state */
acd_state_enum_t state;
/** sent number of probes or announces, dependent on state */
u8_t sent_num;
/** ticks to wait, tick is ACD_TMR_INTERVAL long */
u16_t ttw;
/** ticks until a conflict can again be solved by defending */
u8_t lastconflict;
/** total number of probed/used IP-Addresses that resulted in a conflict */
u8_t num_conflicts;
/** callback function -> let's the acd user know if the address is good or
if a conflict is detected */
acd_conflict_callback_t acd_conflict_callback;
};
err_t acd_add(struct netif *netif, struct acd *acd,
acd_conflict_callback_t acd_conflict_callback);
void acd_remove(struct netif *netif, struct acd *acd);
err_t acd_start(struct netif *netif, struct acd *acd, ip4_addr_t ipaddr);
err_t acd_stop(struct acd *acd);
void acd_arp_reply(struct netif *netif, struct etharp_hdr *hdr);
void acd_tmr(void);
void acd_network_changed_link_down(struct netif *netif);
void acd_netif_ip_addr_changed(struct netif *netif, const ip_addr_t *old_addr,
const ip_addr_t *new_addr);
#ifdef __cplusplus
}
#endif
#endif /* LWIP_IPV4 && LWIP_ACD */
#endif /* LWIP_HDR_ACD_H */
+207
View File
@@ -0,0 +1,207 @@
/**
* @file
* Application layered TCP connection API (to be used from TCPIP thread)
*
* This file contains the generic API.
* For more details see @ref altcp_api.
*/
/*
* 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 <goldsimon@gmx.de>
*
*/
#ifndef LWIP_HDR_ALTCP_H
#define LWIP_HDR_ALTCP_H
#include "lwip/opt.h"
#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
#include "lwip/tcpbase.h"
#include "lwip/err.h"
#include "lwip/pbuf.h"
#include "lwip/ip_addr.h"
#ifdef __cplusplus
extern "C" {
#endif
struct altcp_pcb;
struct altcp_functions;
typedef err_t (*altcp_accept_fn)(void *arg, struct altcp_pcb *new_conn, err_t err);
typedef err_t (*altcp_connected_fn)(void *arg, struct altcp_pcb *conn, err_t err);
typedef err_t (*altcp_recv_fn)(void *arg, struct altcp_pcb *conn, struct pbuf *p, err_t err);
typedef err_t (*altcp_sent_fn)(void *arg, struct altcp_pcb *conn, u16_t len);
typedef err_t (*altcp_poll_fn)(void *arg, struct altcp_pcb *conn);
typedef void (*altcp_err_fn)(void *arg, err_t err);
typedef struct altcp_pcb* (*altcp_new_fn)(void *arg, u8_t ip_type);
struct altcp_pcb {
const struct altcp_functions *fns;
struct altcp_pcb *inner_conn;
void *arg;
void *state;
/* application callbacks */
altcp_accept_fn accept;
altcp_connected_fn connected;
altcp_recv_fn recv;
altcp_sent_fn sent;
altcp_poll_fn poll;
altcp_err_fn err;
u8_t pollinterval;
};
/** @ingroup altcp
* Struct containing an allocator and its state. */
typedef struct altcp_allocator_s {
/** Allocator function */
altcp_new_fn alloc;
/** Argument to allocator function */
void *arg;
} altcp_allocator_t;
struct altcp_pcb *altcp_new(altcp_allocator_t *allocator);
struct altcp_pcb *altcp_new_ip6(altcp_allocator_t *allocator);
struct altcp_pcb *altcp_new_ip_type(altcp_allocator_t *allocator, u8_t ip_type);
void altcp_arg(struct altcp_pcb *conn, void *arg);
void altcp_accept(struct altcp_pcb *conn, altcp_accept_fn accept);
void altcp_recv(struct altcp_pcb *conn, altcp_recv_fn recv);
void altcp_sent(struct altcp_pcb *conn, altcp_sent_fn sent);
void altcp_poll(struct altcp_pcb *conn, altcp_poll_fn poll, u8_t interval);
void altcp_err(struct altcp_pcb *conn, altcp_err_fn err);
void altcp_recved(struct altcp_pcb *conn, u16_t len);
err_t altcp_bind(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port);
err_t altcp_connect(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port, altcp_connected_fn connected);
/* return conn for source code compatibility to tcp callback API only */
struct altcp_pcb *altcp_listen_with_backlog_and_err(struct altcp_pcb *conn, u8_t backlog, err_t *err);
#define altcp_listen_with_backlog(conn, backlog) altcp_listen_with_backlog_and_err(conn, backlog, NULL)
/** @ingroup altcp */
#define altcp_listen(conn) altcp_listen_with_backlog_and_err(conn, TCP_DEFAULT_LISTEN_BACKLOG, NULL)
void altcp_abort(struct altcp_pcb *conn);
err_t altcp_close(struct altcp_pcb *conn);
err_t altcp_shutdown(struct altcp_pcb *conn, int shut_rx, int shut_tx);
err_t altcp_write(struct altcp_pcb *conn, const void *dataptr, u16_t len, u8_t apiflags);
err_t altcp_output(struct altcp_pcb *conn);
u16_t altcp_mss(struct altcp_pcb *conn);
u16_t altcp_sndbuf(struct altcp_pcb *conn);
u16_t altcp_sndqueuelen(struct altcp_pcb *conn);
void altcp_nagle_disable(struct altcp_pcb *conn);
void altcp_nagle_enable(struct altcp_pcb *conn);
int altcp_nagle_disabled(struct altcp_pcb *conn);
void altcp_setprio(struct altcp_pcb *conn, u8_t prio);
err_t altcp_get_tcp_addrinfo(struct altcp_pcb *conn, int local, ip_addr_t *addr, u16_t *port);
ip_addr_t *altcp_get_ip(struct altcp_pcb *conn, int local);
u16_t altcp_get_port(struct altcp_pcb *conn, int local);
#if LWIP_TCP_KEEPALIVE
void altcp_keepalive_disable(struct altcp_pcb *conn);
void altcp_keepalive_enable(struct altcp_pcb *conn, u32_t idle, u32_t intvl, u32_t count);
#endif
#ifdef LWIP_DEBUG
enum tcp_state altcp_dbg_get_tcp_state(struct altcp_pcb *conn);
#endif
#ifdef __cplusplus
}
#endif
#else /* LWIP_ALTCP */
/* ALTCP disabled, define everything to link against tcp callback API (e.g. to get a small non-ssl httpd) */
#include "lwip/tcp.h"
#define altcp_accept_fn tcp_accept_fn
#define altcp_connected_fn tcp_connected_fn
#define altcp_recv_fn tcp_recv_fn
#define altcp_sent_fn tcp_sent_fn
#define altcp_poll_fn tcp_poll_fn
#define altcp_err_fn tcp_err_fn
#define altcp_pcb tcp_pcb
#define altcp_tcp_new_ip_type tcp_new_ip_type
#define altcp_tcp_new tcp_new
#define altcp_tcp_new_ip6 tcp_new_ip6
#define altcp_new(allocator) tcp_new()
#define altcp_new_ip6(allocator) tcp_new_ip6()
#define altcp_new_ip_type(allocator, ip_type) tcp_new_ip_type(ip_type)
#define altcp_arg tcp_arg
#define altcp_accept tcp_accept
#define altcp_recv tcp_recv
#define altcp_sent tcp_sent
#define altcp_poll tcp_poll
#define altcp_err tcp_err
#define altcp_recved tcp_recved
#define altcp_bind tcp_bind
#define altcp_connect tcp_connect
#define altcp_listen_with_backlog_and_err tcp_listen_with_backlog_and_err
#define altcp_listen_with_backlog tcp_listen_with_backlog
#define altcp_listen tcp_listen
#define altcp_abort tcp_abort
#define altcp_close tcp_close
#define altcp_shutdown tcp_shutdown
#define altcp_write tcp_write
#define altcp_output tcp_output
#define altcp_mss tcp_mss
#define altcp_sndbuf tcp_sndbuf
#define altcp_sndqueuelen tcp_sndqueuelen
#define altcp_nagle_disable tcp_nagle_disable
#define altcp_nagle_enable tcp_nagle_enable
#define altcp_nagle_disabled tcp_nagle_disabled
#define altcp_setprio tcp_setprio
#define altcp_get_tcp_addrinfo tcp_get_tcp_addrinfo
#define altcp_get_ip(pcb, local) ((local) ? (&(pcb)->local_ip) : (&(pcb)->remote_ip))
#ifdef LWIP_DEBUG
#define altcp_dbg_get_tcp_state tcp_dbg_get_tcp_state
#endif
#endif /* LWIP_ALTCP */
#endif /* LWIP_HDR_ALTCP_H */
+72
View File
@@ -0,0 +1,72 @@
/**
* @file
* Application layered TCP connection API (to be used from TCPIP thread)<br>
* 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 <goldsimon@gmx.de>
*
*/
#ifndef LWIP_HDR_ALTCP_TCP_H
#define LWIP_HDR_ALTCP_TCP_H
#include "lwip/opt.h"
#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
#include "lwip/altcp.h"
#ifdef __cplusplus
extern "C" {
#endif
struct altcp_pcb *altcp_tcp_new_ip_type(u8_t ip_type);
#define altcp_tcp_new() altcp_tcp_new_ip_type(IPADDR_TYPE_V4)
#define altcp_tcp_new_ip6() altcp_tcp_new_ip_type(IPADDR_TYPE_V6)
struct altcp_pcb *altcp_tcp_alloc(void *arg, u8_t ip_type);
struct tcp_pcb;
struct altcp_pcb *altcp_tcp_wrap(struct tcp_pcb *tpcb);
#ifdef __cplusplus
}
#endif
#endif /* LWIP_ALTCP */
#endif /* LWIP_HDR_ALTCP_TCP_H */
+196
View File
@@ -0,0 +1,196 @@
/**
* @file
* Application layered TCP/TLS connection API (to be used from TCPIP thread)
*
* @defgroup altcp_tls TLS layer
* @ingroup altcp
* This file contains function prototypes for a TLS layer.
* A port to ARM mbedtls is provided in the apps/ tree
* (LWIP_ALTCP_TLS_MBEDTLS option).
*/
/*
* 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 <goldsimon@gmx.de>
*
*/
#ifndef LWIP_HDR_ALTCP_TLS_H
#define LWIP_HDR_ALTCP_TLS_H
#include "lwip/opt.h"
#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
#if LWIP_ALTCP_TLS
#include "lwip/altcp.h"
/* check if mbedtls port is enabled */
#include "lwip/apps/altcp_tls_mbedtls_opts.h"
/* allow session structure to be fully defined when using mbedtls port */
#if LWIP_ALTCP_TLS_MBEDTLS
#include "mbedtls/ssl.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
/** @ingroup altcp_tls
* ALTCP_TLS configuration handle, content depends on port (e.g. mbedtls)
*/
struct altcp_tls_config;
/** @ingroup altcp_tls
* Create an ALTCP_TLS server configuration handle prepared for multiple certificates
*/
struct altcp_tls_config *altcp_tls_create_config_server(u8_t cert_count);
/** @ingroup altcp_tls
* Add a certificate to an ALTCP_TLS server configuration handle
*/
err_t altcp_tls_config_server_add_privkey_cert(struct altcp_tls_config *config,
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);
/** @ingroup altcp_tls
* Create an ALTCP_TLS server configuration handle with one certificate
* (short version of calling @ref altcp_tls_create_config_server and
* @ref altcp_tls_config_server_add_privkey_cert)
*/
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);
/** @ingroup altcp_tls
* Create an ALTCP_TLS client configuration handle
*/
struct altcp_tls_config *altcp_tls_create_config_client(const u8_t *cert, size_t cert_len);
/** @ingroup altcp_tls
* Create an ALTCP_TLS client configuration handle with two-way server/client authentication
*/
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);
/** @ingroup altcp_tls
* Configure ALPN TLS extension
* Example:<br>
* static const char *g_alpn_protocols[] = { "x-amzn-mqtt-ca", NULL };<br>
* tls_config = altcp_tls_create_config_client(ca, ca_len);<br>
* altcp_tls_conf_alpn_protocols(tls_config, g_alpn_protocols);<br>
*/
int altcp_tls_configure_alpn_protocols(struct altcp_tls_config *conf, const char **protos);
/** @ingroup altcp_tls
* Free an ALTCP_TLS configuration handle
*/
void altcp_tls_free_config(struct altcp_tls_config *conf);
/** @ingroup altcp_tls
* Free an ALTCP_TLS global entropy instance.
* All ALTCP_TLS configuration are linked to one altcp_tls_entropy_rng structure
* that handle an unique system entropy & ctr_drbg instance.
* This function allow application to free this altcp_tls_entropy_rng structure
* when all configuration referencing it were destroyed.
* This function does nothing if some ALTCP_TLS configuration handle are still
* active.
*/
void altcp_tls_free_entropy(void);
/** @ingroup altcp_tls
* Create new ALTCP_TLS layer wrapping an existing pcb as inner connection (e.g. TLS over TCP)
*/
struct altcp_pcb *altcp_tls_wrap(struct altcp_tls_config *config, struct altcp_pcb *inner_pcb);
/** @ingroup altcp_tls
* Create new ALTCP_TLS pcb and its inner tcp pcb
*/
struct altcp_pcb *altcp_tls_new(struct altcp_tls_config *config, u8_t ip_type);
/** @ingroup altcp_tls
* Create new ALTCP_TLS layer pcb and its inner tcp pcb.
* Same as @ref altcp_tls_new but this allocator function fits to
* @ref altcp_allocator_t / @ref altcp_new.<br>
'arg' must contain a struct altcp_tls_config *.
*/
struct altcp_pcb *altcp_tls_alloc(void *arg, u8_t ip_type);
/** @ingroup altcp_tls
* Return pointer to internal TLS context so application can tweak it.
* Real type depends on port (e.g. mbedtls)
*/
void *altcp_tls_context(struct altcp_pcb *conn);
/** @ingroup altcp_tls
* ALTCP_TLS session handle, content depends on port (e.g. mbedtls)
*/
struct altcp_tls_session
#if LWIP_ALTCP_TLS_MBEDTLS
{
mbedtls_ssl_session data;
}
#endif
;
/** @ingroup altcp_tls
* Initialise a TLS session buffer.
* Real type depends on port (e.g. mbedtls use mbedtls_ssl_session)
*/
void altcp_tls_init_session(struct altcp_tls_session *dest);
/** @ingroup altcp_tls
* Save current connected session to reuse it later. Should be called after altcp_connect() succeeded.
* Return error if saving session fail.
* Real type depends on port (e.g. mbedtls use mbedtls_ssl_session)
*/
err_t altcp_tls_get_session(struct altcp_pcb *conn, struct altcp_tls_session *dest);
/** @ingroup altcp_tls
* Restore a previously saved session. Must be called before altcp_connect().
* Return error if cannot restore session.
* Real type depends on port (e.g. mbedtls use mbedtls_ssl_session)
*/
err_t altcp_tls_set_session(struct altcp_pcb *conn, struct altcp_tls_session *from);
/** @ingroup altcp_tls
* Free allocated data inside a TLS session buffer.
* Real type depends on port (e.g. mbedtls use mbedtls_ssl_session)
*/
void altcp_tls_free_session(struct altcp_tls_session *dest);
#ifdef __cplusplus
}
#endif
#endif /* LWIP_ALTCP_TLS */
#endif /* LWIP_ALTCP */
#endif /* LWIP_HDR_ALTCP_TLS_H */
+434
View File
@@ -0,0 +1,434 @@
/**
* @file
* netconn API (to be used from non-TCPIP threads)
*/
/*
* 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 <adam@sics.se>
*
*/
#ifndef LWIP_HDR_API_H
#define LWIP_HDR_API_H
#include "lwip/opt.h"
#if LWIP_NETCONN || LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
/* Note: Netconn API is always available when sockets are enabled -
* sockets are implemented on top of them */
#include "lwip/arch.h"
#include "lwip/netbuf.h"
#include "lwip/sys.h"
#include "lwip/ip_addr.h"
#include "lwip/err.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Throughout this file, IP addresses and port numbers are expected to be in
* the same byte order as in the corresponding pcb.
*/
/* Flags for netconn_write (u8_t) */
#define NETCONN_NOFLAG 0x00
#define NETCONN_NOCOPY 0x00 /* Only for source code compatibility */
#define NETCONN_COPY 0x01
#define NETCONN_MORE 0x02
#define NETCONN_DONTBLOCK 0x04
#define NETCONN_NOAUTORCVD 0x08 /* prevent netconn_recv_data_tcp() from updating the tcp window - must be done manually via netconn_tcp_recvd() */
#define NETCONN_NOFIN 0x10 /* upper layer already received data, leave FIN in queue until called again */
/* Flags for struct netconn.flags (u8_t) */
/** This netconn had an error, don't block on recvmbox/acceptmbox any more */
#define NETCONN_FLAG_MBOXCLOSED 0x01
/** Should this netconn avoid blocking? */
#define NETCONN_FLAG_NON_BLOCKING 0x02
/** Was the last connect action a non-blocking one? */
#define NETCONN_FLAG_IN_NONBLOCKING_CONNECT 0x04
#if LWIP_NETCONN_FULLDUPLEX
/** The mbox of this netconn is being deallocated, don't use it anymore */
#define NETCONN_FLAG_MBOXINVALID 0x08
#endif /* LWIP_NETCONN_FULLDUPLEX */
/** If a nonblocking write has been rejected before, poll_tcp needs to
check if the netconn is writable again */
#define NETCONN_FLAG_CHECK_WRITESPACE 0x10
#if LWIP_IPV6
/** If this flag is set then only IPv6 communication is allowed on the
netconn. As per RFC#3493 this features defaults to OFF allowing
dual-stack usage by default. */
#define NETCONN_FLAG_IPV6_V6ONLY 0x20
#endif /* LWIP_IPV6 */
#if LWIP_NETBUF_RECVINFO
/** Received packet info will be recorded for this netconn */
#define NETCONN_FLAG_PKTINFO 0x40
#endif /* LWIP_NETBUF_RECVINFO */
/** A FIN has been received but not passed to the application yet */
#define NETCONN_FIN_RX_PENDING 0x80
/* Helpers to process several netconn_types by the same code */
#define NETCONNTYPE_GROUP(t) ((t)&0xF0)
#define NETCONNTYPE_DATAGRAM(t) ((t)&0xE0)
#if LWIP_IPV6
#define NETCONN_TYPE_IPV6 0x08
#define NETCONNTYPE_ISIPV6(t) (((t)&NETCONN_TYPE_IPV6) != 0)
#define NETCONNTYPE_ISUDPLITE(t) (((t)&0xF3) == NETCONN_UDPLITE)
#define NETCONNTYPE_ISUDPNOCHKSUM(t) (((t)&0xF3) == NETCONN_UDPNOCHKSUM)
#else /* LWIP_IPV6 */
#define NETCONNTYPE_ISIPV6(t) (0)
#define NETCONNTYPE_ISUDPLITE(t) ((t) == NETCONN_UDPLITE)
#define NETCONNTYPE_ISUDPNOCHKSUM(t) ((t) == NETCONN_UDPNOCHKSUM)
#endif /* LWIP_IPV6 */
/** @ingroup netconn_common
* Protocol family and type of the netconn
*/
enum netconn_type {
NETCONN_INVALID = 0,
/** TCP IPv4 */
NETCONN_TCP = 0x10,
#if LWIP_IPV6
/** TCP IPv6 */
NETCONN_TCP_IPV6 = NETCONN_TCP | NETCONN_TYPE_IPV6 /* 0x18 */,
#endif /* LWIP_IPV6 */
/** UDP IPv4 */
NETCONN_UDP = 0x20,
/** UDP IPv4 lite */
NETCONN_UDPLITE = 0x21,
/** UDP IPv4 no checksum */
NETCONN_UDPNOCHKSUM = 0x22,
#if LWIP_IPV6
/** UDP IPv6 (dual-stack by default, unless you call @ref netconn_set_ipv6only) */
NETCONN_UDP_IPV6 = NETCONN_UDP | NETCONN_TYPE_IPV6 /* 0x28 */,
/** UDP IPv6 lite (dual-stack by default, unless you call @ref netconn_set_ipv6only) */
NETCONN_UDPLITE_IPV6 = NETCONN_UDPLITE | NETCONN_TYPE_IPV6 /* 0x29 */,
/** UDP IPv6 no checksum (dual-stack by default, unless you call @ref netconn_set_ipv6only) */
NETCONN_UDPNOCHKSUM_IPV6 = NETCONN_UDPNOCHKSUM | NETCONN_TYPE_IPV6 /* 0x2a */,
#endif /* LWIP_IPV6 */
/** Raw connection IPv4 */
NETCONN_RAW = 0x40
#if LWIP_IPV6
/** Raw connection IPv6 (dual-stack by default, unless you call @ref netconn_set_ipv6only) */
, NETCONN_RAW_IPV6 = NETCONN_RAW | NETCONN_TYPE_IPV6 /* 0x48 */
#endif /* LWIP_IPV6 */
};
/** Current state of the netconn. Non-TCP netconns are always
* in state NETCONN_NONE! */
enum netconn_state {
NETCONN_NONE,
NETCONN_WRITE,
NETCONN_LISTEN,
NETCONN_CONNECT,
NETCONN_CLOSE
};
/** Used to inform the callback function about changes
*
* Event explanation:
*
* In the netconn implementation, there are three ways to block a client:
*
* - accept mbox (sys_arch_mbox_fetch(&conn->acceptmbox, &accept_ptr, 0); in netconn_accept())
* - receive mbox (sys_arch_mbox_fetch(&conn->recvmbox, &buf, 0); in netconn_recv_data())
* - send queue is full (sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0); in lwip_netconn_do_write())
*
* The events have to be seen as events signaling the state of these mboxes/semaphores. For non-blocking
* connections, you need to know in advance whether a call to a netconn function call would block or not,
* and these events tell you about that.
*
* RCVPLUS events say: Safe to perform a potentially blocking call call once more.
* They are counted in sockets - three RCVPLUS events for accept mbox means you are safe
* to call netconn_accept 3 times without being blocked.
* Same thing for receive mbox.
*
* RCVMINUS events say: Your call to to a possibly blocking function is "acknowledged".
* Socket implementation decrements the counter.
*
* For TX, there is no need to count, its merely a flag. SENDPLUS means you may send something.
* SENDPLUS occurs when enough data was delivered to peer so netconn_send() can be called again.
* A SENDMINUS event occurs when the next call to a netconn_send() would be blocking.
*/
enum netconn_evt {
NETCONN_EVT_RCVPLUS,
NETCONN_EVT_RCVMINUS,
NETCONN_EVT_SENDPLUS,
NETCONN_EVT_SENDMINUS,
NETCONN_EVT_ERROR
};
#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
/** Used for netconn_join_leave_group() */
enum netconn_igmp {
NETCONN_JOIN,
NETCONN_LEAVE
};
#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
#if LWIP_DNS
/* Used for netconn_gethostbyname_addrtype(), these should match the DNS_ADDRTYPE defines in dns.h */
#define NETCONN_DNS_DEFAULT NETCONN_DNS_IPV4_IPV6
#define NETCONN_DNS_IPV4 0
#define NETCONN_DNS_IPV6 1
#define NETCONN_DNS_IPV4_IPV6 2 /* try to resolve IPv4 first, try IPv6 if IPv4 fails only */
#define NETCONN_DNS_IPV6_IPV4 3 /* try to resolve IPv6 first, try IPv4 if IPv6 fails only */
#endif /* LWIP_DNS */
/* forward-declare some structs to avoid to include their headers */
struct ip_pcb;
struct tcp_pcb;
struct udp_pcb;
struct raw_pcb;
struct netconn;
struct api_msg;
/** A callback prototype to inform about events for a netconn */
typedef void (* netconn_callback)(struct netconn *, enum netconn_evt, u16_t len);
/** A netconn descriptor */
struct netconn {
/** type of the netconn (TCP, UDP or RAW) */
enum netconn_type type;
/** current state of the netconn */
enum netconn_state state;
/** the lwIP internal protocol control block */
union {
struct ip_pcb *ip;
struct tcp_pcb *tcp;
struct udp_pcb *udp;
struct raw_pcb *raw;
} pcb;
/** the last asynchronous unreported error this netconn had */
err_t pending_err;
#if !LWIP_NETCONN_SEM_PER_THREAD
/** sem that is used to synchronously execute functions in the core context */
sys_sem_t op_completed;
#endif
/** mbox where received packets are stored until they are fetched
by the netconn application thread (can grow quite big) */
sys_mbox_t recvmbox;
#if LWIP_TCP
/** mbox where new connections are stored until processed
by the application thread */
sys_mbox_t acceptmbox;
#endif /* LWIP_TCP */
#if LWIP_NETCONN_FULLDUPLEX
/** number of threads waiting on an mbox. This is required to unblock
all threads when closing while threads are waiting. */
int mbox_threads_waiting;
#endif
union {
int socket;
void *ptr;
} callback_arg;
#if LWIP_SO_SNDTIMEO
/** timeout to wait for sending data (which means enqueueing data for sending
in internal buffers) in milliseconds */
s32_t send_timeout;
#endif /* LWIP_SO_RCVTIMEO */
#if LWIP_SO_RCVTIMEO
/** timeout in milliseconds to wait for new data to be received
(or connections to arrive for listening netconns) */
u32_t recv_timeout;
#endif /* LWIP_SO_RCVTIMEO */
#if LWIP_SO_RCVBUF
/** maximum amount of bytes queued in recvmbox
not used for TCP: adjust TCP_WND instead! */
int recv_bufsize;
/** number of bytes currently in recvmbox to be received,
tested against recv_bufsize to limit bytes on recvmbox
for UDP and RAW, used for FIONREAD */
int recv_avail;
#endif /* LWIP_SO_RCVBUF */
#if LWIP_SO_LINGER
/** values <0 mean linger is disabled, values > 0 are seconds to linger */
s16_t linger;
#endif /* LWIP_SO_LINGER */
/** flags holding more netconn-internal state, see NETCONN_FLAG_* defines */
u8_t flags;
#if LWIP_TCP
/** TCP: when data passed to netconn_write doesn't fit into the send buffer,
this temporarily stores the message.
Also used during connect and close. */
struct api_msg *current_msg;
#endif /* LWIP_TCP */
/** A callback function that is informed about events for this netconn */
netconn_callback callback;
};
/** This vector type is passed to @ref netconn_write_vectors_partly to send
* multiple buffers at once.
* ATTENTION: This type has to directly map struct iovec since one is casted
* into the other!
*/
struct netvector {
/** pointer to the application buffer that contains the data to send */
const void *ptr;
/** size of the application data to send */
size_t len;
};
/** Register an Network connection event */
#define API_EVENT(c,e,l) if (c->callback) { \
(*c->callback)(c, e, l); \
}
/* Network connection functions: */
/** @ingroup netconn_common
* Create new netconn connection
* @param t @ref netconn_type */
#define netconn_new(t) netconn_new_with_proto_and_callback(t, 0, NULL)
#define netconn_new_with_callback(t, c) netconn_new_with_proto_and_callback(t, 0, c)
struct netconn *netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto,
netconn_callback callback);
err_t netconn_prepare_delete(struct netconn *conn);
err_t netconn_delete(struct netconn *conn);
/** Get the type of a netconn (as enum netconn_type). */
#define netconn_type(conn) (conn->type)
err_t netconn_getaddr(struct netconn *conn, ip_addr_t *addr,
u16_t *port, u8_t local);
/** @ingroup netconn_common */
#define netconn_peer(c,i,p) netconn_getaddr(c,i,p,0)
/** @ingroup netconn_common */
#define netconn_addr(c,i,p) netconn_getaddr(c,i,p,1)
err_t netconn_bind(struct netconn *conn, const ip_addr_t *addr, u16_t port);
err_t netconn_bind_if(struct netconn *conn, u8_t if_idx);
err_t netconn_connect(struct netconn *conn, const ip_addr_t *addr, u16_t port);
err_t netconn_disconnect (struct netconn *conn);
err_t netconn_listen_with_backlog(struct netconn *conn, u8_t backlog);
/** @ingroup netconn_tcp */
#define netconn_listen(conn) netconn_listen_with_backlog(conn, TCP_DEFAULT_LISTEN_BACKLOG)
err_t netconn_accept(struct netconn *conn, struct netconn **new_conn);
err_t netconn_recv(struct netconn *conn, struct netbuf **new_buf);
err_t netconn_recv_udp_raw_netbuf(struct netconn *conn, struct netbuf **new_buf);
err_t netconn_recv_udp_raw_netbuf_flags(struct netconn *conn, struct netbuf **new_buf, u8_t apiflags);
err_t netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf);
err_t netconn_recv_tcp_pbuf_flags(struct netconn *conn, struct pbuf **new_buf, u8_t apiflags);
err_t netconn_tcp_recvd(struct netconn *conn, size_t len);
err_t netconn_sendto(struct netconn *conn, struct netbuf *buf,
const ip_addr_t *addr, u16_t port);
err_t netconn_send(struct netconn *conn, struct netbuf *buf);
err_t netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size,
u8_t apiflags, size_t *bytes_written);
err_t netconn_write_vectors_partly(struct netconn *conn, struct netvector *vectors, u16_t vectorcnt,
u8_t apiflags, size_t *bytes_written);
/** @ingroup netconn_tcp */
#define netconn_write(conn, dataptr, size, apiflags) \
netconn_write_partly(conn, dataptr, size, apiflags, NULL)
err_t netconn_close(struct netconn *conn);
err_t netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx);
#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
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);
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);
#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
#if LWIP_DNS
#if LWIP_IPV4 && LWIP_IPV6
err_t netconn_gethostbyname_addrtype(const char *name, ip_addr_t *addr, u8_t dns_addrtype);
#define netconn_gethostbyname(name, addr) netconn_gethostbyname_addrtype(name, addr, NETCONN_DNS_DEFAULT)
#else /* LWIP_IPV4 && LWIP_IPV6 */
err_t netconn_gethostbyname(const char *name, ip_addr_t *addr);
#define netconn_gethostbyname_addrtype(name, addr, dns_addrtype) netconn_gethostbyname(name, addr)
#endif /* LWIP_IPV4 && LWIP_IPV6 */
#endif /* LWIP_DNS */
err_t netconn_err(struct netconn *conn);
#define netconn_recv_bufsize(conn) ((conn)->recv_bufsize)
#define netconn_set_flags(conn, set_flags) do { (conn)->flags = (u8_t)((conn)->flags | (set_flags)); } while(0)
#define netconn_clear_flags(conn, clr_flags) do { (conn)->flags = (u8_t)((conn)->flags & (u8_t)(~(clr_flags) & 0xff)); } while(0)
#define netconn_is_flag_set(conn, flag) (((conn)->flags & (flag)) != 0)
#define netconn_set_callback_arg(conn, arg) do { (conn)->callback_arg.ptr = (arg); } while(0)
#define netconn_get_callback_arg(conn) ((conn)->callback_arg.ptr)
/** Set the blocking status of netconn calls (@todo: write/send is missing) */
#define netconn_set_nonblocking(conn, val) do { if(val) { \
netconn_set_flags(conn, NETCONN_FLAG_NON_BLOCKING); \
} else { \
netconn_clear_flags(conn, NETCONN_FLAG_NON_BLOCKING); }} while(0)
/** Get the blocking status of netconn calls (@todo: write/send is missing) */
#define netconn_is_nonblocking(conn) (((conn)->flags & NETCONN_FLAG_NON_BLOCKING) != 0)
#if LWIP_IPV6
/** @ingroup netconn_common
* TCP: Set the IPv6 ONLY status of netconn calls (see NETCONN_FLAG_IPV6_V6ONLY)
*/
#define netconn_set_ipv6only(conn, val) do { if(val) { \
netconn_set_flags(conn, NETCONN_FLAG_IPV6_V6ONLY); \
} else { \
netconn_clear_flags(conn, NETCONN_FLAG_IPV6_V6ONLY); }} while(0)
/** @ingroup netconn_common
* TCP: Get the IPv6 ONLY status of netconn calls (see NETCONN_FLAG_IPV6_V6ONLY)
*/
#define netconn_get_ipv6only(conn) (((conn)->flags & NETCONN_FLAG_IPV6_V6ONLY) != 0)
#endif /* LWIP_IPV6 */
#if LWIP_SO_SNDTIMEO
/** Set the send timeout in milliseconds */
#define netconn_set_sendtimeout(conn, timeout) ((conn)->send_timeout = (timeout))
/** Get the send timeout in milliseconds */
#define netconn_get_sendtimeout(conn) ((conn)->send_timeout)
#endif /* LWIP_SO_SNDTIMEO */
#if LWIP_SO_RCVTIMEO
/** Set the receive timeout in milliseconds */
#define netconn_set_recvtimeout(conn, timeout) ((conn)->recv_timeout = (timeout))
/** Get the receive timeout in milliseconds */
#define netconn_get_recvtimeout(conn) ((conn)->recv_timeout)
#endif /* LWIP_SO_RCVTIMEO */
#if LWIP_SO_RCVBUF
/** Set the receive buffer in bytes */
#define netconn_set_recvbufsize(conn, recvbufsize) ((conn)->recv_bufsize = (recvbufsize))
/** Get the receive buffer in bytes */
#define netconn_get_recvbufsize(conn) ((conn)->recv_bufsize)
#endif /* LWIP_SO_RCVBUF*/
#if LWIP_NETCONN_SEM_PER_THREAD
void netconn_thread_init(void);
void netconn_thread_cleanup(void);
#else /* LWIP_NETCONN_SEM_PER_THREAD */
#define netconn_thread_init()
#define netconn_thread_cleanup()
#endif /* LWIP_NETCONN_SEM_PER_THREAD */
#ifdef __cplusplus
}
#endif
#endif /* LWIP_NETCONN || LWIP_SOCKET */
#endif /* LWIP_HDR_API_H */
+2
View File
@@ -0,0 +1,2 @@
This directory contains application headers.
Every application shall provide one api file APP.h and optionally one options file APP_opts.h
@@ -0,0 +1,79 @@
/**
* @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 <goldsimon@gmx.de>
*
*/
#ifndef LWIP_HDR_APPS_ALTCP_PROXYCONNECT_H
#define LWIP_HDR_APPS_ALTCP_PROXYCONNECT_H
#include "lwip/opt.h"
#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
#include "lwip/ip_addr.h"
#ifdef __cplusplus
extern "C" {
#endif
struct altcp_proxyconnect_config {
ip_addr_t proxy_addr;
u16_t proxy_port;
};
struct altcp_pcb *altcp_proxyconnect_new(struct altcp_proxyconnect_config *config, struct altcp_pcb *inner_pcb);
struct altcp_pcb *altcp_proxyconnect_new_tcp(struct altcp_proxyconnect_config *config, u8_t ip_type);
struct altcp_pcb *altcp_proxyconnect_alloc(void *arg, u8_t ip_type);
#if LWIP_ALTCP_TLS
struct altcp_proxyconnect_tls_config {
struct altcp_proxyconnect_config proxy;
struct altcp_tls_config *tls_config;
};
struct altcp_pcb *altcp_proxyconnect_tls_alloc(void *arg, u8_t ip_type);
#endif /* LWIP_ALTCP_TLS */
#ifdef __cplusplus
}
#endif
#endif /* LWIP_ALTCP */
#endif /* LWIP_HDR_APPS_ALTCP_PROXYCONNECT_H */
@@ -0,0 +1,111 @@
/**
* @file
* Application layered TCP/TLS connection API (to be used from TCPIP thread)
*
* This file contains options for an mbedtls port of the TLS layer.
*/
/*
* 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 <goldsimon@gmx.de>
*
*/
#ifndef LWIP_HDR_ALTCP_TLS_OPTS_H
#define LWIP_HDR_ALTCP_TLS_OPTS_H
#include "lwip/opt.h"
#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
/** LWIP_ALTCP_TLS_MBEDTLS==1: use mbedTLS for TLS support for altcp API
* mbedtls include directory must be reachable via include search path
*/
#ifndef LWIP_ALTCP_TLS_MBEDTLS
#define LWIP_ALTCP_TLS_MBEDTLS 0
#endif
/** Configure debug level of this file */
#ifndef ALTCP_MBEDTLS_DEBUG
#define ALTCP_MBEDTLS_DEBUG LWIP_DBG_OFF
#endif
/** Configure lwIP debug level of the mbedTLS library */
#ifndef ALTCP_MBEDTLS_LIB_DEBUG
#define ALTCP_MBEDTLS_LIB_DEBUG LWIP_DBG_OFF
#endif
/** Configure minimum internal debug level of the mbedTLS library */
#ifndef ALTCP_MBEDTLS_LIB_DEBUG_LEVEL_MIN
#define ALTCP_MBEDTLS_LIB_DEBUG_LEVEL_MIN 0
#endif
/** Enable the basic session cache
* ATTENTION: Using a session cache can lower security by reusing keys!
*/
#ifndef ALTCP_MBEDTLS_USE_SESSION_CACHE
#define ALTCP_MBEDTLS_USE_SESSION_CACHE 0
#endif
/** Maximum cache size of the basic session cache */
#ifndef ALTCP_MBEDTLS_SESSION_CACHE_SIZE
#define ALTCP_MBEDTLS_SESSION_CACHE_SIZE 30
#endif
/** Set a session timeout in seconds for the basic session cache */
#ifndef ALTCP_MBEDTLS_SESSION_CACHE_TIMEOUT_SECONDS
#define ALTCP_MBEDTLS_SESSION_CACHE_TIMEOUT_SECONDS (60 * 60)
#endif
/** Use session tickets to speed up connection setup (needs
* MBEDTLS_SSL_SESSION_TICKETS enabled in mbedTLS config).
* ATTENTION: Using session tickets can lower security by reusing keys!
*/
#ifndef ALTCP_MBEDTLS_USE_SESSION_TICKETS
#define ALTCP_MBEDTLS_USE_SESSION_TICKETS 0
#endif
/** Session ticket cipher */
#ifndef ALTCP_MBEDTLS_SESSION_TICKET_CIPHER
#define ALTCP_MBEDTLS_SESSION_TICKET_CIPHER MBEDTLS_CIPHER_AES_256_GCM
#endif
/** Maximum timeout for session tickets */
#ifndef ALTCP_MBEDTLS_SESSION_TICKET_TIMEOUT_SECONDS
#define ALTCP_MBEDTLS_SESSION_TICKET_TIMEOUT_SECONDS (60 * 60 * 24)
#endif
/** Certificate verification mode: MBEDTLS_SSL_VERIFY_NONE, MBEDTLS_SSL_VERIFY_OPTIONAL (default),
* MBEDTLS_SSL_VERIFY_REQUIRED (recommended)*/
#ifndef ALTCP_MBEDTLS_AUTHMODE
#define ALTCP_MBEDTLS_AUTHMODE MBEDTLS_SSL_VERIFY_OPTIONAL
#endif
#endif /* LWIP_ALTCP */
#endif /* LWIP_HDR_ALTCP_TLS_OPTS_H */

Some files were not shown because too many files have changed in this diff Show More