1 | /* This source file is part of the ATMEL AVR-UC3-SoftwareFramework-1.7.0 Release */ |
---|
2 | |
---|
3 | /* |
---|
4 | * Copyright (c) 2001-2003 Swedish Institute of Computer Science. |
---|
5 | * All rights reserved. |
---|
6 | * |
---|
7 | * Redistribution and use in source and binary forms, with or without modification, |
---|
8 | * are permitted provided that the following conditions are met: |
---|
9 | * |
---|
10 | * 1. Redistributions of source code must retain the above copyright notice, |
---|
11 | * this list of conditions and the following disclaimer. |
---|
12 | * 2. Redistributions in binary form must reproduce the above copyright notice, |
---|
13 | * this list of conditions and the following disclaimer in the documentation |
---|
14 | * and/or other materials provided with the distribution. |
---|
15 | * 3. The name of the author may not be used to endorse or promote products |
---|
16 | * derived from this software without specific prior written permission. |
---|
17 | * |
---|
18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
---|
19 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
---|
20 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT |
---|
21 | * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
---|
22 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT |
---|
23 | * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
---|
24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
---|
25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
---|
26 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY |
---|
27 | * OF SUCH DAMAGE. |
---|
28 | * |
---|
29 | * This file is derived from a part of the lwIP TCP/IP stack. |
---|
30 | * |
---|
31 | */ |
---|
32 | #ifdef PING_CMD |
---|
33 | #include "lwip/opt.h" |
---|
34 | |
---|
35 | #include "lwip/mem.h" |
---|
36 | #include "lwip/raw.h" |
---|
37 | #include "lwip/icmp.h" |
---|
38 | #include "lwip/netif.h" |
---|
39 | #include "lwip/sys.h" |
---|
40 | #include "lwip/sockets.h" |
---|
41 | #include "lwip/inet.h" |
---|
42 | #include "lwip/inet_chksum.h" |
---|
43 | |
---|
44 | #include "ping.h" |
---|
45 | #include "timer.h" |
---|
46 | #include "util.h" |
---|
47 | |
---|
48 | #include "getopt.h" |
---|
49 | |
---|
50 | #define PING_ID 0xAFAF |
---|
51 | |
---|
52 | struct ping_info_t { |
---|
53 | struct ip_addr destination; |
---|
54 | uint32_t deadline; /* -w (in seconds) */ |
---|
55 | uint32_t interval; /* -i (in ms) */ |
---|
56 | uint32_t timeout; /* ms */ |
---|
57 | uint32_t data_size; /* -s */ |
---|
58 | uint32_t count; /* -c, 0 means continous ping */ |
---|
59 | uint32_t size; |
---|
60 | uint32_t first_tx_tm; |
---|
61 | uint32_t last_tx_tm; |
---|
62 | uint32_t last_rx_tm; |
---|
63 | uint32_t num_tx; |
---|
64 | uint32_t num_rx; |
---|
65 | uint32_t flags; |
---|
66 | uint16_t seq_num; |
---|
67 | Bool quiet; /* -q */ |
---|
68 | ping_complete_cb_t complete_cb; |
---|
69 | void *ctx; |
---|
70 | #define PING_REPLY (1 << 0) |
---|
71 | }; |
---|
72 | |
---|
73 | static struct ping_info_t INFO; |
---|
74 | |
---|
75 | /** Prepare a echo ICMP request */ |
---|
76 | static void ping_prepare_echo(struct icmp_echo_hdr *iecho, |
---|
77 | struct ping_info_t* ping_info) |
---|
78 | { |
---|
79 | int i; |
---|
80 | |
---|
81 | ICMPH_TYPE_SET(iecho,ICMP_ECHO); |
---|
82 | ICMPH_CODE_SET(iecho, 0); |
---|
83 | iecho->chksum = 0; |
---|
84 | iecho->id = PING_ID; |
---|
85 | iecho->seqno = htons(++ping_info->seq_num); |
---|
86 | iecho->chksum = 0; |
---|
87 | |
---|
88 | /* fill the additional data buffer with some data */ |
---|
89 | for(i = 0; i < ping_info->data_size; i++) { |
---|
90 | ((char*)iecho)[sizeof(struct icmp_echo_hdr) + i] = i; |
---|
91 | } |
---|
92 | |
---|
93 | iecho->chksum = inet_chksum(iecho, ping_info->size); |
---|
94 | } |
---|
95 | |
---|
96 | /* Ping using the raw ip */ |
---|
97 | static u8_t ping_recv(void *arg, struct raw_pcb *pcb, struct pbuf *p, |
---|
98 | struct ip_addr *addr) |
---|
99 | { |
---|
100 | struct icmp_echo_hdr *iecho; |
---|
101 | struct ip_hdr *ip = p->payload; |
---|
102 | struct ping_info_t* ping_info = (struct ping_info_t*) arg; |
---|
103 | uint32_t us; |
---|
104 | |
---|
105 | if (pbuf_header( p, -PBUF_IP_HLEN)==0) { |
---|
106 | iecho = p->payload; |
---|
107 | |
---|
108 | if ((iecho->id == PING_ID) && |
---|
109 | (iecho->seqno == htons(ping_info->seq_num))) { |
---|
110 | ping_info->last_rx_tm = timer_get_ms(); |
---|
111 | ping_info->num_rx++; |
---|
112 | us = 1000 * |
---|
113 | (ping_info->last_rx_tm - ping_info->last_tx_tm); |
---|
114 | |
---|
115 | if (!ping_info->quiet) |
---|
116 | printk("%d bytes from %s: icmp_seq=%d ttl=%d " \ |
---|
117 | "time=%d.%03d ms\n", |
---|
118 | p->tot_len, ip2str(ip->src), |
---|
119 | iecho->seqno, |
---|
120 | IPH_TTL(ip), |
---|
121 | us / 1000, us % 1000); |
---|
122 | |
---|
123 | /* do some ping result processing */ |
---|
124 | ping_info->flags |= PING_REPLY; |
---|
125 | } |
---|
126 | } |
---|
127 | |
---|
128 | pbuf_free(p); |
---|
129 | return 1; /* eat the event */ |
---|
130 | } |
---|
131 | |
---|
132 | static void ping_send(struct raw_pcb *raw, struct ping_info_t* ping_info) |
---|
133 | { |
---|
134 | struct pbuf *p; |
---|
135 | struct icmp_echo_hdr *iecho; |
---|
136 | |
---|
137 | if (!(p = pbuf_alloc(PBUF_IP, ping_info->size, PBUF_RAM))) { |
---|
138 | return; |
---|
139 | } |
---|
140 | if ((p->len == p->tot_len) && (p->next == NULL)) { |
---|
141 | iecho = p->payload; |
---|
142 | |
---|
143 | ping_prepare_echo(iecho, ping_info); |
---|
144 | raw_sendto(raw, p, &ping_info->destination); |
---|
145 | |
---|
146 | if (!ping_info->first_tx_tm) |
---|
147 | ping_info->first_tx_tm = timer_get_ms(); |
---|
148 | ping_info->last_tx_tm = timer_get_ms(); |
---|
149 | ping_info->num_tx++; |
---|
150 | } |
---|
151 | pbuf_free(p); |
---|
152 | } |
---|
153 | |
---|
154 | void ping_set_callback(ping_complete_cb_t cb, void *ctx) { |
---|
155 | INFO.complete_cb = cb; |
---|
156 | INFO.ctx = ctx; |
---|
157 | } |
---|
158 | |
---|
159 | void ping_stop(uint32_t *tx_cnt, uint32_t *rx_cnt) { |
---|
160 | struct ping_info_t *ping_info = &INFO; |
---|
161 | |
---|
162 | *tx_cnt = ping_info->num_tx; |
---|
163 | *rx_cnt = ping_info->num_rx; |
---|
164 | ping_info->count = ping_info->num_tx; |
---|
165 | if ( 0 == ping_info->count ) { |
---|
166 | ping_info->count = 1; |
---|
167 | } |
---|
168 | } |
---|
169 | |
---|
170 | static int init_ping_info(int argc, char* argv[], struct ping_info_t* ping_info) |
---|
171 | { |
---|
172 | int c; |
---|
173 | ping_complete_cb_t cb; |
---|
174 | void *ctx; |
---|
175 | |
---|
176 | cb = ping_info->complete_cb; |
---|
177 | ctx = ping_info->ctx; |
---|
178 | memset(ping_info, 0, sizeof(struct ping_info_t)); |
---|
179 | ping_info->complete_cb = cb; |
---|
180 | ping_info->ctx = ctx; |
---|
181 | |
---|
182 | ping_info->deadline = 0; |
---|
183 | ping_info->interval = 1000; |
---|
184 | ping_info->timeout = 3000; |
---|
185 | ping_info->data_size = 32; |
---|
186 | ping_info->count = 3; |
---|
187 | ping_info->destination = |
---|
188 | netif_default ? netif_default->gw : ip_addr_any; |
---|
189 | |
---|
190 | optind = 1; |
---|
191 | while ((c = getopt(argc, argv, "c:i:s:w:q")) != -1) { |
---|
192 | switch (c) { |
---|
193 | case 'c': |
---|
194 | ping_info->count = atoi(optarg); |
---|
195 | break; |
---|
196 | |
---|
197 | case 'i': |
---|
198 | ping_info->interval = atoi(optarg); |
---|
199 | break; |
---|
200 | |
---|
201 | case 's': |
---|
202 | ping_info->data_size = atoi(optarg); |
---|
203 | break; |
---|
204 | |
---|
205 | case 'q': |
---|
206 | ping_info->quiet = TRUE; |
---|
207 | break; |
---|
208 | |
---|
209 | case 'w': |
---|
210 | ping_info->deadline = atoi(optarg); |
---|
211 | break; |
---|
212 | } |
---|
213 | } |
---|
214 | |
---|
215 | ping_info->size = sizeof(struct icmp_echo_hdr) + ping_info->data_size; |
---|
216 | |
---|
217 | if (optind >= argc) |
---|
218 | return -1; |
---|
219 | |
---|
220 | ping_info->destination = str2ip(argv[optind]); |
---|
221 | if (!ping_info->destination.addr) |
---|
222 | return -1; |
---|
223 | |
---|
224 | |
---|
225 | ping_info->last_rx_tm = timer_get_ms(); |
---|
226 | |
---|
227 | return 0; |
---|
228 | } |
---|
229 | |
---|
230 | static void print_stats(struct ping_info_t* ping_info) |
---|
231 | { |
---|
232 | printk("\n--- %s ping statistics ---\n", |
---|
233 | ip2str(ping_info->destination)); |
---|
234 | printk("%d packets transmitted, %d received, %d%% packet loss, "\ |
---|
235 | "time %dms\n\n", |
---|
236 | ping_info->num_tx, ping_info->num_rx, |
---|
237 | 100 * (ping_info->num_tx - ping_info->num_rx) / |
---|
238 | ping_info->num_tx, |
---|
239 | timer_get_ms() - ping_info->first_tx_tm); |
---|
240 | } |
---|
241 | |
---|
242 | static void ping_finalize(struct ping_info_t* ping_info) { |
---|
243 | print_stats(ping_info); |
---|
244 | if (ping_info->complete_cb) { |
---|
245 | ping_info->complete_cb(ping_info->num_tx, ping_info->num_rx, ping_info->ctx); |
---|
246 | } |
---|
247 | } |
---|
248 | |
---|
249 | cmd_state_t cmd_ping(int argc, char* argv[], void* ctx) |
---|
250 | { |
---|
251 | static enum { |
---|
252 | INIT, |
---|
253 | PING, |
---|
254 | WAIT_REPLY |
---|
255 | } state = INIT; |
---|
256 | |
---|
257 | struct ping_info_t *ping_info = &INFO; |
---|
258 | static struct raw_pcb *pcb; |
---|
259 | |
---|
260 | switch (state) { |
---|
261 | case INIT: |
---|
262 | if (init_ping_info(argc, argv, ping_info) != 0) { |
---|
263 | printk("Usage: ping [-c count] [-i interval] " \ |
---|
264 | "[-s packetsize]\n " \ |
---|
265 | "[-w deadline] [-q] destination\n"); |
---|
266 | return CMD_DONE; |
---|
267 | } |
---|
268 | |
---|
269 | if (!(pcb = raw_new(IP_PROTO_ICMP))) { |
---|
270 | printk("could not allocate pcb\n"); |
---|
271 | state = INIT; |
---|
272 | return CMD_DONE; |
---|
273 | } |
---|
274 | raw_recv(pcb, ping_recv, ping_info); |
---|
275 | raw_bind(pcb, IP_ADDR_ANY); |
---|
276 | |
---|
277 | printk("PING %s %d(%d) bytes of data\n", |
---|
278 | ip2str(ping_info->destination), |
---|
279 | ping_info->data_size, |
---|
280 | ping_info->size); |
---|
281 | state = PING; |
---|
282 | /* fall through */ |
---|
283 | |
---|
284 | case PING: |
---|
285 | if (!netif_is_up(netif_default)) { |
---|
286 | printk("netif is down\n"); |
---|
287 | raw_remove(pcb); |
---|
288 | state = INIT; |
---|
289 | return CMD_DONE; |
---|
290 | } |
---|
291 | |
---|
292 | if (ping_info->count && ping_info->num_tx == ping_info->count) { |
---|
293 | ping_finalize(ping_info); |
---|
294 | raw_remove(pcb); |
---|
295 | state = INIT; |
---|
296 | return CMD_DONE; |
---|
297 | } |
---|
298 | |
---|
299 | |
---|
300 | if (timer_get_ms() < ping_info->last_rx_tm + ping_info->interval) { |
---|
301 | return CMD_INPROGRESS; |
---|
302 | } |
---|
303 | ping_send(pcb, ping_info); |
---|
304 | |
---|
305 | state = WAIT_REPLY; |
---|
306 | return CMD_INPROGRESS; |
---|
307 | |
---|
308 | case WAIT_REPLY: |
---|
309 | if (ping_info->flags & PING_REPLY) { |
---|
310 | ping_info->flags &= (~PING_REPLY); |
---|
311 | state = PING; |
---|
312 | return CMD_INPROGRESS; |
---|
313 | } |
---|
314 | |
---|
315 | if (timer_get_ms() > |
---|
316 | ping_info->last_tx_tm + ping_info->timeout) { |
---|
317 | if (!ping_info->quiet) |
---|
318 | printk("timeout from %s\n", |
---|
319 | ip2str(ping_info->destination)); |
---|
320 | state = PING; |
---|
321 | return CMD_INPROGRESS; |
---|
322 | } |
---|
323 | |
---|
324 | if (ping_info->deadline && |
---|
325 | timer_get_ms() > |
---|
326 | ping_info->first_tx_tm + ping_info->deadline * 1000) { |
---|
327 | ping_finalize(ping_info); |
---|
328 | raw_remove(pcb); |
---|
329 | state = INIT; |
---|
330 | return CMD_DONE; |
---|
331 | } |
---|
332 | |
---|
333 | return CMD_INPROGRESS; |
---|
334 | } |
---|
335 | |
---|
336 | /* unreachable */ |
---|
337 | Assert(0); |
---|
338 | return CMD_DONE; |
---|
339 | } |
---|
340 | #endif |
---|