Skip to content

Commit 7a77d76

Browse files
vsky279marcelstoer
authored andcommitted
net_info module - ping function (#2854)
* Net_info module exposing ping function initial commit * Ping as a part of net module * Sent callback implemented * Add NET_PING_ENABLE macro Authored-by: vsky <[email protected]> with support from TerryE
1 parent a0864c4 commit 7a77d76

File tree

5 files changed

+234
-0
lines changed

5 files changed

+234
-0
lines changed

app/include/user_config.h

+3
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,9 @@
136136
//#define TIMER_SUSPEND_ENABLE
137137
//#define PMSLEEP_ENABLE
138138

139+
// The net module optionally offers net info functionnality. Uncomment the following
140+
// to enable the functionnality.
141+
#define NET_PING_ENABLE
139142

140143
// The WiFi module optionally offers an enhanced level of WiFi connection
141144
// management, using internal timer callbacks. Whilst many Lua developers

app/modules/net.c

+5
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
#include "lwip/udp.h"
2121
#include "lwip/dhcp.h"
2222

23+
#include "net_ping.h"
24+
2325
typedef enum net_type {
2426
TYPE_TCP_SERVER = 0,
2527
TYPE_TCP_CLIENT,
@@ -1070,6 +1072,9 @@ LROT_BEGIN(net, NULL, 0)
10701072
LROT_FUNCENTRY( ifinfo, net_ifinfo )
10711073
LROT_FUNCENTRY( multicastJoin, net_multicastJoin )
10721074
LROT_FUNCENTRY( multicastLeave, net_multicastLeave )
1075+
#ifdef NET_PING_ENABLE
1076+
LROT_FUNCENTRY( ping, net_ping )
1077+
#endif
10731078
LROT_TABENTRY( dns, net_dns_map )
10741079
#ifdef TLS_MODULE_PRESENT
10751080
LROT_TABENTRY( cert, tls_cert )

app/modules/net_ping.c

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

app/modules/net_ping.h

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#ifdef NET_PING_ENABLE
2+
int net_ping(lua_State *L);
3+
#endif

docs/modules/net.md

+68
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,74 @@ Sets the IP of the DNS server used to resolve hostnames. Default: resolver1.open
626626
#### See also
627627
[`net.dns:getdnsserver()`](#netdnsgetdnsserver)
628628

629+
630+
### net.ping()
631+
632+
Pings a server. A callback function is called when response is or is not received. Summary statistics can be retrieved via the second callback.
633+
634+
The function can be disabled by commenting `NET_PING_ENABLE` macro in `user_config.h` when more compact build is needed.
635+
636+
#### Syntax
637+
`net.ping(domain, [count], callback_received, [callback_sent])`
638+
639+
#### Parameters
640+
- `domain` destination domain or IP address
641+
- `count` number of ping packets to be sent (optional parameter, default value is 4)
642+
- `callback_received(bytes, ipaddr, seqno, rtt)` callback function which is invoked when response is received where
643+
- `bytes` number of bytes received from destination server (0 means no response)
644+
- `ipaddr` destination server IP address
645+
- `seqno` ICMP sequence number
646+
- `rtt` round trip time in ms
647+
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.
648+
649+
- `callback_sent(ipaddr, total_count, timeout_count, total_bytes, total_time)` callback function which is invoked when response is received where
650+
- `ipaddrstr` destination server IP address
651+
- `total_count` total number of packets sent
652+
- `timeout_count` total number of packets lost (not received)
653+
- `total_bytes` total number of bytes received from destination server
654+
- `total_time` total time to perform ping
655+
656+
#### Returns
657+
`nil`
658+
659+
#### Example
660+
```lua
661+
net.ping("www.nodemcu.com", function (b, ip, sq, tm)
662+
if ip then print(("%d bytes from %s, icmp_seq=%d time=%dms"):format(b, ip, sq, tm)) else print("Invalid IP address") end
663+
end)
664+
net.ping("www.nodemcu.com", 10, function (b, ip, sq, tm)
665+
if ip then print(("%d bytes from %s, icmp_seq=%d time=%dms"):format(b, ip, sq, tm)) else print("Invalid IP address") end
666+
end)
667+
net.ping("www.nodemcu.com", function (b, ip, sq, tm)
668+
if ip then print(("%d bytes from %s, icmp_seq=%d time=%dms"):format(b, ip, sq, tm)) else print("Invalid IP address") end
669+
end,
670+
function (ip, tc, toc, tb, tt)
671+
print(("--- %s ping statistics ---\n%d packets transmitted, %d received, %d%% packet loss, time %dms"):format(ip, tc, tc-toc, toc/tc*100, tt))
672+
end)
673+
```
674+
675+
Multiple pings can start in short sequence thought if the new ping overlaps with the previous one the first stops receiving answers, i.e.
676+
```lua
677+
function ping_resp(b, ip, sq, tm)
678+
print(string.format("%d bytes from %s, icmp_seq=%d time=%dms", b, ip, sq, tm))
679+
end
680+
681+
net.ping("8.8.8.8", 4, ping_resp)
682+
tmr.create():alarm(1000, tmr.ALARM_SINGLE, function() net.ping("8.8.4.4", 4, ping_resp) end)
683+
```
684+
gives
685+
```
686+
32 bytes from 8.8.8.8, icmp_seq=9 time=14ms
687+
32 bytes from 8.8.8.8, icmp_seq=10 time=9ms
688+
32 bytes from 8.8.4.4, icmp_seq=11 time=6ms
689+
32 bytes from 8.8.4.4, icmp_seq=13 time=12ms
690+
0 bytes from 8.8.8.8, icmp_seq=0 time=0ms -- no more answers received
691+
32 bytes from 8.8.4.4, icmp_seq=15 time=16ms
692+
0 bytes from 8.8.8.8, icmp_seq=0 time=0ms -- no more answers received
693+
32 bytes from 8.8.4.4, icmp_seq=16 time=7ms
694+
```
695+
696+
629697
# net.cert Module
630698

631699
This part gone to the [TLS](tls.md) module, link kept for backward compatibility.

0 commit comments

Comments
 (0)