From 939544964e0cccd74f51902203adfa47edf86b6b Mon Sep 17 00:00:00 2001 From: vsky Date: Sat, 2 Sep 2017 00:00:26 +0200 Subject: [PATCH] Net_info module exposing ping function initial commit --- app/include/user_modules.h | 2 + app/modules/net_info.c | 184 ++++++++++++++++++++++++++++++++++++ docs/en/modules/net_info.md | 79 ++++++++++++++++ mkdocs.yml | 1 + 4 files changed, 266 insertions(+) create mode 100644 app/modules/net_info.c create mode 100644 docs/en/modules/net_info.md diff --git a/app/include/user_modules.h b/app/include/user_modules.h index 0b28dbe757..830a6fedb2 100644 --- a/app/include/user_modules.h +++ b/app/include/user_modules.h @@ -38,6 +38,7 @@ //#define LUA_USE_MODULES_MDNS #define LUA_USE_MODULES_MQTT #define LUA_USE_MODULES_NET +#define LUA_USE_MODULES_NET_INFO #define LUA_USE_MODULES_NODE #define LUA_USE_MODULES_OW //#define LUA_USE_MODULES_PCM @@ -76,6 +77,7 @@ //#define LUA_USE_MODULES_WS2812 //#define LUA_USE_MODULES_WS2812_EFFECTS //#define LUA_USE_MODULES_XPT2046 +#define LUA_USE_MODULES_TEST //debug modules //#define LUA_USE_MODULES_SWTMR_DBG //SWTMR timer suspend Debug functions diff --git a/app/modules/net_info.c b/app/modules/net_info.c new file mode 100644 index 0000000000..19f47c4351 --- /dev/null +++ b/app/modules/net_info.c @@ -0,0 +1,184 @@ +// *************************************************************************** +// net_info module for ESP8266 with nodeMCU +// +// adopted jan 2017 for commit to nodeMCU by Wolfgang Rosner +// re-worked by Lukas Voborsky, @voborsky +// *************************************************************************** + +/** + * test.ping() + * Description: + * Send ICMP ping request to address, optionally call callback when response received + * + * Syntax: + * wifi.sta.getconfig(ssid, password) --Set STATION configuration, Auto-connect by default, Connects to any BSSID + * test.ping(address) -- send 4 ping requests to target address + * test.ping(address, n) -- send n ping requests to target address + * test.ping(address, n, callback) -- send n ping requests to target address + * Parameters: + * address: string + * n: number of requests to send + * callback: + * Returns: + * Nothing. + * + * Example: + * test.ping("192.168.0.1") -- send 4 pings to 192.168.0.1 + * test.ping("192.168.0.1", 10) -- send 10 pings to 192.168.0.1 + * test.ping("192.168.0.1", 10, got_ping) -- send 10 pings to 192.168.0.1, call got_ping() with the + * -- ping results + */ + +#define NODE_DEBUG + +#include "module.h" +#include "lauxlib.h" +#include "platform.h" +#include "mem.h" +#include "task/task.h" + +#include "lwip/ip_addr.h" +#include "espconn.h" +#include "lwip/dns.h" +#include "lwip/app/ping.h" +#include "lwip/raw.h" + +typedef struct { + struct ping_option ping_opt; // ping_opt needs to be the first element of the structure + sint32_t ping_callback_ref; + sint32_t self_ref; /* Reference to this structure as userdata */ + uint8_t ping_host_count; + ip_addr_t ping_host_ip; + } net_info_ping_t; +typedef net_info_ping_t* ping_t; + +static ping_t nip_get( lua_State *L, int stack ) { + ping_t nip = (ping_t)luaL_checkudata(L, stack, "net_info.ping"); + if (nip == NULL) { + return (ping_t)luaL_error(L, "ping object expected"); + } + return nip; +} + +void ping_received(void *arg, void *data) { + NODE_DBG("[ping_received] \n"); + // struct ping_option *pingopt = (struct ping_option*)arg; + ping_t nip = (ping_t)arg; + struct ping_resp *pingresp = (struct ping_resp*)data; + + char ipaddrstr[16]; + ip_addr_t source_ip; + + source_ip.addr = nip->ping_opt.ip; + ipaddr_ntoa_r(&source_ip, ipaddrstr, sizeof(ipaddrstr)); + + if (nip->ping_callback_ref != LUA_NOREF) { + lua_State *L = lua_getstate(); + lua_rawgeti(L, LUA_REGISTRYINDEX, nip->ping_callback_ref); + lua_pushinteger(L, pingresp->bytes); + lua_pushstring(L, ipaddrstr); + lua_pushinteger(L, pingresp->seqno); + lua_pushinteger(L, pingresp->resp_time); + lua_call(L, 4, 0); + } +} + +static int net_info_ping(lua_State *L) +{ + NODE_DBG("[net_info_ping]\n"); + ping_t nip = nip_get(L, 1); + + if (nip->self_ref == LUA_NOREF) { + lua_pushvalue(L, 1); + nip->self_ref = luaL_ref(L, LUA_REGISTRYINDEX); + } + + const char *ping_target; + unsigned count = 4; + + // retrieve address arg (mandatory) + if (lua_isstring(L, 2)) { + ping_target = luaL_checkstring(L, 2); + } else { + return luaL_error(L, "no address specified"); + } + + // retrieve count arg (optional) + if (lua_isnumber(L, 3)) { + count = luaL_checkinteger(L, 3); + } + + // retrieve callback arg (optional) + if (nip->ping_callback_ref != LUA_NOREF) { + luaL_unref(L, LUA_REGISTRYINDEX, nip->ping_callback_ref); + } + nip->ping_callback_ref = LUA_NOREF; + + if (lua_type(L, 4) == LUA_TFUNCTION || lua_type(L, 4) == LUA_TLIGHTFUNCTION) { + nip->ping_callback_ref = luaL_ref(L, LUA_REGISTRYINDEX); + } + + // attempt to parse ping target as IP + uint32 ip = ipaddr_addr(ping_target); + + if (ip != IPADDR_NONE) { + nip->ping_opt.count = count; + nip->ping_opt.ip = ip; + nip->ping_opt.coarse_time = 0; + nip->ping_opt.recv_function = &ping_received; + + NODE_DBG("[net_info_ping] calling ping_start\n"); + ping_start(&(nip->ping_opt)); + } else { + return luaL_error(L, "wrong IP address"); + } + + return 0; +} + +static int net_info_create( lua_State *L ) { + NODE_DBG("[net_info_create]\n"); + ping_t nip = (ping_t)lua_newuserdata(L, sizeof(net_info_ping_t)); + if (!nip) return luaL_error(L, "not enough memory"); + luaL_getmetatable(L, "net_info.ping"); + lua_setmetatable(L, -2); + nip->ping_callback_ref = LUA_NOREF; + nip->self_ref = LUA_NOREF; + return 1; +} + +static int net_info_unregister(lua_State* L){ + NODE_DBG("[net_info_unregister]\n"); + ping_t nip = nip_get(L, 1); + + if (nip->self_ref != LUA_REFNIL) { + luaL_unref(L, LUA_REGISTRYINDEX, nip->self_ref); + nip->self_ref = LUA_NOREF; + } + + if(nip->ping_callback_ref != LUA_NOREF) { + luaL_unref(L, LUA_REGISTRYINDEX, nip->ping_callback_ref); + } + nip->ping_callback_ref = LUA_NOREF; + return 0; +} + +// Module function map +LROT_BEGIN(net_info_dyn) + LROT_FUNCENTRY( ping, net_info_ping ) + LROT_FUNCENTRY( __gc, net_info_unregister ) + LROT_TABENTRY( __index, net_info_dyn ) +LROT_END( net_info_dyn, net_info_dyn, LROT_MASK_GC_INDEX ) + + +LROT_BEGIN(net_info) + LROT_FUNCENTRY( create, net_info_create ) +LROT_END( net_ifo, NULL, 0 ) + +int luaopen_net_info( lua_State *L ){ + NODE_DBG("[luaopen_net_info]\n"); + luaL_rometatable(L, "net_info.ping", LROT_TABLEREF(net_info_dyn)); + return 0; +} + +NODEMCU_MODULE(NET_INFO, "net_info", net_info, luaopen_net_info); diff --git a/docs/en/modules/net_info.md b/docs/en/modules/net_info.md new file mode 100644 index 0000000000..d095dcc446 --- /dev/null +++ b/docs/en/modules/net_info.md @@ -0,0 +1,79 @@ +# net_info Module +| Since | Origin / Contributor | Maintainer | Source | +| :----- | :-------------------- | :---------- | :------ | +| 2017-01-21 | [smcl](http://blog.mclemon.io/esp8266-contributing-to-the-nodemcu-ecosystem) | [wolfgangr](https://github.com/wolfgangr/nodemcu-firmware) | [net_info.c](../../../app/modules/net_info.c) + + +This module is a stub to collect common network diagnostic and analysis tools. + +##net_info.ping() + send ICMP ECHO_REQUEST to network hosts + +**Synopsis:**: +``` +net_info.ping(host [, count [, callback]]) +``` + +**Usage example:** +``` +=net_info.ping("www.google.de",2) +> 32 bytes from 172.217.20.195, icmp_seq=25 ttl=53 time=37ms +32 bytes from 172.217.20.195, icmp_seq=26 ttl=53 time=38ms +ping 2, timeout 0, total payload 64 bytes, 1946 ms +``` + + +**Description:** (from *linux man 8 ping*) + +> `ping` uses the ICMP protocol's mandatory ECHO_REQUEST datagram to elicit an ICMP ECHO_RESPONSE from a host or gateway. ECHO_REQUEST datagrams (''pings'') have an IP and ICMP header, followed by a struct timeval and then an arbitrary number of ''pad'' bytes used to fill out the packet. + + +**Usage variants** + +ping host **by IP-Adress:** +``` +net_info.ping("1.2.3.4") +``` +Enter IP-Adress in commonly known dotted quad-decimal notation, enclosed in string quotes. + +!!! Note + There is only limited error checking on the validity of IP adresses in lwIP. Check twice! + + +Use **DNS resolver** to get IP adress for ping: +``` +net_info.ping("hostname") +``` + +!!! Note + This only works with Network access and DNS resolution properly configured. + + +Custom **ping count**: +``` +net_info.ping(host, count) +``` +* `host` can be given by IP or hostname as above. +* `count` number of repetitive pings - default is 4 if omitted. + + +Ping **callback function**: +``` +net_info.ping(host, count, callback) +``` +Instead of printing ping results on the console, the given callback function ist called each time a ECHO_RESPONSE is received. + +The callback receives the following arguments: +``` +function ping_recv(bytes, ipaddr, seqno, ttl, ping) +``` +* length of message +* ip-address of pinged host +* icmp_seq number +* time-to-live-value +* ping time in ms + +!!! Caution + The callback functionality is still untested. Use at even more your own risk! + +For further reference to callback functionality, see smcl origin link provided on top of this page. \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index 4d704d1751..7a99fc1617 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -83,6 +83,7 @@ pages: - 'mdns': 'modules/mdns.md' - 'mqtt': 'modules/mqtt.md' - 'net': 'modules/net.md' + - 'net_info': 'en/modules/net_info.md' - 'node': 'modules/node.md' - 'ow (1-Wire)': 'modules/ow.md' - 'pcm' : 'modules/pcm.md'