Skip to content

Commit 9eacf44

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

File tree

4 files changed

+209
-0
lines changed

4 files changed

+209
-0
lines changed

app/include/user_modules.h

Lines changed: 1 addition & 0 deletions
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

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
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+
// ip_addr_t source_ip;
50+
// source_ip.addr = nip->ping_opt.ip;
51+
// ipaddr_ntoa_r(&source_ip, ipaddrstr, sizeof(ipaddrstr));
52+
ipaddr_ntoa_r((ip_addr_t *) &nip->ping_opt.ip, ipaddrstr, sizeof(ipaddrstr));
53+
54+
if (resp->total_count == 0) { /* processing receive response */
55+
NODE_DBG("[ping_received] %s: resp_time=%d seqno=%d bytes=%d ping_err=%d\n",
56+
ipaddrstr, resp->resp_time, resp->seqno, resp->bytes, resp->ping_err);
57+
lua_pushinteger(L, resp->bytes);
58+
lua_pushstring(L, ipaddrstr);
59+
lua_pushinteger(L, resp->seqno);
60+
lua_pushinteger(L, resp->ping_err == 0 ? resp->resp_time: -1);
61+
luaL_pcallx(L, 4, 0);
62+
} else { /* sent completion is currently a No-op */
63+
NODE_DBG("[ping_sent] %s: total_count=%d timeout_count=%d "
64+
"total_bytes=%d total_time=%d\n",
65+
ipaddrstr, resp->total_count, resp->timeout_count,
66+
resp->total_bytes, resp->total_time);
67+
luaL_unref(L, LUA_REGISTRYINDEX, nip->ping_callback_ref);
68+
}
69+
return 0;
70+
}
71+
72+
73+
/*
74+
* Wrapper to call ping_received_sent(pingresp)
75+
*/
76+
static void ping_CB(net_info_ping_t *nip, struct ping_resp *pingresp) {
77+
NODE_DBG("[net_info ping_CB] nip = %p, nip->ping_callback_ref = %p, pingresp= %p\n", nip, nip->ping_callback_ref, pingresp);
78+
lua_State *L = lua_getstate();
79+
lua_rawgeti(L, LUA_REGISTRYINDEX, nip->ping_callback_ref);
80+
lua_pushlightuserdata(L, pingresp);
81+
lua_call(L, 1, 0); // call the closure (ping_received_sent)
82+
}
83+
84+
/*
85+
* Wrapper to call ping_start using fully resolve IP4 address
86+
*/
87+
static void net_info_ping_raw(const char *name, ip_addr_t *ipaddr, ping_t nip) {
88+
NODE_DBG("[net_info_ping_raw] name = %s, ipaddr= %x\n", name, ipaddr);
89+
if (ipaddr) {
90+
char ipaddrstr[16];
91+
ipaddr_ntoa_r(ipaddr, ipaddrstr, sizeof(ipaddrstr));
92+
NODE_DBG("[net_info_ping_raw] ip: %s\n", ipaddrstr);
93+
}
94+
lua_State *L = lua_getstate();
95+
96+
if (!ipaddr || ipaddr->addr == 0xFFFFFFFF) {
97+
ping_CB(nip, NULL); /* A NULL pinresp flags DNS resolution failure */
98+
return;
99+
}
100+
101+
nip->ping_opt.ip = ipaddr->addr;
102+
NODE_DBG("[net_info_ping_raw] calling ping_start\n");
103+
if (!ping_start(&(nip->ping_opt))) {
104+
luaL_unref(L, LUA_REGISTRYINDEX, nip->ping_callback_ref);
105+
luaL_error(L, "memory allocation error: cannot start ping");
106+
}
107+
}
108+
109+
// Lua: net_info.ping(target, [count], responseCB)
110+
static int net_info_ping(lua_State *L)
111+
{
112+
ip_addr_t addr;
113+
114+
// retrieve function parameters
115+
const char *ping_target = luaL_checkstring(L, 1);
116+
bool isf2 = lua_isfunction(L, 2);
117+
lua_Integer l_count = isf2 ? 0: luaL_optinteger(L, 2, 0); /* use ping_start() default */
118+
lua_settop(L, isf2 ? 2 : 3);
119+
luaL_argcheck(L, lua_isfunction(L, -1), -1, "no callback specified");
120+
121+
ping_t nip = (ping_t) memset(lua_newuserdata(L, sizeof(*nip)), 0, sizeof(*nip));
122+
123+
/* Register C closure with 2 Upvals: (1) Lua CB function; (2) nip Userdata */
124+
lua_pushcclosure(L, ping_received_sent, 2); // stack has nip UD, responseCB; [-n, +1, m]
125+
126+
nip->ping_callback_ref = luaL_ref(L, LUA_REGISTRYINDEX); // registers the closure to registry [-1, +0, m]
127+
nip->ping_opt.count = l_count;
128+
nip->ping_opt.coarse_time = 0;
129+
nip->ping_opt.recv_function = (ping_recv_function) &ping_CB;
130+
nip->ping_opt.sent_function = (ping_sent_function) &ping_CB;
131+
132+
NODE_DBG("[net_info_ping] nip = %p, nip->ping_callback_ref = %p\n", nip, nip->ping_callback_ref);
133+
134+
err_t err = dns_gethostbyname(ping_target, &addr, (dns_found_callback) net_info_ping_raw, nip);
135+
if (err != ERR_OK && err != ERR_INPROGRESS) {
136+
luaL_unref(L, LUA_REGISTRYINDEX, nip->ping_callback_ref);
137+
return luaL_error(L, "lwip error %d", err);
138+
}
139+
if (err == ERR_OK) {
140+
NODE_DBG("[net_info_ping] No DNS resolution needed\n");
141+
net_info_ping_raw(ping_target, &addr, nip);
142+
}
143+
return 0;
144+
}
145+
146+
LROT_BEGIN(net_info, NULL, 0)
147+
LROT_FUNCENTRY( ping, net_info_ping )
148+
LROT_END( net_info, NULL, 0 )
149+
150+
NODEMCU_MODULE(NET_INFO, "net_info", net_info, NULL);

docs/modules/net_info.md

Lines changed: 57 additions & 0 deletions
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

Lines changed: 1 addition & 0 deletions
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)