xref: /nrf52832-nimble/rt-thread/components/net/lwip-2.1.0/src/apps/ping/ping.c (revision 104654410c56c573564690304ae786df310c91fc)
1 /*
2  * netutils: ping implementation
3  */
4 
5 #include "lwip/opt.h"
6 
7 #include "lwip/mem.h"
8 #include "lwip/icmp.h"
9 #include "lwip/netif.h"
10 #include "lwip/sys.h"
11 #include "lwip/sockets.h"
12 #include "lwip/inet.h"
13 #include "lwip/inet_chksum.h"
14 #include "lwip/ip.h"
15 
16 /**
17  * PING_DEBUG: Enable debugging for PING.
18  */
19 #ifndef PING_DEBUG
20 #define PING_DEBUG     LWIP_DBG_ON
21 #endif
22 
23 /** ping receive timeout - in milliseconds */
24 #define PING_RCV_TIMEO rt_tick_from_millisecond(2000)
25 /** ping delay - in milliseconds */
26 #define PING_DELAY     rt_tick_from_millisecond(1000)
27 
28 /** ping identifier - must fit on a u16_t */
29 #ifndef PING_ID
30 #define PING_ID        0xAFAF
31 #endif
32 
33 /** ping additional data size to include in the packet */
34 #ifndef PING_DATA_SIZE
35 #define PING_DATA_SIZE 32
36 #endif
37 
38 /* ping variables */
39 static u16_t ping_seq_num;
40 struct _ip_addr
41 {
42     rt_uint8_t addr0, addr1, addr2, addr3;
43 };
44 
45 /** Prepare a echo ICMP request */
ping_prepare_echo(struct icmp_echo_hdr * iecho,u16_t len)46 static void ping_prepare_echo( struct icmp_echo_hdr *iecho, u16_t len)
47 {
48     size_t i;
49     size_t data_len = len - sizeof(struct icmp_echo_hdr);
50 
51     ICMPH_TYPE_SET(iecho, ICMP_ECHO);
52     ICMPH_CODE_SET(iecho, 0);
53     iecho->chksum = 0;
54     iecho->id     = PING_ID;
55     iecho->seqno  = htons(++ping_seq_num);
56 
57     /* fill the additional data buffer with some data */
58     for(i = 0; i < data_len; i++)
59     {
60         ((char*)iecho)[sizeof(struct icmp_echo_hdr) + i] = (char)i;
61     }
62 
63     iecho->chksum = inet_chksum(iecho, len);
64 }
65 
66 /* Ping using the socket ip */
ping_send(int s,ip_addr_t * addr,int size)67 static err_t ping_send(int s, ip_addr_t *addr, int size)
68 {
69     int err;
70     struct icmp_echo_hdr *iecho;
71     struct sockaddr_in to;
72     size_t ping_size = sizeof(struct icmp_echo_hdr) + size;
73     LWIP_ASSERT("ping_size is too big", ping_size <= 0xffff);
74 
75     iecho = rt_malloc(ping_size);
76     if (iecho == RT_NULL)
77     {
78         return ERR_MEM;
79     }
80 
81     ping_prepare_echo(iecho, (u16_t)ping_size);
82 
83     to.sin_len = sizeof(to);
84     to.sin_family = AF_INET;
85     to.sin_addr.s_addr = addr->addr;
86 
87     err = lwip_sendto(s, iecho, ping_size, 0, (struct sockaddr*)&to, sizeof(to));
88     rt_free(iecho);
89 
90     return (err == ping_size ? ERR_OK : ERR_VAL);
91 }
92 
ping_recv(int s,int * ttl)93 static int ping_recv(int s, int *ttl)
94 {
95     char buf[64];
96     int fromlen = sizeof(struct sockaddr_in), len;
97     struct sockaddr_in from;
98     struct ip_hdr *iphdr;
99     struct icmp_echo_hdr *iecho;
100 
101     while((len = lwip_recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr*)&from, (socklen_t*)&fromlen)) > 0)
102     {
103         if (len >= (sizeof(struct ip_hdr)+sizeof(struct icmp_echo_hdr)))
104         {
105             iphdr = (struct ip_hdr *)buf;
106             iecho = (struct icmp_echo_hdr *)(buf+(IPH_HL(iphdr) * 4));
107             if ((iecho->id == PING_ID) && (iecho->seqno == htons(ping_seq_num)))
108             {
109                 *ttl = iphdr->_ttl;
110                 return len;
111             }
112         }
113     }
114 
115     return len;
116 }
117 
ping(char * target,rt_uint32_t times,rt_size_t size)118 rt_err_t ping(char* target, rt_uint32_t times, rt_size_t size)
119 {
120     int s, ttl, recv_len;
121     struct timeval timeout = { PING_RCV_TIMEO / RT_TICK_PER_SECOND, PING_RCV_TIMEO % RT_TICK_PER_SECOND };
122     ip_addr_t ping_target;
123     rt_uint32_t send_times;
124     rt_tick_t recv_start_tick;
125     struct _ip_addr
126     {
127         rt_uint8_t addr0, addr1, addr2, addr3;
128     } *addr;
129 
130     send_times = 0;
131     ping_seq_num = 0;
132 
133     if(size == 0)
134         size = PING_DATA_SIZE;
135 
136     if (inet_aton(target, &ping_target) == 0)
137     {
138         rt_kprintf("ping: unknown host %s\n", target);
139         return -RT_ERROR;
140     }
141     addr = (struct _ip_addr*)&ping_target;
142 
143     if ((s = lwip_socket(AF_INET, SOCK_RAW, IP_PROTO_ICMP)) < 0)
144     {
145         rt_kprintf("ping: create socket failled\n");
146         return -RT_ERROR;
147     }
148 
149     lwip_setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval));
150 
151     while (1)
152     {
153         if (ping_send(s, &ping_target, size) == ERR_OK)
154         {
155             recv_start_tick = rt_tick_get();
156             if ((recv_len = ping_recv(s, &ttl)) >= 0)
157             {
158                 rt_kprintf("%d bytes from %d.%d.%d.%d icmp_seq=%d ttl=%d time=%d ticks\n", recv_len, addr->addr0,
159                         addr->addr1, addr->addr2, addr->addr3, send_times, ttl, rt_tick_get() - recv_start_tick);
160             }
161             else
162             {
163                 rt_kprintf("From %d.%d.%d.%d icmp_seq=%d timeout\n", addr->addr0, addr->addr1, addr->addr2,
164                         addr->addr3, send_times);
165             }
166         }
167         else
168         {
169             rt_kprintf("Send %d.%d.%d.%d - error\n", addr->addr0, addr->addr1, addr->addr2, addr->addr3);
170         }
171 
172         send_times ++;
173         if (send_times >= times) break; /* send ping times reached, stop */
174 
175         rt_thread_delay(PING_DELAY); /* take a delay */
176     }
177 
178     lwip_close(s);
179 
180     return RT_EOK;
181 }
182 #ifdef RT_USING_FINSH
183 #include <finsh.h>
184 
185 FINSH_FUNCTION_EXPORT(ping, ping network host);
186 
cmd_ping(int argc,char ** argv)187 int cmd_ping(int argc, char **argv)
188 {
189     if (argc == 1)
190     {
191         rt_kprintf("Please input: ping <host address>\n");
192     }
193     else
194     {
195         ping(argv[1], 4, 0);
196     }
197 
198     return 0;
199 }
200 FINSH_FUNCTION_EXPORT_ALIAS(cmd_ping, __cmd_ping, ping network host);
201 #endif
202