Skip to content

Commit 0fe0952

Browse files
author
vsky
committed
Net_info module exposing ping function initial commit
1 parent 314ca4f commit 0fe0952

File tree

4 files changed

+206
-0
lines changed

4 files changed

+206
-0
lines changed

app/include/user_modules.h

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
//#define LUA_USE_MODULES_MDNS
4040
#define LUA_USE_MODULES_MQTT
4141
#define LUA_USE_MODULES_NET
42+
//#define LUA_USE_MODULES_NET_INFO
4243
#define LUA_USE_MODULES_NODE
4344
#define LUA_USE_MODULES_OW
4445
//#define LUA_USE_MODULES_PCM

app/modules/net_info.c

+147
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
// ***************************************************************************
2+
// net_info module for ESP8266 with nodeMCU
3+
//
4+
// Written by Lukas Voborsky (@voborsky) with great help by TerryE
5+
// ***************************************************************************
6+
7+
// #define NODE_DEBUG
8+
#define LUA_USE_MODULES_NET_INFO
9+
10+
#include "module.h"
11+
#include "lauxlib.h"
12+
13+
#include "lwip/ip_addr.h"
14+
#include "espconn.h"
15+
#include "lwip/dns.h"
16+
#include "lwip/app/ping.h"
17+
18+
/*
19+
ping_opt needs to be the first element of the structure. It is a workaround to pass the
20+
callback reference and self_ref to the ping_received function. Pointer the ping_option
21+
structure is equal to the pointer to net_info_ping_t structure.
22+
*/
23+
typedef struct {
24+
struct ping_option ping_opt;
25+
uint32_t ping_callback_ref;
26+
} net_info_ping_t;
27+
typedef net_info_ping_t* ping_t;
28+
29+
/*
30+
* ping_received_sent(pingresp)
31+
*/
32+
#define LuaCBfunc lua_upvalueindex(1)
33+
#define nipUD lua_upvalueindex(2)
34+
35+
static int ping_received_sent(lua_State *L) {
36+
struct ping_resp *resp = (struct ping_resp *) lua_touserdata (L, 1);
37+
ping_t nip = (ping_t) lua_touserdata (L, nipUD);
38+
lua_pushvalue(L, LuaCBfunc);
39+
40+
NODE_DBG("[net_info ping_received_sent] nip = %p\n", nip);
41+
42+
if (resp == NULL) { /* resolution failed so call the CB with 0 byte count to flag this */
43+
luaL_unref(L, LUA_REGISTRYINDEX, nip->ping_callback_ref);
44+
lua_pushinteger(L, 0);
45+
luaL_pcallx(L, 1, 0);
46+
return 0;
47+
}
48+
char ipaddrstr[16];
49+
ipaddr_ntoa_r((ip_addr_t *) &nip->ping_opt.ip, ipaddrstr, sizeof(ipaddrstr));
50+
51+
if (resp->total_count == 0) { /* processing receive response */
52+
NODE_DBG("[ping_received] %s: resp_time=%d seqno=%d bytes=%d ping_err=%d\n",
53+
ipaddrstr, resp->resp_time, resp->seqno, resp->bytes, resp->ping_err);
54+
lua_pushinteger(L, resp->bytes);
55+
lua_pushstring(L, ipaddrstr);
56+
lua_pushinteger(L, resp->seqno);
57+
lua_pushinteger(L, resp->ping_err == 0 ? resp->resp_time: -1);
58+
luaL_pcallx(L, 4, 0);
59+
} else { /* sent completion is currently a No-op */
60+
NODE_DBG("[ping_sent] %s: total_count=%d timeout_count=%d "
61+
"total_bytes=%d total_time=%d\n",
62+
ipaddrstr, resp->total_count, resp->timeout_count,
63+
resp->total_bytes, resp->total_time);
64+
luaL_unref(L, LUA_REGISTRYINDEX, nip->ping_callback_ref);
65+
}
66+
return 0;
67+
}
68+
69+
70+
/*
71+
* Wrapper to call ping_received_sent(pingresp)
72+
*/
73+
static void ping_CB(net_info_ping_t *nip, struct ping_resp *pingresp) {
74+
NODE_DBG("[net_info ping_CB] nip = %p, nip->ping_callback_ref = %p, pingresp= %p\n", nip, nip->ping_callback_ref, pingresp);
75+
lua_State *L = lua_getstate();
76+
lua_rawgeti(L, LUA_REGISTRYINDEX, nip->ping_callback_ref);
77+
lua_pushlightuserdata(L, pingresp);
78+
lua_call(L, 1, 0); // call the closure (ping_received_sent)
79+
}
80+
81+
/*
82+
* Wrapper to call ping_start using fully resolve IP4 address
83+
*/
84+
static void net_info_ping_raw(const char *name, ip_addr_t *ipaddr, ping_t nip) {
85+
NODE_DBG("[net_info_ping_raw] name = %s, ipaddr= %x\n", name, ipaddr);
86+
if (ipaddr) {
87+
char ipaddrstr[16];
88+
ipaddr_ntoa_r(ipaddr, ipaddrstr, sizeof(ipaddrstr));
89+
NODE_DBG("[net_info_ping_raw] ip: %s\n", ipaddrstr);
90+
}
91+
lua_State *L = lua_getstate();
92+
93+
if (!ipaddr || ipaddr->addr == 0xFFFFFFFF) {
94+
ping_CB(nip, NULL); /* A NULL pinresp flags DNS resolution failure */
95+
return;
96+
}
97+
98+
nip->ping_opt.ip = ipaddr->addr;
99+
NODE_DBG("[net_info_ping_raw] calling ping_start\n");
100+
if (!ping_start(&(nip->ping_opt))) {
101+
luaL_unref(L, LUA_REGISTRYINDEX, nip->ping_callback_ref);
102+
luaL_error(L, "memory allocation error: cannot start ping");
103+
}
104+
}
105+
106+
// Lua: net_info.ping(target, [count], responseCB)
107+
static int net_info_ping(lua_State *L)
108+
{
109+
ip_addr_t addr;
110+
111+
// retrieve function parameters
112+
const char *ping_target = luaL_checkstring(L, 1);
113+
bool isf2 = lua_isfunction(L, 2);
114+
lua_Integer l_count = isf2 ? 0: luaL_optinteger(L, 2, 0); /* use ping_start() default */
115+
lua_settop(L, isf2 ? 2 : 3);
116+
luaL_argcheck(L, lua_isfunction(L, -1), -1, "no callback specified");
117+
118+
ping_t nip = (ping_t) memset(lua_newuserdata(L, sizeof(*nip)), 0, sizeof(*nip));
119+
120+
/* Register C closure with 2 Upvals: (1) Lua CB function; (2) nip Userdata */
121+
lua_pushcclosure(L, ping_received_sent, 2); // stack has nip UD, responseCB; [-n, +1, m]
122+
123+
nip->ping_callback_ref = luaL_ref(L, LUA_REGISTRYINDEX); // registers the closure to registry [-1, +0, m]
124+
nip->ping_opt.count = l_count;
125+
nip->ping_opt.coarse_time = 0;
126+
nip->ping_opt.recv_function = (ping_recv_function) &ping_CB;
127+
nip->ping_opt.sent_function = (ping_sent_function) &ping_CB;
128+
129+
NODE_DBG("[net_info_ping] nip = %p, nip->ping_callback_ref = %p\n", nip, nip->ping_callback_ref);
130+
131+
err_t err = dns_gethostbyname(ping_target, &addr, (dns_found_callback) net_info_ping_raw, nip);
132+
if (err != ERR_OK && err != ERR_INPROGRESS) {
133+
luaL_unref(L, LUA_REGISTRYINDEX, nip->ping_callback_ref);
134+
return luaL_error(L, "lwip error %d", err);
135+
}
136+
if (err == ERR_OK) {
137+
NODE_DBG("[net_info_ping] No DNS resolution needed\n");
138+
net_info_ping_raw(ping_target, &addr, nip);
139+
}
140+
return 0;
141+
}
142+
143+
LROT_BEGIN(net_info, NULL, 0)
144+
LROT_FUNCENTRY( ping, net_info_ping )
145+
LROT_END( net_info, NULL, 0 )
146+
147+
NODEMCU_MODULE(NET_INFO, "net_info", net_info, NULL);

docs/modules/net_info.md

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# net_info Module
2+
| Since | Origin / Contributor | Maintainer | Source |
3+
| :----- | :-------------------- | :---------- | :------ |
4+
| 2020-05-03 | [vsky279](https://github.com/vsky279) | [vsky279](https://github.com/vsky279) | [net_info.c](../../../app/modules/net_info.c)
5+
6+
This module is a wrapper for common network diagnostic and analysis tools.
7+
8+
## net_info Module Methods
9+
10+
### ni:ping()
11+
12+
This is a function to ping a server. A callback function is called when response is received.
13+
14+
#### Syntax
15+
`ni:ping(ip_address, [count], callback)`
16+
17+
#### Parameters
18+
- `domain` destination domain or IP address
19+
- `count` number of ping packets to be sent (optional parameter, default value is 4)
20+
- `callback(bytes, ipaddr, seqno, rtt)` callback function which is invoked when response is received where
21+
- `bytes` number of bytes received from destination server (0 means no response)
22+
- `ipaddr` destination serve IP address
23+
- `seqno` ICMP sequence number (does not increment when no response is received)
24+
- `rtt` round trip time in ms
25+
26+
If domain name cannot be resolved callback is invoked with `bytes` parameter equal to 0 (i.e. no response) and `nil` values for all other parameters.
27+
28+
#### Returns
29+
`nil`
30+
31+
#### Example
32+
```lua
33+
net_info.ping("www.nodemcu.com", function (b, ip, sq, tm)
34+
if ip then print(("%d bytes from %s, icmp_seq=%d time=%dms"):format(b, ip, sq, tm)) else print("Invalid IP address") end
35+
end)
36+
```
37+
38+
Multiple pings can start in short sequence thought if the new ping overlaps with the previous one the first stops receiving answers, i.e.
39+
```lua
40+
function ping_resp(b, ip, sq, tm)
41+
print(string.format("%d bytes from %s, icmp_seq=%d time=%dms", b, ip, sq, tm))
42+
end
43+
44+
net_info.ping("8.8.8.8", 4, ping_resp)
45+
tmr.create():alarm(1000, tmr.ALARM_SINGLE, function() net_info.ping("8.8.4.4", 4, ping_resp) end)
46+
```
47+
gives
48+
```
49+
32 bytes from 8.8.8.8, icmp_seq=9 time=14ms
50+
32 bytes from 8.8.8.8, icmp_seq=10 time=9ms
51+
32 bytes from 8.8.4.4, icmp_seq=11 time=6ms
52+
32 bytes from 8.8.4.4, icmp_seq=13 time=12ms
53+
0 bytes from 8.8.8.8, icmp_seq=0 time=0ms
54+
32 bytes from 8.8.4.4, icmp_seq=15 time=16ms
55+
0 bytes from 8.8.8.8, icmp_seq=0 time=0ms
56+
32 bytes from 8.8.4.4, icmp_seq=16 time=7ms
57+
```

mkdocs.yml

+1
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ pages:
9292
- 'mdns': 'modules/mdns.md'
9393
- 'mqtt': 'modules/mqtt.md'
9494
- 'net': 'modules/net.md'
95+
- 'net_info': 'modules/net_info.md'
9596
- 'node': 'modules/node.md'
9697
- 'ow (1-Wire)': 'modules/ow.md'
9798
- 'pcm' : 'modules/pcm.md'

0 commit comments

Comments
 (0)