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 #include "lwip/opt.h"
42 #include <unistd.h>
43 
44 #if LWIP_RAW /* don't build if not configured for use in lwipopts.h */
45 
46 #include "lwip/apps/ping.h"
47 
48 #include <stdlib.h>
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/netdb.h"
55 #include "lwip/timeouts.h"
56 #include "lwip/inet_chksum.h"
57 #include "lwip/prot/ip4.h"
58 #include "lwip/sockets.h"
59 
60 #if PING_USE_SOCKETS
61 #include "lwip/inet.h"
62 #include <string.h>
63 #endif /* PING_USE_SOCKETS */
64 
65 #define LWIP_IPV6 0
66 
67 /** ping receive timeout - in milliseconds */
68 #ifndef PING_RCV_TIMEO
69 #define PING_RCV_TIMEO 1000
70 #endif
71 
72 /** ping delay - in milliseconds */
73 #ifndef PING_DELAY
74 #define PING_DELAY     1000
75 #endif
76 
77 /** ping identifier - must fit on a u16_t */
78 #ifndef PING_ID
79 #define PING_ID        0xAFAF
80 #endif
81 
82 /** ping additional data size to include in the packet */
83 #ifndef PING_DATA_SIZE
84 #define PING_DATA_SIZE 32
85 #endif
86 
87 /** ping result action - no default action */
88 #ifndef PING_RESULT
89 #define PING_RESULT(ping_ok)
90 #endif
91 
92 #ifndef PING_STACK_SIZE
93 #define PING_STACK_SIZE   (1024*20)
94 #endif
95 
96 /* ping variables */
97 static int32_t ping_send_count = -1;
98 static int ping_rcv_timeo = PING_RCV_TIMEO;
99 static int ping_delay = PING_DELAY;
100 static int ping_data_size = PING_DATA_SIZE;
101 static const ip_addr_t* ping_target;
102 static ip_addr_t addr;
103 static u16_t ping_seq_num;
104 #ifdef LWIP_DEBUG
105 static u32_t ping_time;
106 #endif /* LWIP_DEBUG */
107 #if !PING_USE_SOCKETS
108 static struct raw_pcb *ping_pcb;
109 #endif /* PING_USE_SOCKETS */
110 #if PING_USE_SOCKETS
111 static char ping_started = 0;
112 static char ping_do_exit = 0;
113 #endif
114 /** Prepare a echo ICMP request */
115 static void
ping_prepare_echo(struct icmp_echo_hdr * iecho,u16_t len)116 ping_prepare_echo( struct icmp_echo_hdr *iecho, u16_t len)
117 {
118   size_t i;
119   size_t data_len = len - sizeof(struct icmp_echo_hdr);
120 
121   ICMPH_TYPE_SET(iecho, ICMP_ECHO);
122   ICMPH_CODE_SET(iecho, 0);
123   iecho->chksum = 0;
124   iecho->id     = PING_ID;
125   iecho->seqno  = lwip_htons(++ping_seq_num);
126 
127   /* fill the additional data buffer with some data */
128   for(i = 0; i < data_len; i++) {
129     ((char*)iecho)[sizeof(struct icmp_echo_hdr) + i] = (char)i;
130   }
131 
132   iecho->chksum = inet_chksum(iecho, len);
133 }
134 
135 #if PING_USE_SOCKETS
136 
137 /* Ping using the socket ip */
138 static err_t
ping_send(int s,const ip_addr_t * addr)139 ping_send(int s, const ip_addr_t *addr)
140 {
141   int err;
142   struct icmp_echo_hdr *iecho;
143   struct sockaddr_storage to;
144   size_t ping_size = sizeof(struct icmp_echo_hdr) + ping_data_size;
145   LWIP_ASSERT("ping_size is too big", ping_size <= 0xffff);
146 
147 #if LWIP_IPV6
148   if(IP_IS_V6(addr) && !ip6_addr_isipv6mappedipv4(ip_2_ip6(addr))) {
149     /* todo: support ICMP6 echo */
150     return ERR_VAL;
151   }
152 #endif /* LWIP_IPV6 */
153 
154   iecho = (struct icmp_echo_hdr *)mem_malloc((mem_size_t)ping_size);
155   if (!iecho) {
156     return ERR_MEM;
157   }
158 
159   ping_prepare_echo(iecho, (u16_t)ping_size);
160 
161 #if LWIP_IPV4
162   if(IP_IS_V4(addr)) {
163     struct sockaddr_in *to4 = (struct sockaddr_in*)&to;
164     to4->sin_len    = sizeof(to4);
165     to4->sin_family = AF_INET;
166     inet_addr_from_ipaddr(&to4->sin_addr, ip_2_ip4(addr));
167   }
168 #endif /* LWIP_IPV4 */
169 
170 #if LWIP_IPV6
171   if(IP_IS_V6(addr)) {
172     struct sockaddr_in6 *to6 = (struct sockaddr_in6*)&to;
173     to6->sin6_len    = sizeof(to6);
174     to6->sin6_family = AF_INET6;
175     inet6_addr_from_ip6addr(&to6->sin6_addr, ip_2_ip6(addr));
176   }
177 #endif /* LWIP_IPV6 */
178 
179   err = lwip_sendto(s, iecho, ping_size, 0, (struct sockaddr*)&to, sizeof(to));
180   mem_free(iecho);
181 
182   return (err ? ERR_OK : ERR_VAL);
183 }
184 
185 static void
ping_recv(int s)186 ping_recv(int s)
187 {
188   char buf[64];
189   int len;
190   struct sockaddr_storage from;
191   int fromlen = sizeof(from);
192 
193   while((len = lwip_recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr*)&from, (socklen_t*)&fromlen)) > 0) {
194     if (len >= (int)(sizeof(struct ip_hdr)+sizeof(struct icmp_echo_hdr))) {
195       ip_addr_t fromaddr;
196       memset(&fromaddr, 0, sizeof(fromaddr));
197 
198 #if LWIP_IPV4
199       if(from.ss_family == AF_INET) {
200         struct sockaddr_in *from4 = (struct sockaddr_in*)&from;
201         inet_addr_to_ipaddr(ip_2_ip4(&fromaddr), &from4->sin_addr);
202         IP_SET_TYPE_VAL(fromaddr, IPADDR_TYPE_V4);
203       }
204 #endif /* LWIP_IPV4 */
205 
206 #if LWIP_IPV6
207       if(from.ss_family == AF_INET6) {
208         struct sockaddr_in6 *from6 = (struct sockaddr_in6*)&from;
209         inet6_addr_to_ip6addr(ip_2_ip6(&fromaddr), &from6->sin6_addr);
210         IP_SET_TYPE_VAL(fromaddr, IPADDR_TYPE_V6);
211       }
212 #endif /* LWIP_IPV6 */
213 
214       LWIP_DEBUGF( PING_DEBUG, ("ping: recv "));
215       ip_addr_debug_print_val(PING_DEBUG, fromaddr);
216       LWIP_DEBUGF( PING_DEBUG, (" %"U32_F" ms\n", (sys_now() - ping_time)));
217 
218       /* todo: support ICMP6 echo */
219 #if LWIP_IPV4
220       if (IP_IS_V4_VAL(fromaddr)) {
221         struct ip_hdr *iphdr;
222         struct icmp_echo_hdr *iecho;
223 
224         iphdr = (struct ip_hdr *)buf;
225         iecho = (struct icmp_echo_hdr *)(buf + (IPH_HL(iphdr) * 4));
226         if ((iecho->id == PING_ID) && (iecho->seqno == lwip_htons(ping_seq_num))) {
227           /* do some ping result processing */
228           PING_RESULT((ICMPH_TYPE(iecho) == ICMP_ER));
229           return;
230         } else {
231           LWIP_DEBUGF( PING_DEBUG, ("ping: drop\n"));
232         }
233       }
234 #endif /* LWIP_IPV4 */
235     }
236     fromlen = sizeof(from);
237   }
238 
239   if (len == 0) {
240     LWIP_DEBUGF( PING_DEBUG, ("ping: recv - %"U32_F" ms - timeout\n", (sys_now()-ping_time)));
241   }
242 
243   /* do some ping result processing */
244   PING_RESULT(0);
245 }
246 
247 static void
ping_thread(void * arg)248 ping_thread(void *arg)
249 {
250   int s;
251   int ret;
252   int32_t count = ping_send_count;
253 
254 #if LWIP_SO_SNDRCVTIMEO_NONSTANDARD
255   int timeout = ping_rcv_timeo;
256 #else
257   struct timeval timeout;
258   timeout.tv_sec = ping_rcv_timeo/1000;
259   timeout.tv_usec = (ping_rcv_timeo%1000)*1000;
260 #endif
261   LWIP_UNUSED_ARG(arg);
262 #if LWIP_IPV6
263   if(IP_IS_V4(ping_target) || ip6_addr_isipv6mappedipv4(ip_2_ip6(ping_target))) {
264     s = lwip_socket(AF_INET6, SOCK_RAW, IP_PROTO_ICMP);
265   } else {
266     s = lwip_socket(AF_INET6, SOCK_RAW, IP6_NEXTH_ICMP6);
267   }
268 #else
269   s = lwip_socket(AF_INET,  SOCK_RAW, IP_PROTO_ICMP);
270 #endif
271   if (s < 0) {
272     LWIP_DEBUGF( PING_DEBUG, ("ping: create socket failed"));
273     goto exit;
274   }
275 
276   ret = lwip_setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
277   LWIP_ASSERT("setting receive timeout failed", ret == 0);
278   LWIP_UNUSED_ARG(ret);
279 
280   while (1){
281     if(count >= 0) {
282         if(count == 0) {
283             LWIP_DEBUGF( PING_DEBUG, ("ping: send %"U32_F"times finished", ping_send_count));
284             goto exit;
285         }
286         count --;
287     }
288     if(ping_do_exit) {
289         LWIP_DEBUGF( PING_DEBUG, ("ping: recv ping exit command"));
290         goto exit;
291     }
292     if (ping_send(s, ping_target) == ERR_OK) {
293       LWIP_DEBUGF( PING_DEBUG, ("ping: send "));
294       ip_addr_debug_print(PING_DEBUG, ping_target);
295       LWIP_DEBUGF( PING_DEBUG, ("\n"));
296 
297 #ifdef LWIP_DEBUG
298       ping_time = sys_now();
299 #endif /* LWIP_DEBUG */
300       ping_recv(s);
301     } else {
302       LWIP_DEBUGF( PING_DEBUG, ("ping: send "));
303       ip_addr_debug_print(PING_DEBUG, ping_target);
304       LWIP_DEBUGF( PING_DEBUG, (" - error\n"));
305     }
306     sys_msleep(ping_delay);
307   }
308 exit:
309     if (s >= 0) {
310       lwip_close(s);
311     }
312     ping_started = 0;
313     ping_do_exit = 0;
314     aos_task_exit(0);
315 }
316 
317 #else /* PING_USE_SOCKETS */
318 
319 /* Ping using the raw ip */
320 static u8_t
ping_recv(void * arg,struct raw_pcb * pcb,struct pbuf * p,const ip_addr_t * addr)321 ping_recv(void *arg, struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *addr)
322 {
323   struct icmp_echo_hdr *iecho;
324   LWIP_UNUSED_ARG(arg);
325   LWIP_UNUSED_ARG(pcb);
326   LWIP_UNUSED_ARG(addr);
327   LWIP_ASSERT("p != NULL", p != NULL);
328 
329   if ((p->tot_len >= (PBUF_IP_HLEN + sizeof(struct icmp_echo_hdr))) &&
330       pbuf_remove_header(p, PBUF_IP_HLEN) == 0) {
331     iecho = (struct icmp_echo_hdr *)p->payload;
332 
333     if ((iecho->id == PING_ID) && (iecho->seqno == lwip_htons(ping_seq_num))) {
334       LWIP_DEBUGF( PING_DEBUG, ("ping: recv "));
335       ip_addr_debug_print(PING_DEBUG, addr);
336       LWIP_DEBUGF( PING_DEBUG, (" %"U32_F" ms\n", (sys_now()-ping_time)));
337 
338       /* do some ping result processing */
339       PING_RESULT(1);
340       pbuf_free(p);
341       return 1; /* eat the packet */
342     }
343     /* not eaten, restore original packet */
344     pbuf_add_header(p, PBUF_IP_HLEN);
345   }
346 
347   return 0; /* don't eat the packet */
348 }
349 
350 static void
ping_send(struct raw_pcb * raw,const ip_addr_t * addr)351 ping_send(struct raw_pcb *raw, const ip_addr_t *addr)
352 {
353   struct pbuf *p;
354   struct icmp_echo_hdr *iecho;
355   size_t ping_size = sizeof(struct icmp_echo_hdr) + ping_data_size;
356 
357   LWIP_DEBUGF( PING_DEBUG, ("ping: send "));
358   ip_addr_debug_print(PING_DEBUG, addr);
359   LWIP_DEBUGF( PING_DEBUG, ("\n"));
360   LWIP_ASSERT("ping_size <= 0xffff", ping_size <= 0xffff);
361 
362   p = pbuf_alloc(PBUF_IP, (u16_t)ping_size, PBUF_RAM);
363   if (!p) {
364     return;
365   }
366   if ((p->len == p->tot_len) && (p->next == NULL)) {
367     iecho = (struct icmp_echo_hdr *)p->payload;
368 
369     ping_prepare_echo(iecho, (u16_t)ping_size);
370 
371     raw_sendto(raw, p, addr);
372 #ifdef LWIP_DEBUG
373     ping_time = sys_now();
374 #endif /* LWIP_DEBUG */
375   }
376   pbuf_free(p);
377 }
378 
379 static void
ping_timeout(void * arg)380 ping_timeout(void *arg)
381 {
382   struct raw_pcb *pcb = (struct raw_pcb*)arg;
383 
384   LWIP_ASSERT("ping_timeout: no pcb given!", pcb != NULL);
385 
386   ping_send(pcb, ping_target);
387 
388   sys_timeout(ping_delay, ping_timeout, pcb);
389 }
390 
391 static void
ping_raw_init(void)392 ping_raw_init(void)
393 {
394   ping_pcb = raw_new(IP_PROTO_ICMP);
395   LWIP_ASSERT("ping_pcb != NULL", ping_pcb != NULL);
396 
397   raw_recv(ping_pcb, ping_recv, NULL);
398   raw_bind(ping_pcb, IP_ADDR_ANY);
399   sys_timeout(ping_delay, ping_timeout, ping_pcb);
400 }
401 
402 void
ping_send_now(void)403 ping_send_now(void)
404 {
405   LWIP_ASSERT("ping_pcb != NULL", ping_pcb != NULL);
406   ping_send(ping_pcb, ping_target);
407 }
408 
409 #endif /* PING_USE_SOCKETS */
410 
411 void
ping_init(const ip_addr_t * ping_addr)412 ping_init(const ip_addr_t* ping_addr)
413 {
414   ping_target = ping_addr;
415 
416 #if PING_USE_SOCKETS
417   if(!ping_started) {
418       ping_started = 1;
419       aos_task_new("ping_thread", ping_thread, NULL, PING_STACK_SIZE);
420   }
421   else {
422       LWIP_DEBUGF( PING_DEBUG, ("ping: ping task is already running\n"));
423       LWIP_DEBUGF( PING_DEBUG, ("ping: use command \"ping -e\" to exit running ping task\n"));
424       LWIP_DEBUGF( PING_DEBUG, ("ping: and then run Ping command again\n"));
425   }
426 #else /* PING_USE_SOCKETS */
427   ping_raw_init();
428 #endif /* PING_USE_SOCKETS */
429 }
430 
ping_run(int argc,char ** argv)431 void ping_run( int argc, char **argv )
432 {
433     int i;
434     int32_t temp;
435     struct addrinfo hints, *res;
436 
437     for(i = 0; i < argc; i++) {
438         if ( strcmp( (char *) argv[i], "-c" ) == 0 ) {
439             i++;
440             if((i < argc) && ((temp = atoi(argv[i])) > 0)) {
441                 ping_send_count = temp;
442             }
443             else {
444                 LWIP_DEBUGF( PING_DEBUG, ("ping: bad number of packets to transmit\n"));
445                 return;
446             }
447         }
448         else if ( strcmp( (char *) argv[i], "-i" ) == 0 ) {
449             i++;
450             if((i < argc) && ((temp = atoi(argv[i])) > 0)) {
451                 ping_delay = temp;
452             }
453             else {
454                 LWIP_DEBUGF( PING_DEBUG, ("ping: bad timing interval\n"));
455                 return;
456             }
457         }
458         else if ( strcmp( (char *) argv[i], "-w" ) == 0 ) {
459             i++;
460             if((i < argc) && ((temp = atoi(argv[i])) > 0)) {
461                 ping_rcv_timeo = temp;
462             }
463             else {
464                 LWIP_DEBUGF( PING_DEBUG, ("ping: bad recv timeout\n"));
465                 return;
466             }
467         }
468         else if ( strcmp( (char *) argv[i], "-s" ) == 0 ) {
469             i++;
470             if((i < argc) && ((temp = atoi(argv[i])) > 0)) {
471                 ping_data_size = temp;
472             }
473             else {
474                 LWIP_DEBUGF( PING_DEBUG, ("ping: bad packet data size\n"));
475                 return;
476             }
477         }
478         else if ( strcmp( (char *) argv[i], "-e" ) == 0 ) {
479             ping_do_exit = 1;
480             return ;
481         }
482         else if ( argc != i + 1){
483            extern void _cli_ping_help_cmd( int argc, char **argv );
484            LWIP_DEBUGF( PING_DEBUG, ("ping: Invalid command format\n"));
485            _cli_ping_help_cmd( 0, NULL );
486            return;
487         }
488     }
489 
490     memset(&hints, 0, sizeof(struct addrinfo));
491     hints.ai_family = AF_UNSPEC;
492     hints.ai_socktype = SOCK_STREAM;
493     hints.ai_flags = AI_CANONNAME;
494     hints.ai_protocol = 0;
495 
496     if((temp = lwip_getaddrinfo(argv[argc-1], NULL, &hints, &res)) != 0) {
497         LWIP_DEBUGF( PING_DEBUG, ("ping: Getaddrinfo error%"U32_F"\n", temp));
498         return;
499     }
500 
501     memset(&addr, 0, sizeof(ip_addr_t));
502 #if LWIP_IPV4
503     if(((struct sockaddr*)(res->ai_addr))->sa_family == AF_INET) {
504       struct sockaddr_in *addr4_in = (struct sockaddr_in*)(res->ai_addr);
505       inet_addr_to_ipaddr(ip_2_ip4(&addr), &addr4_in->sin_addr);
506       IP_SET_TYPE_VAL(addr, IPADDR_TYPE_V4);
507     }
508 #endif /* LWIP_IPV4 */
509 
510 #if LWIP_IPV6
511     if(((struct sockaddr*)(res->ai_addr))->sa_family == AF_INET6) {
512       struct sockaddr_in6 *addr6_in = (struct sockaddr_in6*)(res->ai_addr);
513       inet6_addr_to_ip6addr(ip_2_ip6(&addr), &addr6_in->sin6_addr);
514       IP_SET_TYPE_VAL(addr, IPADDR_TYPE_V6);
515     }
516 #endif /* LWIP_IPV6 */
517 
518     lwip_freeaddrinfo(res);
519 
520     ping_init(&addr);
521 }
522 #endif /* LWIP_RAW */
523