Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

net_info module - ping function #2854

Merged
merged 4 commits into from
May 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions app/include/user_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,9 @@
//#define TIMER_SUSPEND_ENABLE
//#define PMSLEEP_ENABLE

// The net module optionally offers net info functionnality. Uncomment the following
// to enable the functionnality.
#define NET_PING_ENABLE

// The WiFi module optionally offers an enhanced level of WiFi connection
// management, using internal timer callbacks. Whilst many Lua developers
Expand Down
5 changes: 5 additions & 0 deletions app/modules/net.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
#include "lwip/udp.h"
#include "lwip/dhcp.h"

#include "net_ping.h"

typedef enum net_type {
TYPE_TCP_SERVER = 0,
TYPE_TCP_CLIENT,
Expand Down Expand Up @@ -1070,6 +1072,9 @@ LROT_BEGIN(net, NULL, 0)
LROT_FUNCENTRY( ifinfo, net_ifinfo )
LROT_FUNCENTRY( multicastJoin, net_multicastJoin )
LROT_FUNCENTRY( multicastLeave, net_multicastLeave )
#ifdef NET_PING_ENABLE
LROT_FUNCENTRY( ping, net_ping )
#endif
LROT_TABENTRY( dns, net_dns_map )
#ifdef TLS_MODULE_PRESENT
LROT_TABENTRY( cert, tls_cert )
Expand Down
155 changes: 155 additions & 0 deletions app/modules/net_ping.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
// ***************************************************************************
// net_ping functionnality for ESP8266 with nodeMCU
//
// Written by Lukas Voborsky (@voborsky) with great help by TerryE
// ***************************************************************************

// #define NODE_DEBUG

#include "net_ping.h"

#include "module.h"
#include "lauxlib.h"

#include "lwip/ip_addr.h"
#include "espconn.h"
#include "lwip/dns.h"
#include "lwip/app/ping.h"

/*
ping_opt needs to be the first element of the structure. It is a workaround to pass the
callback reference and self_ref to the ping_received function. Pointer the ping_option
structure is equal to the pointer to net_ping_t structure.
*/
typedef struct {
struct ping_option ping_opt;
uint32_t ping_callback_ref;
} net_ping_t;
typedef net_ping_t* ping_t;

/*
* ping_received_sent(pingresp)
*/
#define LuaCBreceivedfunc lua_upvalueindex(1)
#define LuaCBsentfunc lua_upvalueindex(2)
#define nipUD lua_upvalueindex(3)

static int ping_received_sent(lua_State *L) {
struct ping_resp *resp = (struct ping_resp *) lua_touserdata (L, 1);
ping_t nip = (ping_t) lua_touserdata (L, nipUD);

NODE_DBG("[net_info ping_received_sent] nip = %p\n", nip);

if (resp == NULL) { /* resolution failed so call the CB with 0 byte count to flag this */
luaL_unref(L, LUA_REGISTRYINDEX, nip->ping_callback_ref);
lua_pushvalue(L, LuaCBreceivedfunc);
lua_pushinteger(L, 0);
luaL_pcallx(L, 1, 0);
return 0;
}
char ipaddrstr[16];
ipaddr_ntoa_r((ip_addr_t *) &nip->ping_opt.ip, ipaddrstr, sizeof(ipaddrstr));

if (resp->total_count == 0) { /* processing receive response */
NODE_DBG("[ping_received] %s: resp_time=%d seqno=%d bytes=%d ping_err=%d\n",
ipaddrstr, resp->resp_time, resp->seqno, resp->bytes, resp->ping_err);
lua_pushvalue(L, LuaCBreceivedfunc);
lua_pushinteger(L, resp->bytes);
lua_pushstring(L, ipaddrstr);
lua_pushinteger(L, resp->seqno);
lua_pushinteger(L, resp->ping_err == 0 ? resp->resp_time: -1);
luaL_pcallx(L, 4, 0);
} else { /* processing sent response */
NODE_DBG("[ping_sent] %s: total_count=%d timeout_count=%d "
"total_bytes=%d total_time=%d\n",
ipaddrstr, resp->total_count, resp->timeout_count,
resp->total_bytes, resp->total_time);

lua_pushvalue(L, LuaCBsentfunc);
if lua_isfunction(L, -1) {
lua_pushstring(L, ipaddrstr);
lua_pushinteger(L, resp->total_count);
lua_pushinteger(L, resp->timeout_count);
lua_pushinteger(L, resp->total_bytes);
lua_pushinteger(L, resp->total_time);
luaL_pcallx(L, 5, 0);
}
luaL_unref(L, LUA_REGISTRYINDEX, nip->ping_callback_ref); /* unregister the closure */
}
return 0;
}


/*
* Wrapper to call ping_received_sent(pingresp)
*/
static void ping_CB(net_ping_t *nip, struct ping_resp *pingresp) {
NODE_DBG("[net_info ping_CB] nip = %p, nip->ping_callback_ref = %p, pingresp= %p\n", nip, nip->ping_callback_ref, pingresp);
lua_State *L = lua_getstate();
lua_rawgeti(L, LUA_REGISTRYINDEX, nip->ping_callback_ref);
lua_pushlightuserdata(L, pingresp);
lua_call(L, 1, 0); // call the closure (ping_received_sent)
}

/*
* Wrapper to call ping_start using fully resolve IP4 address
*/
static void net_ping_raw(const char *name, ip_addr_t *ipaddr, ping_t nip) {
NODE_DBG("[net_ping_raw] name = %s, ipaddr= %x\n", name, ipaddr);
if (ipaddr) {
char ipaddrstr[16];
ipaddr_ntoa_r(ipaddr, ipaddrstr, sizeof(ipaddrstr));
NODE_DBG("[net_ping_raw] ip: %s\n", ipaddrstr);
}
lua_State *L = lua_getstate();

if (!ipaddr || ipaddr->addr == 0xFFFFFFFF) {
ping_CB(nip, NULL); /* A NULL pinresp flags DNS resolution failure */
return;
}

nip->ping_opt.ip = ipaddr->addr;
NODE_DBG("[net_ping_raw] calling ping_start\n");
if (!ping_start(&(nip->ping_opt))) {
luaL_unref(L, LUA_REGISTRYINDEX, nip->ping_callback_ref);
luaL_error(L, "memory allocation error: cannot start ping");
}
}

// Lua: net.ping(domain, [count], callback)
int net_ping(lua_State *L)
{
ip_addr_t addr;

// retrieve function parameters
const char *ping_target = luaL_checkstring(L, 1);
bool isf2 = lua_isfunction(L, 2);
lua_Integer l_count = isf2 ? 0: luaL_optinteger(L, 2, 0); /* use ping_start() default */
lua_settop(L, isf2 ? 3 : 4);
luaL_argcheck(L, lua_isfunction(L, -2), -2, "no received callback specified");
luaL_argcheck(L, lua_isfunction(L, -1) || lua_isnil(L, -1), -1, "invalid sent callback, function expected");

ping_t nip = (ping_t) memset(lua_newuserdata(L, sizeof(*nip)), 0, sizeof(*nip));

/* Register C closure with 3 Upvals: (1) Lua CB receive function; (2) Lua CB sent function; (3) nip Userdata */
lua_pushcclosure(L, ping_received_sent, 3); // stack has 2 callbacks and nip UD; [-3, +1, m]

nip->ping_callback_ref = luaL_ref(L, LUA_REGISTRYINDEX); // registers the closure to registry [-1, +0, m]
nip->ping_opt.count = l_count;
nip->ping_opt.coarse_time = 0;
nip->ping_opt.recv_function = (ping_recv_function) &ping_CB;
nip->ping_opt.sent_function = (ping_sent_function) &ping_CB;

NODE_DBG("[net_ping] nip = %p, nip->ping_callback_ref = %p\n", nip, nip->ping_callback_ref);

err_t err = dns_gethostbyname(ping_target, &addr, (dns_found_callback) net_ping_raw, nip);
if (err != ERR_OK && err != ERR_INPROGRESS) {
luaL_unref(L, LUA_REGISTRYINDEX, nip->ping_callback_ref);
return luaL_error(L, "lwip error %d", err);
}
if (err == ERR_OK) {
NODE_DBG("[net_ping] No DNS resolution needed\n");
net_ping_raw(ping_target, &addr, nip);
}
return 0;
}
3 changes: 3 additions & 0 deletions app/modules/net_ping.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#ifdef NET_PING_ENABLE
int net_ping(lua_State *L);
#endif
68 changes: 68 additions & 0 deletions docs/modules/net.md
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,74 @@ Sets the IP of the DNS server used to resolve hostnames. Default: resolver1.open
#### See also
[`net.dns:getdnsserver()`](#netdnsgetdnsserver)


### net.ping()

Pings a server. A callback function is called when response is or is not received. Summary statistics can be retrieved via the second callback.

The function can be disabled by commenting `NET_PING_ENABLE` macro in `user_config.h` when more compact build is needed.

#### Syntax
`net.ping(domain, [count], callback_received, [callback_sent])`

#### Parameters
- `domain` destination domain or IP address
- `count` number of ping packets to be sent (optional parameter, default value is 4)
- `callback_received(bytes, ipaddr, seqno, rtt)` callback function which is invoked when response is received where
- `bytes` number of bytes received from destination server (0 means no response)
- `ipaddr` destination server IP address
- `seqno` ICMP sequence number
- `rtt` round trip time in ms
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.

- `callback_sent(ipaddr, total_count, timeout_count, total_bytes, total_time)` callback function which is invoked when response is received where
- `ipaddrstr` destination server IP address
- `total_count` total number of packets sent
- `timeout_count` total number of packets lost (not received)
- `total_bytes` total number of bytes received from destination server
- `total_time` total time to perform ping

#### Returns
`nil`

#### Example
```lua
net.ping("www.nodemcu.com", function (b, ip, sq, tm)
if ip then print(("%d bytes from %s, icmp_seq=%d time=%dms"):format(b, ip, sq, tm)) else print("Invalid IP address") end
end)
net.ping("www.nodemcu.com", 10, function (b, ip, sq, tm)
if ip then print(("%d bytes from %s, icmp_seq=%d time=%dms"):format(b, ip, sq, tm)) else print("Invalid IP address") end
end)
net.ping("www.nodemcu.com", function (b, ip, sq, tm)
if ip then print(("%d bytes from %s, icmp_seq=%d time=%dms"):format(b, ip, sq, tm)) else print("Invalid IP address") end
end,
function (ip, tc, toc, tb, tt)
print(("--- %s ping statistics ---\n%d packets transmitted, %d received, %d%% packet loss, time %dms"):format(ip, tc, tc-toc, toc/tc*100, tt))
end)
```

Multiple pings can start in short sequence thought if the new ping overlaps with the previous one the first stops receiving answers, i.e.
```lua
function ping_resp(b, ip, sq, tm)
print(string.format("%d bytes from %s, icmp_seq=%d time=%dms", b, ip, sq, tm))
end

net.ping("8.8.8.8", 4, ping_resp)
tmr.create():alarm(1000, tmr.ALARM_SINGLE, function() net.ping("8.8.4.4", 4, ping_resp) end)
```
gives
```
32 bytes from 8.8.8.8, icmp_seq=9 time=14ms
32 bytes from 8.8.8.8, icmp_seq=10 time=9ms
32 bytes from 8.8.4.4, icmp_seq=11 time=6ms
32 bytes from 8.8.4.4, icmp_seq=13 time=12ms
0 bytes from 8.8.8.8, icmp_seq=0 time=0ms -- no more answers received
32 bytes from 8.8.4.4, icmp_seq=15 time=16ms
0 bytes from 8.8.8.8, icmp_seq=0 time=0ms -- no more answers received
32 bytes from 8.8.4.4, icmp_seq=16 time=7ms
```


# net.cert Module

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