Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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_INFO_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_info.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_INFO_ENABLE
LROT_FUNCENTRY( ping, net_info_ping )
#endif
LROT_TABENTRY( dns, net_dns_map )
#ifdef TLS_MODULE_PRESENT
LROT_TABENTRY( cert, tls_cert )
Expand Down
142 changes: 142 additions & 0 deletions app/modules/net_info.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
// ***************************************************************************
// net_info module for ESP8266 with nodeMCU
//
// Written by Lukas Voborsky (@voborsky) with great help by TerryE
// ***************************************************************************

// #define NODE_DEBUG

#include "net_info.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_info_ping_t structure.
*/
typedef struct {
struct ping_option ping_opt;
uint32_t ping_callback_ref;
} net_info_ping_t;
typedef net_info_ping_t* ping_t;

/*
* ping_received_sent(pingresp)
*/
#define LuaCBfunc lua_upvalueindex(1)
#define nipUD lua_upvalueindex(2)

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);
lua_pushvalue(L, LuaCBfunc);

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_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_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 { /* sent completion is currently a No-op */
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);
luaL_unref(L, LUA_REGISTRYINDEX, nip->ping_callback_ref);
}
return 0;
}


/*
* Wrapper to call ping_received_sent(pingresp)
*/
static void ping_CB(net_info_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_info_ping_raw(const char *name, ip_addr_t *ipaddr, ping_t nip) {
NODE_DBG("[net_info_ping_raw] name = %s, ipaddr= %x\n", name, ipaddr);
if (ipaddr) {
char ipaddrstr[16];
ipaddr_ntoa_r(ipaddr, ipaddrstr, sizeof(ipaddrstr));
NODE_DBG("[net_info_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_info_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_info_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 ? 2 : 3);
luaL_argcheck(L, lua_isfunction(L, -1), -1, "no callback specified");

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

/* Register C closure with 2 Upvals: (1) Lua CB function; (2) nip Userdata */
lua_pushcclosure(L, ping_received_sent, 2); // stack has nip UD, callback; [-n, +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_info_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_info_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_info_ping] No DNS resolution needed\n");
net_info_ping_raw(ping_target, &addr, nip);
}
return 0;
}
3 changes: 3 additions & 0 deletions app/modules/net_info.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#ifdef NET_INFO_ENABLE
int net_info_ping(lua_State *L);
#endif
55 changes: 55 additions & 0 deletions docs/modules/net.md
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,61 @@ Sets the IP of the DNS server used to resolve hostnames. Default: resolver1.open
#### See also
[`net.dns:getdnsserver()`](#netdnsgetdnsserver)


### net.ping()

This is a function to ping a server. A callback function is called when response is or is not received.

The function is disabled by default to enable this functionnality define `NET_INFO_ENABLE` macro needs to be defined in `user_config.h`.

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

#### Parameters
- `domain` destination domain or IP address
- `count` number of ping packets to be sent (optional parameter, default value is 4)
- `callback(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 serve 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.

#### 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)
```

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
32 bytes from 8.8.4.4, icmp_seq=15 time=16ms
0 bytes from 8.8.8.8, icmp_seq=0 time=0ms
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.
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ pages:
- 'mdns': 'modules/mdns.md'
- 'mqtt': 'modules/mqtt.md'
- 'net': 'modules/net.md'
- 'net_info': 'modules/net_info.md'
- 'node': 'modules/node.md'
- 'ow (1-Wire)': 'modules/ow.md'
- 'pcm' : 'modules/pcm.md'
Expand Down