MeterLogger
ping.c
Go to the documentation of this file.
1 /**
2  * @file
3  * Ping sender module
4  *
5  */
6 
7 /*
8  * Redistribution and use in source and binary forms, with or without modification,
9  * are permitted provided that the following conditions are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright notice,
12  * this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright notice,
14  * this list of conditions and the following disclaimer in the documentation
15  * and/or other materials provided with the distribution.
16  * 3. The name of the author may not be used to endorse or promote products
17  * derived from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
20  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
22  * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
24  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
27  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
28  * OF SUCH DAMAGE.
29  *
30  * This file is part of the lwIP TCP/IP stack.
31  *
32  */
33 
34 /**
35  * This is an example of a "ping" sender (with raw API and socket API).
36  * It can be used as a start point to maintain opened a network connection, or
37  * like a network "watchdog" for your device.
38  *
39  */
40 
41 /*
42  * copyright (c) 2010 - 2011 Espressif System
43  */
44 
45 #include "lwip/opt.h"
46 
47 #if LWIP_RAW /* don't build if not configured for use in lwipopts.h */
48 
49 #include "lwip/mem.h"
50 #include "lwip/raw.h"
51 #include "lwip/icmp.h"
52 #include "lwip/netif.h"
53 #include "lwip/sys.h"
54 #include "lwip/timers.h"
55 #include "lwip/inet_chksum.h"
56 #include "os_type.h"
57 #include "osapi.h"
58 
59 #include "lwip/app/ping.h"
60 
61 #if PING_USE_SOCKETS
62 #include "lwip/sockets.h"
63 #include "lwip/inet.h"
64 #endif /* PING_USE_SOCKETS */
65 
66 #ifdef MEMLEAK_DEBUG
67 static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__;
68 #endif
69 
70 /* ping variables */
71 static u16_t ping_seq_num = 0;
72 static u32_t ping_time;
73 
74 static void ICACHE_FLASH_ATTR ping_timeout(void* arg)
75 {
76  struct ping_msg *pingmsg = (struct ping_msg *)arg;
77  pingmsg->timeout_count ++;
78  if (pingmsg->ping_opt->recv_function == NULL){
79  os_printf("ping timeout\n");
80  } else {
81  struct ping_resp pingresp;
82  os_bzero(&pingresp, sizeof(struct ping_resp));
83  pingresp.ping_err = -1;
84  pingmsg->ping_opt->recv_function(pingmsg->ping_opt, (void*)&pingresp);
85  }
86 }
87 
88 /** Prepare a echo ICMP request */
89 static void ICACHE_FLASH_ATTR
90 ping_prepare_echo( struct icmp_echo_hdr *iecho, u16_t len)
91 {
92  size_t i = 0;
93  size_t data_len = len - sizeof(struct icmp_echo_hdr);
94 
95  ICMPH_TYPE_SET(iecho, ICMP_ECHO);
96  ICMPH_CODE_SET(iecho, 0);
97  iecho->chksum = 0;
98  iecho->id = PING_ID;
99  ++ ping_seq_num;
100  if (ping_seq_num == 0x7fff)
101  ping_seq_num = 0;
102 
103  iecho->seqno = htons(ping_seq_num);
104 
105  /* fill the additional data buffer with some data */
106  for(i = 0; i < data_len; i++) {
107  ((char*)iecho)[sizeof(struct icmp_echo_hdr) + i] = (char)i;
108  }
109 
110  iecho->chksum = inet_chksum(iecho, len);
111 }
112 
113 static void ICACHE_FLASH_ATTR
114 ping_prepare_er(struct icmp_echo_hdr *iecho, u16_t len)
115 {
116 
117  ICMPH_TYPE_SET(iecho, ICMP_ER);
118  ICMPH_CODE_SET(iecho, 0);
119  iecho->chksum = 0;
120 
121  iecho->chksum = inet_chksum(iecho, len);
122 }
123 
124 /* Ping using the raw ip */
125 static u8_t ICACHE_FLASH_ATTR
126 ping_recv(void *arg, struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *addr)
127 {
128  struct icmp_echo_hdr *iecho = NULL;
129  static u16_t seqno = 0;
130  struct ping_msg *pingmsg = (struct ping_msg*)arg;
131 
132  LWIP_UNUSED_ARG(arg);
133  LWIP_UNUSED_ARG(pcb);
134  LWIP_UNUSED_ARG(addr);
135  LWIP_ASSERT("p != NULL", p != NULL);
136 
137  if (pbuf_header( p, -PBUF_IP_HLEN)==0) {
138  iecho = (struct icmp_echo_hdr *)p->payload;
139 
140  if ((iecho->id == PING_ID) && (iecho->seqno == htons(ping_seq_num)) && iecho->type == ICMP_ER) {
141  LWIP_DEBUGF( PING_DEBUG, ("ping: recv "));
143  LWIP_DEBUGF( PING_DEBUG, (" %"U32_F" ms\n", (sys_now()-ping_time)));
144  if (iecho->seqno != seqno){
145  /* do some ping result processing */
146  {
147  struct ip_hdr *iphdr = NULL;
148  char ipaddrstr[16];
149  ip_addr_t source_ip;
150  sys_untimeout(ping_timeout, pingmsg);
151  os_bzero(&source_ip, sizeof(ip_addr_t));
152  os_bzero(ipaddrstr, sizeof(ipaddrstr));
153  uint32 delay = system_relative_time(pingmsg->ping_sent);
154  delay /= PING_COARSE;
155  iphdr = (struct ip_hdr*)((u8*)iecho - PBUF_IP_HLEN);
156  source_ip.addr = iphdr->src.addr;
157  ipaddr_ntoa_r(&source_ip,ipaddrstr, sizeof(ipaddrstr));
158  if (pingmsg->ping_opt->recv_function == NULL){
159  os_printf("recv %s: byte = %d, time = %d ms, seq = %d\n",ipaddrstr, PING_DATA_SIZE, delay, ntohs(iecho->seqno));
160  } else {
161  struct ping_resp pingresp;
162  os_bzero(&pingresp, sizeof(struct ping_resp));
163  pingresp.bytes = PING_DATA_SIZE;
164  pingresp.resp_time = delay;
165  pingresp.seqno = ntohs(iecho->seqno);
166  pingresp.ping_err = 0;
167  pingmsg->ping_opt->recv_function(pingmsg->ping_opt,(void*) &pingresp);
168  }
169  }
170  seqno = iecho->seqno;
171  }
172 
173  PING_RESULT(1);
174  pbuf_free(p);
175  return 1; /* eat the packet */
176  }
177 // } else if(iecho->type == ICMP_ECHO){
178 // struct pbuf *q = NULL;
179 // os_printf("receive ping request:seq=%d\n", ntohs(iecho->seqno));
180 // q = pbuf_alloc(PBUF_IP, (u16_t)p->tot_len, PBUF_RAM);
181 // if (q!=NULL) {
182 // pbuf_copy(q, p);
183 // iecho = (struct icmp_echo_hdr *)q->payload;
184 // ping_prepare_er(iecho, q->tot_len);
185 // raw_sendto(pcb, q, addr);
186 // pbuf_free(q);
187 // }
188 // pbuf_free(p);
189 // return 1;
190 // }
191  }
192 
193  return 0; /* don't eat the packet */
194 }
195 
196 static void ICACHE_FLASH_ATTR
197 ping_send(struct raw_pcb *raw, ip_addr_t *addr)
198 {
199  struct pbuf *p = NULL;
200  struct icmp_echo_hdr *iecho = NULL;
201  size_t ping_size = sizeof(struct icmp_echo_hdr) + PING_DATA_SIZE;
202 
203  LWIP_DEBUGF( PING_DEBUG, ("ping: send "));
205  LWIP_DEBUGF( PING_DEBUG, ("\n"));
206  LWIP_ASSERT("ping_size <= 0xffff", ping_size <= 0xffff);
207 
208  p = pbuf_alloc(PBUF_IP, (u16_t)ping_size, PBUF_RAM);
209  if (!p) {
210  return;
211  }
212  if ((p->len == p->tot_len) && (p->next == NULL)) {
213  iecho = (struct icmp_echo_hdr *)p->payload;
214 
215  ping_prepare_echo(iecho, (u16_t)ping_size);
216 
217  raw_sendto(raw, p, addr);
218  ping_time = sys_now();
219  }
220  pbuf_free(p);
221 }
222 
223 static void ICACHE_FLASH_ATTR
224 ping_coarse_tmr(void *arg)
225 {
226  struct ping_msg *pingmsg = (struct ping_msg*)arg;
227  struct ping_option *ping_opt= NULL;
228  struct ping_resp pingresp;
229  ip_addr_t ping_target;
230 
231  LWIP_ASSERT("ping_timeout: no pcb given!", pingmsg != NULL);
232  ping_target.addr = pingmsg->ping_opt->ip;
233  ping_opt = pingmsg->ping_opt;
234  if (--pingmsg->sent_count != 0){
235  pingmsg ->ping_sent = system_get_time();
236  ping_send(pingmsg->ping_pcb, &ping_target);
237 
238  sys_timeout(PING_TIMEOUT_MS, ping_timeout, pingmsg);
239  sys_timeout(pingmsg->coarse_time, ping_coarse_tmr, pingmsg);
240  } else {
241  uint32 delay = system_relative_time(pingmsg->ping_start);
242  delay /= PING_COARSE;
243 // ping_seq_num = 0;
244  if (ping_opt->sent_function == NULL){
245  os_printf("ping %d, timeout %d, total payload %d bytes, %d ms\n",
246  pingmsg->max_count, pingmsg->timeout_count, PING_DATA_SIZE*(pingmsg->max_count - pingmsg->timeout_count),delay);
247  } else {
248  os_bzero(&pingresp, sizeof(struct ping_resp));
249  pingresp.total_count = pingmsg->max_count;
250  pingresp.timeout_count = pingmsg->timeout_count;
251  pingresp.total_bytes = PING_DATA_SIZE*(pingmsg->max_count - pingmsg->timeout_count);
252  pingresp.total_time = delay;
253  pingresp.ping_err = 0;
254  }
255  sys_untimeout(ping_coarse_tmr, pingmsg);
256  raw_remove(pingmsg->ping_pcb);
257  os_free(pingmsg);
258  if (ping_opt->sent_function != NULL)
259  ping_opt->sent_function(ping_opt,(uint8*)&pingresp);
260  }
261 }
262 
263 static bool ICACHE_FLASH_ATTR
264 ping_raw_init(struct ping_msg *pingmsg)
265 {
266  if (pingmsg == NULL)
267  return false;
268 
269  ip_addr_t ping_target;
270  pingmsg->ping_pcb = raw_new(IP_PROTO_ICMP);
271  LWIP_ASSERT("ping_pcb != NULL", pingmsg->ping_pcb != NULL);
272 
273  raw_recv(pingmsg->ping_pcb, ping_recv, pingmsg);
274  raw_bind(pingmsg->ping_pcb, IP_ADDR_ANY);
275 
276  ping_target.addr = pingmsg->ping_opt->ip;
277  pingmsg ->ping_sent = system_get_time();
278  ping_send(pingmsg->ping_pcb, &ping_target);
279 
280  sys_timeout(PING_TIMEOUT_MS, ping_timeout, pingmsg);
281  sys_timeout(pingmsg->coarse_time, ping_coarse_tmr, pingmsg);
282  return true;
283 }
284 
286 ping_start(struct ping_option *ping_opt)
287 {
288  struct ping_msg *pingmsg = NULL;
289  pingmsg = (struct ping_msg *)os_zalloc(sizeof(struct ping_msg));
290  if (pingmsg == NULL || ping_opt == NULL)
291  return false;
292 
293  pingmsg->ping_opt = ping_opt;
294  if (ping_opt->count != 0)
295  pingmsg->max_count = ping_opt->count;
296  else
298 
299  if (ping_opt->coarse_time != 0)
300  pingmsg->coarse_time = ping_opt->coarse_time * PING_COARSE;
301  else
302  pingmsg->coarse_time = PING_COARSE;
303 
304  pingmsg->ping_start = system_get_time();
305  pingmsg->sent_count = pingmsg->max_count;
306  return ping_raw_init(pingmsg);
307 }
308 
310 ping_regist_recv(struct ping_option *ping_opt, ping_recv_function ping_recv)
311 {
312  if (ping_opt == NULL)
313  return false;
314 
315  ping_opt ->recv_function = ping_recv;
316  return true;
317 }
318 
321 {
322  if (ping_opt == NULL)
323  return false;
324 
325  ping_opt ->sent_function = ping_sent;
326  return true;
327 }
328 
329 #endif /* LWIP_RAW */
#define PING_RESULT(ping_ok)
Definition: ping.h:41
#define IP_PROTO_ICMP
Definition: ip.h:52
u16_t tot_len
Definition: pbuf.h:90
bool ping_regist_recv(struct ping_option *ping_opt, ping_recv_function ping_recv)
#define ICMP_ECHO
Definition: icmp.h:48
struct pbuf * next
Definition: pbuf.h:78
u16_t len
Definition: pbuf.h:93
u16_t inet_chksum(void *dataptr, u16_t len) ICACHE_FLASH_ATTR
Definition: inet_chksum.c:396
void(* ping_sent_function)(void *arg, void *pdata)
Definition: ping.h:48
#define ICMP_ER
Definition: icmp.h:44
ping_recv_function recv_function
Definition: ping.h:54
#define NULL
Definition: def.h:47
#define ip_addr_debug_print(debug, ipaddr)
Definition: ip_addr.h:212
struct ping_option * ping_opt
Definition: ping.h:60
uint32 max_count
Definition: ping.h:65
#define PBUF_IP_HLEN
Definition: pbuf.h:48
#define htons(x)
Definition: def.h:81
const ip_addr_t ip_addr_any ICACHE_RODATA_ATTR
Definition: ip_addr.c:44
#define ICACHE_FLASH_ATTR
Definition: c_types.h:99
uint32 coarse_time
Definition: ping.h:67
ping_sent_function sent_function
Definition: ping.h:55
Definition: pbuf.h:58
Definition: ping.h:59
static u32_t sys_now(void) ICACHE_FLASH_ATTR
Definition: sys.h:235
uint32 system_get_time(void)
#define os_printf
Definition: osapi.h:62
uint32 ip
Definition: ping.h:52
#define os_zalloc(s)
Definition: mem.h:44
void sys_timeout(u32_t msecs, sys_timeout_handler handler, void *arg) ICACHE_FLASH_ATTR
#define U32_F
Definition: cc.h:65
unsigned long u32_t
Definition: cc.h:56
#define LWIP_DEBUGF(debug, message)
Definition: debug.h:94
struct raw_pcb * ping_pcb
Definition: ping.h:61
#define PING_COARSE
Definition: ping.h:26
#define ICMPH_CODE_SET(hdr, c)
Definition: icmp.h:100
#define PING_ID
Definition: ping.h:31
Definition: pbuf.h:76
unsigned char uint8
Definition: c_types.h:45
typedefPACK_STRUCT_END struct ip_addr ip_addr_t
Definition: ip_addr.h:64
uint32 count
Definition: ping.h:51
uint32 sent_count
Definition: ping.h:66
char * ipaddr_ntoa_r(const ip_addr_t *addr, char *buf, int buflen) ICACHE_FLASH_ATTR
Definition: ip_addr.c:293
void sys_untimeout(sys_timeout_handler handler, void *arg) ICACHE_FLASH_ATTR
#define IP_ADDR_ANY
Definition: ip_addr.h:92
#define os_free(s)
Definition: mem.h:40
uint32 coarse_time
Definition: ping.h:53
unsigned char u8
Definition: c_types.h:46
unsigned int uint32
Definition: c_types.h:54
uint32 ping_sent
Definition: ping.h:63
u8_t pbuf_free(struct pbuf *p) ICACHE_FLASH_ATTR
Definition: pbuf.c:685
#define PING_DATA_SIZE
Definition: ping.h:36
uint32 ping_start
Definition: ping.h:62
Definition: ping.h:70
Definition: ip.h:116
u8_t pbuf_header(struct pbuf *p, s16_t header_size) ICACHE_FLASH_ATTR
Definition: pbuf.c:573
unsigned char u8_t
Definition: cc.h:52
void(* ping_recv_function)(void *arg, void *pdata)
Definition: ping.h:47
uint32 timeout_count
Definition: ping.h:64
#define LWIP_ASSERT(message, assertion)
Definition: debug.h:65
void * payload
Definition: pbuf.h:81
#define PING_TIMEOUT_MS
Definition: ping.h:45
#define DEFAULT_PING_MAX_COUNT
Definition: ping.h:44
Definition: pbuf.h:52
#define os_bzero
Definition: osapi.h:31
#define PING_DEBUG
Definition: ping.h:16
bool ping_start(struct ping_option *ping_opt)
bool ping_regist_sent(struct ping_option *ping_opt, ping_sent_function ping_sent)
#define LWIP_UNUSED_ARG(x)
Definition: arch.h:73
#define ntohs(x)
Definition: def.h:82
struct pbuf * pbuf_alloc(pbuf_layer l, u16_t length, pbuf_type type) ICACHE_FLASH_ATTR
Definition: pbuf.c:234
unsigned short u16_t
Definition: cc.h:54
#define ICMPH_TYPE_SET(hdr, t)
Definition: icmp.h:99