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