Browse Source

add: 添加icmp库,支持ping操作

Wendal Chen 11 months ago
parent
commit
39fcc4d564

+ 146 - 0
components/network/icmp/binding/luat_lib_icmp.c

@@ -0,0 +1,146 @@
+/*
+@module  icmp
+@summary ICMP协议(PING)
+@version 1.0
+@date    2024.010.15
+@demo    icmp
+@tag LUAT_USE_NETWORK
+@usage
+-- 等网络就绪后, 初始化icmp库
+icmp.setup(socket.LWIP_GP)
+-- 执行ping,等待回应
+icmp.ping(socket.LWIP_GP, "183.2.172.177")
+-- 等待结果
+sys.waitUnitl("PING_RESULT", 3000)
+-- 详细用法请看demo
+*/
+
+
+#include "luat_base.h"
+#include "luat_icmp.h"
+#include "luat_network_adapter.h"
+#include "luat_msgbus.h"
+
+#include "lwip/ip_addr.h"
+
+#include "rotable2.h"
+
+#define LUAT_LOG_TAG "icmp"
+#include "luat_log.h"
+
+typedef struct ping_result
+{
+    uint8_t adapter_id;
+    uint32_t addr;
+    uint32_t t_used;
+}ping_result_t;
+
+
+static int l_icmp_handler(lua_State *L, void* ptr) {
+    rtos_msg_t* msg = (rtos_msg_t*)lua_topointer(L, -1);
+    lua_getglobal(L, "sys_pub");
+    uint32_t addr = msg->arg2;
+    char buff[32] = {0};
+    ip_addr_t ip = {0};
+    ip.u_addr.ip4.addr = addr;
+    ipaddr_ntoa_r(&ip, buff, 32);
+    if (lua_isfunction(L, -1)) {
+        lua_pushstring(L, "PING_RESULT");
+        lua_pushinteger(L, (msg->arg1 >> 16) & 0xFFFF);
+        lua_pushinteger(L, (msg->arg1 >> 0) & 0xFFFF);
+        lua_pushstring(L, buff);
+        lua_call(L, 4, 0);
+    }
+    return 0;
+}
+
+static void l_icmp_cb(void* _ctx, uint32_t tused) {
+    if (_ctx == NULL) {
+        return;
+    }
+    luat_icmp_ctx_t* ctx = (luat_icmp_ctx_t*)_ctx;
+    rtos_msg_t msg = {
+        .handler = l_icmp_handler,
+        .arg1 = ctx->adapter_id << 16 | (tused & 0xFFFF),
+        .arg2 = ctx->dst.u_addr.ip4.addr
+    };
+    luat_msgbus_put(&msg, 0);
+}
+
+/*
+初始化指定网络设备的icmp
+@api icmp.setup(id)
+@int 网络适配器的id
+@return bool 成功与否
+@usage
+-- 初始化4G网络的icmp, 要等4G联网后才能调用
+icmp.setup(socket.LWIP_GP)
+*/
+static int l_icmp_setup(lua_State *L) {
+    int id = luaL_checkinteger(L, 1);
+    luat_icmp_ctx_t* ctx = luat_icmp_init(id);
+    if (ctx != NULL) {
+        ctx->cb = l_icmp_cb;
+    }
+    lua_pushboolean(L, ctx != NULL);
+    return 1;
+}
+
+/*
+发起ping(异步的)
+@api icmp.ping(id, ip, len)
+@int 网络适配器的id
+@string 目标ip地址,不支持域名!!
+@int ping包大小,默认128字节,可以不传
+@return bool 成功与否, 仅代表发送与否,不代表服务器已经响应
+@usage
+sys.taskInit(function()
+    sys.waitUntil("IP_READY")
+    sys.wait(1000)
+    icmp.setup(socket.LWIP_GP)
+    while 1 do
+        icmp.ping(socket.LWIP_GP, "121.14.77.221")
+        sys.waitUntil("PING_RESULT", 3000)
+        sys.wait(3000)
+    end
+end)
+
+sys.subscribe("PING_RESULT", function(id, time, dst)
+    log.info("ping", id, time, dst);
+end)
+*/
+static int l_icmp_ping(lua_State *L) {
+    int id = luaL_checkinteger(L, 1);
+    luat_icmp_ctx_t* ctx = luat_icmp_get(id);
+    if (ctx == NULL) {
+        ctx = luat_icmp_init(id);
+        if (ctx == NULL) {
+            LLOGW("icmp初始化失败");
+            return 0;
+        }
+    }
+    const char* ip = luaL_checkstring(L, 2);
+    size_t len = luaL_optinteger(L, 3, 128);
+    ip_addr_t addr = {0};
+    if (0 == ipaddr_aton(ip, &addr)) {
+        LLOGW("目标地址非法 %s", ip);
+        return 0;
+    };
+    int result = luat_icmp_ping(ctx, &addr, len);
+    lua_pushinteger(L, result == 0);
+    return 1;
+}
+
+
+static const rotable_Reg_t reg_icmp[] =
+{
+    { "setup" ,           ROREG_FUNC(l_icmp_setup)},
+    { "ping" ,            ROREG_FUNC(l_icmp_ping)},
+    // { "close" ,           ROREG_FUNC(l_icmp_close)},
+	{ NULL,               ROREG_INT(0)}
+};
+
+LUAMOD_API int luaopen_icmp( lua_State *L ) {
+    luat_newlib2(L, reg_icmp);
+    return 1;
+}

+ 33 - 0
components/network/icmp/include/luat_icmp.h

@@ -0,0 +1,33 @@
+#ifndef LUAT_ICMP_H
+#define LUAT_ICMP_H
+
+#include "luat_base.h"
+#include "luat_netdrv.h"
+#include "lwip/ip.h"
+#include "lwip/ip_addr.h"
+#include "lwip/icmp.h"
+#include "lwip/netif.h"
+#include "lwip/raw.h"
+
+
+typedef void (*luat_icmp_recv_fn)(void* ctx, uint32_t tused);
+
+typedef struct luat_icmp_ctx
+{
+    uint8_t adapter_id;
+    struct netif *netif;
+    struct raw_pcb *pcb;
+    ip_addr_t dst;
+    uint16_t id;
+    uint16_t seqno;
+    uint64_t send_time;
+    uint64_t recv_time;
+    luat_icmp_recv_fn cb;
+}luat_icmp_ctx_t;
+
+
+luat_icmp_ctx_t* luat_icmp_init(uint8_t adapter_id);
+luat_icmp_ctx_t* luat_icmp_get(uint8_t adapter_id);
+int luat_icmp_ping(luat_icmp_ctx_t* ctx, ip_addr_t* dst, size_t size);
+
+#endif

+ 153 - 0
components/network/icmp/src/luat_icmp.c

@@ -0,0 +1,153 @@
+#include "luat_base.h"
+#include "luat_icmp.h"
+#include "luat_mem.h"
+#include "luat_netdrv.h"
+#include "luat_network_adapter.h"
+#include "luat_mcu.h"
+
+#include "lwip/prot/icmp.h"
+#include "lwip/prot/ip.h"
+#include "lwip/pbuf.h"
+#include "lwip/inet_chksum.h"
+
+#define LUAT_LOG_TAG "icmp"
+#include "luat_log.h"
+
+static luat_icmp_ctx_t* ctxs[NW_ADAPTER_INDEX_LWIP_NETIF_QTY];
+
+static u8_t luat_icmp_recv(void *arg, struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *addr)
+{
+    char buff[32] = {0};
+    ipaddr_ntoa_r(addr, buff, 32);
+    // LLOGD("ICMP recv %p %p %d", arg, pcb, p->tot_len);
+    uint32_t adapter_id = (uint32_t)arg;
+    if (adapter_id >= NW_ADAPTER_INDEX_LWIP_NETIF_QTY) {
+        return 0;
+    }
+    if (ctxs[adapter_id] == NULL) {
+        return 0;
+    }
+    luat_icmp_ctx_t* ctx = ctxs[adapter_id];
+    if (ctx->send_time == 0) {
+        return 0; // 并没有待接收的ICMP数据
+    }
+    struct icmp_echo_hdr *iecho;
+    
+    LWIP_UNUSED_ARG(arg);
+    LWIP_UNUSED_ARG(pcb);
+    LWIP_UNUSED_ARG(addr);
+
+    if (p->tot_len >= sizeof(struct icmp_echo_hdr) + sizeof(struct ip_hdr)) {
+        iecho = (struct icmp_echo_hdr *)(p->payload + sizeof(struct ip_hdr));
+
+        /* 验证是否为ICMP Echo回复 */
+        if (iecho->type == ICMP_ER && htons(iecho->id) == ctx->id && htons(iecho->seqno) == ctx->seqno) {
+            
+            /* 计算往返时间(示例中需要计时器支持)*/
+            uint32_t elapsed = (uint32_t)(luat_mcu_tick64_ms() - ctx->send_time);
+            if (elapsed < 16 *1000 && ctx->cb) {
+                ctx->cb(ctx, elapsed);
+            }
+            pbuf_free(p);   // 释放pbuf
+            return 1;       // 已处理该包
+        }
+    }
+    return 0;               // 继续传递数据包
+}
+
+/** Prepare a echo ICMP request */
+static void ping_prepare_echo( struct icmp_echo_hdr *iecho, u16_t len, u16_t id, uint32_t seq_num)
+{
+    size_t i;
+    size_t data_len = len - sizeof(struct icmp_echo_hdr);
+
+    ICMPH_TYPE_SET(iecho, ICMP_ECHO);
+    ICMPH_CODE_SET(iecho, 0);
+    iecho->chksum = 0;
+    iecho->id     = htons(id);
+    iecho->seqno  = htons(seq_num);
+
+    /* fill the additional data buffer with some data */
+    for (i = 0; i < data_len; i++)
+    {
+        ((char*) iecho)[sizeof(struct icmp_echo_hdr) + i] = (char) i;
+    }
+
+    iecho->chksum = inet_chksum(iecho, len);
+}
+
+luat_icmp_ctx_t* luat_icmp_get(uint8_t adapter_id) {
+    luat_netdrv_t* netdrv = luat_netdrv_get(adapter_id);
+    if (netdrv == NULL || netdrv->netif == NULL) {
+        return NULL;
+    }
+    return ctxs[adapter_id];
+}
+
+luat_icmp_ctx_t* luat_icmp_init(uint8_t adapter_id) {
+    luat_netdrv_t* netdrv = luat_netdrv_get(adapter_id);
+    if (netdrv == NULL || netdrv->netif == NULL) {
+        return NULL;
+    }
+    if (ctxs[adapter_id] != NULL) {
+        return ctxs[adapter_id];
+    }
+    ctxs[adapter_id] = luat_heap_malloc(sizeof(luat_icmp_ctx_t));
+    if (ctxs[adapter_id] == NULL) {
+        return NULL;
+    }
+    luat_icmp_ctx_t* ctx = ctxs[adapter_id];
+    ctx->netif = netdrv->netif;
+    ctx->pcb = raw_new(IP_PROTO_ICMP);
+    if (ctx->pcb == NULL) {
+        LLOGE("分配ICMP RAW PCB失败!!");
+        luat_heap_free(ctxs[adapter_id]);
+        ctxs[adapter_id] = NULL;
+        return NULL;
+    }
+    ctx->id = 0x02;
+    ctx->seqno = 0x01;
+    ctx->adapter_id = adapter_id;
+    // raw_bind_netif(ctx->pcb, ctx->netif);
+    raw_bind(ctx->pcb, &ctx->netif->ip_addr);
+    raw_recv(ctx->pcb, luat_icmp_recv, (void*)(uint32_t)adapter_id);
+    return ctx;
+}
+
+int luat_icmp_ping(luat_icmp_ctx_t* ctx, ip_addr_t* dst, size_t size) {
+    int ret = 0;
+
+    struct icmp_echo_hdr *iecho;
+
+    if (ctx == NULL || dst == NULL) {
+        return -1;
+    }
+    memcpy(&ctx->dst, dst, sizeof(ip_addr_t));
+    ctx->send_time = 0;
+    ctx->recv_time = 0;
+    int ping_size = sizeof(struct icmp_echo_hdr) + size;
+    struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, ping_size, PBUF_RAM);
+    if (p == NULL) {
+        return -2;
+    }
+    raw_bind(ctx->pcb, &ctx->netif->ip_addr);
+    iecho = (struct icmp_echo_hdr *)(p->payload);
+    ctx->id ++;
+    ctx->seqno ++;
+    ping_prepare_echo(iecho, size, ctx->id, ctx->seqno);
+
+    // ret = raw_sendto_if_src(ctx->pcb, p, dst, ctx->netif, &ctx->netif->ip_addr);
+    char buff[32] = {0};
+    char buff2[32] = {0};
+    ipaddr_ntoa_r(&ctx->netif->ip_addr, buff, 32);
+    ipaddr_ntoa_r(dst, buff2, 32);
+    // LLOGD("ICMP sendto %s --> %s", buff, buff2);
+    ctx->send_time = luat_mcu_tick64_ms();
+    ret = raw_sendto(ctx->pcb, p, dst);
+    if (ret) {
+        pbuf_free(p);
+        LLOGW("ICMP sendto error %d %s --> %s", ret, buff, buff2);
+        return ret;
+    }
+    return 0;
+}

+ 26 - 0
demo/icmp/ping/main.lua

@@ -0,0 +1,26 @@
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "pingtest"
+
+VERSION = "1.0.2"
+-- sys库是标配
+sys = require("sys")
+
+sys.subscribe("PING_RESULT", function(id, time, dst)
+    log.info("ping", id, time, dst);
+end)
+
+sys.taskInit(function()
+    sys.waitUntil("IP_READY")
+    sys.wait(1000)
+    icmp.setup(socket.LWIP_GP)
+    while 1 do
+        icmp.ping(socket.LWIP_GP, "121.14.77.221")
+        sys.waitUntil("PING_RESULT", 3000)
+        sys.wait(3000)
+    end
+end)
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!