unified MX27, MX25, MX37 trees
[karo-tx-redboot.git] / packages / net / common / v2_0 / tests / ping_test.c
1 //==========================================================================
2 //
3 //      tests/ping_test.c
4 //
5 //      Simple test of PING (ICMP) and networking support
6 //
7 //==========================================================================
8 //####BSDCOPYRIGHTBEGIN####
9 //
10 // -------------------------------------------
11 //
12 // Portions of this software may have been derived from OpenBSD or other sources,
13 // and are covered by the appropriate copyright disclaimers included herein.
14 //
15 // -------------------------------------------
16 //
17 //####BSDCOPYRIGHTEND####
18 //==========================================================================
19 //#####DESCRIPTIONBEGIN####
20 //
21 // Author(s):    gthomas
22 // Contributors: gthomas, andrew.lunn@ascom.ch
23 // Date:         2000-01-10
24 // Purpose:      
25 // Description:  
26 //              
27 //
28 //####DESCRIPTIONEND####
29 //
30 //==========================================================================
31
32 // PING test code
33
34 #include <network.h>
35 #ifdef CYGPKG_NET_INET6
36 #include <netinet/ip6.h>
37 #include <netinet/icmp6.h>
38 #endif
39
40 #include <pkgconf/system.h>
41 #include <pkgconf/net.h>
42
43 #include <cyg/infra/testcase.h>
44
45 #ifdef CYGBLD_DEVS_ETH_DEVICE_H    // Get the device config if it exists
46 #include CYGBLD_DEVS_ETH_DEVICE_H  // May provide CYGTST_DEVS_ETH_TEST_NET_REALTIME
47 #endif
48
49 #ifdef CYGPKG_NET_TESTS_USE_RT_TEST_HARNESS // do we use the rt test?
50 # ifdef CYGTST_DEVS_ETH_TEST_NET_REALTIME // Get the test ancilla if it exists
51 #  include CYGTST_DEVS_ETH_TEST_NET_REALTIME
52 # endif
53 #endif
54
55 // Fill in the blanks if necessary
56 #ifndef TNR_OFF
57 # define TNR_OFF()
58 #endif
59 #ifndef TNR_ON
60 # define TNR_ON()
61 #endif
62 #ifndef TNR_INIT
63 # define TNR_INIT()
64 #endif
65 #ifndef TNR_PRINT_ACTIVITY
66 # define TNR_PRINT_ACTIVITY()
67 #endif
68
69
70
71 #ifndef CYGPKG_LIBC_STDIO
72 #define perror(s) diag_printf(#s ": %s\n", strerror(errno))
73 #endif
74
75 #define STACK_SIZE (CYGNUM_HAL_STACK_SIZE_TYPICAL + 0x1000)
76 static char stack[STACK_SIZE];
77 static cyg_thread thread_data;
78 static cyg_handle_t thread_handle;
79
80 #define NUM_PINGS 16
81 #define MAX_PACKET 4096
82 #define MIN_PACKET   64
83 #define MAX_SEND   4000
84
85 #define PACKET_ADD  ((MAX_SEND - MIN_PACKET)/NUM_PINGS)
86 #define nPACKET_ADD  1 
87
88 static unsigned char pkt1[MAX_PACKET], pkt2[MAX_PACKET];
89
90 #define UNIQUEID 0x1234
91
92 void
93 pexit(char *s)
94 {
95     CYG_TEST_FAIL_FINISH(s);
96 }
97
98 // Compute INET checksum
99 int
100 inet_cksum(u_short *addr, int len)
101 {
102     register int nleft = len;
103     register u_short *w = addr;
104     register u_short answer;
105     register u_int sum = 0;
106     u_short odd_byte = 0;
107
108     /*
109      *  Our algorithm is simple, using a 32 bit accumulator (sum),
110      *  we add sequential 16 bit words to it, and at the end, fold
111      *  back all the carry bits from the top 16 bits into the lower
112      *  16 bits.
113      */
114     while( nleft > 1 )  {
115         sum += *w++;
116         nleft -= 2;
117     }
118
119     /* mop up an odd byte, if necessary */
120     if( nleft == 1 ) {
121         *(u_char *)(&odd_byte) = *(u_char *)w;
122         sum += odd_byte;
123     }
124
125     /*
126      * add back carry outs from top 16 bits to low 16 bits
127      */
128     sum = (sum >> 16) + (sum & 0x0000ffff); /* add hi 16 to low 16 */
129     sum += (sum >> 16);                     /* add carry */
130     answer = ~sum;                          /* truncate to 16 bits */
131     return (answer);
132 }
133
134 static int
135 show_icmp(unsigned char *pkt, int len, 
136           struct sockaddr_in *from, struct sockaddr_in *to)
137 {
138     cyg_tick_count_t *tp, tv;
139     struct ip *ip;
140     struct icmp *icmp;
141     tv = cyg_current_time();
142     ip = (struct ip *)pkt;
143     if ((len < sizeof(*ip)) || ip->ip_v != IPVERSION) {
144         diag_printf("%s: Short packet or not IP! - Len: %d, Version: %d\n", 
145                     inet_ntoa(from->sin_addr), len, ip->ip_v);
146         return 0;
147     }
148     icmp = (struct icmp *)(pkt + sizeof(*ip));
149     len -= (sizeof(*ip) + 8);
150     tp = (cyg_tick_count_t *)&icmp->icmp_data;
151     if (icmp->icmp_type != ICMP_ECHOREPLY) {
152         diag_printf("%s: Invalid ICMP - type: %d\n", 
153                     inet_ntoa(from->sin_addr), icmp->icmp_type);
154         return 0;
155     }
156     if (icmp->icmp_id != UNIQUEID) {
157         diag_printf("%s: ICMP received for wrong id - sent: %x, recvd: %x\n", 
158                     inet_ntoa(from->sin_addr), UNIQUEID, icmp->icmp_id);
159     }
160     diag_printf("%d bytes from %s: ", len, inet_ntoa(from->sin_addr));
161     diag_printf("icmp_seq=%d", icmp->icmp_seq);
162     diag_printf(", time=%dms\n", (int)(tv - *tp)*10);
163     return (from->sin_addr.s_addr == to->sin_addr.s_addr);
164 }
165
166 static void
167 ping_host(int s, struct sockaddr_in *host)
168 {
169     struct icmp *icmp = (struct icmp *)pkt1;
170     int icmp_len = MIN_PACKET;
171     int seq, ok_recv, bogus_recv;
172     cyg_tick_count_t *tp;
173     long *dp;
174     struct sockaddr_in from;
175     int i, len;
176     socklen_t fromlen;
177
178     ok_recv = 0;
179     bogus_recv = 0;
180     diag_printf("PING server %s\n", inet_ntoa(host->sin_addr));
181     for (seq = 0;  seq < NUM_PINGS;  seq++, icmp_len += PACKET_ADD ) {
182         TNR_ON();
183         // Build ICMP packet
184         icmp->icmp_type = ICMP_ECHO;
185         icmp->icmp_code = 0;
186         icmp->icmp_cksum = 0;
187         icmp->icmp_seq = seq;
188         icmp->icmp_id = 0x1234;
189         // Set up ping data
190         tp = (cyg_tick_count_t *)&icmp->icmp_data;
191         *tp++ = cyg_current_time();
192         dp = (long *)tp;
193         for (i = sizeof(*tp);  i < icmp_len;  i += sizeof(*dp)) {
194             *dp++ = i;
195         }
196         // Add checksum
197         icmp->icmp_cksum = inet_cksum( (u_short *)icmp, icmp_len+8);
198         // Send it off
199         if (sendto(s, icmp, icmp_len+8, 0, (struct sockaddr *)host, sizeof(*host)) < 0) {
200             TNR_OFF();
201             perror("sendto");
202             continue;
203         }
204         // Wait for a response
205         fromlen = sizeof(from);
206         len = recvfrom(s, pkt2, sizeof(pkt2), 0, (struct sockaddr *)&from, &fromlen);
207         TNR_OFF();
208         if (len < 0) {
209             perror("recvfrom");
210             icmp_len = MIN_PACKET - PACKET_ADD; // just in case - long routes
211         } else {
212             if (show_icmp(pkt2, len, &from, host)) {
213                 ok_recv++;
214             } else {
215                 bogus_recv++;
216             }
217         }
218     }
219     TNR_OFF();
220     diag_printf("Sent %d packets, received %d OK, %d bad\n", NUM_PINGS, ok_recv, bogus_recv);
221 }
222
223 #ifdef CYGPKG_NET_INET6
224 static int
225 show6_icmp(unsigned char *pkt, int len, 
226           const struct sockaddr_in6 *from, const struct sockaddr_in6 *to)
227 {
228     cyg_tick_count_t *tp, tv;
229     struct icmp6_hdr *icmp;
230     char fromnamebuf[128];
231     char tonamebuf[128];
232     int error;
233
234     error = getnameinfo((struct sockaddr *)from,sizeof(*from), 
235                         fromnamebuf, sizeof(fromnamebuf), 
236                         NULL, 0,
237                         NI_NUMERICHOST);
238     if (error) {
239       perror ("getnameinfo(from)");
240       return 0;
241     }
242
243     error = getnameinfo((struct sockaddr *)to,sizeof(*to), 
244                         tonamebuf, sizeof(tonamebuf), 
245                         NULL, 0,
246                         NI_NUMERICHOST);
247     if (error) {
248       perror ("getnameinfo(to)");
249       return 0;
250     }
251
252     tv = cyg_current_time();
253     icmp = (struct icmp6_hdr *)pkt;
254     tp = (cyg_tick_count_t *)&icmp->icmp6_data8[4];
255    
256     if (icmp->icmp6_type != ICMP6_ECHO_REPLY) {
257         return 0;
258     }
259     if (icmp->icmp6_id != UNIQUEID) {
260         diag_printf("%s: ICMP received for wrong id - sent: %x, recvd: %x\n", 
261                     fromnamebuf, UNIQUEID, icmp->icmp6_id);
262     }
263     diag_printf("%d bytes from %s: ", len, fromnamebuf);
264     diag_printf("icmp_seq=%d", icmp->icmp6_seq);
265     diag_printf(", time=%dms\n", (int)(tv - *tp)*10);
266     return (!memcmp(&from->sin6_addr, &to->sin6_addr,sizeof(from->sin6_addr)));
267 }
268
269 static void
270 ping6_host(int s, const struct sockaddr_in6 *host)
271 {
272     struct icmp6_hdr *icmp = (struct icmp6_hdr *)pkt1;
273     int icmp_len = 64;
274     int seq, ok_recv, bogus_recv;
275     cyg_tick_count_t *tp;
276     long *dp;
277     struct sockaddr_in6 from;
278     int i, len, fromlen;
279     char namebuf[128];
280     int error;
281     int echo_responce;
282
283     ok_recv = 0;
284     bogus_recv = 0;
285     error = getnameinfo((struct sockaddr *)host,sizeof(*host), 
286                         namebuf, sizeof(namebuf), 
287                         NULL, 0,
288                         NI_NUMERICHOST);
289     if (error) {
290       perror ("getnameinfo");
291     } else {
292       diag_printf("PING6 server %s\n", namebuf);
293     }
294     for (seq = 0;  seq < NUM_PINGS;  seq++) {
295         // Build ICMP packet
296         icmp->icmp6_type = ICMP6_ECHO_REQUEST;
297         icmp->icmp6_code = 0;
298         icmp->icmp6_cksum = 0;
299         icmp->icmp6_seq = seq;
300         icmp->icmp6_id = UNIQUEID;
301
302         // Set up ping data
303         tp = (cyg_tick_count_t *)&icmp->icmp6_data8[4];
304         *tp++ = cyg_current_time();
305         dp = (long *)tp;
306         for (i = sizeof(*tp);  i < icmp_len;  i += sizeof(*dp)) {
307             *dp++ = i;
308         }
309         // Add checksum
310         icmp->icmp6_cksum = inet_cksum( (u_short *)icmp, icmp_len+8);
311         // Send it off
312         if (sendto(s, icmp, icmp_len+8, 0, (struct sockaddr *)host, 
313                    sizeof(*host)) < 0) {
314             perror("sendto");
315             continue;
316         }
317         // Wait for a response. We get our own ECHO_REQUEST and the responce
318         echo_responce = 0;
319         while (!echo_responce) {
320           fromlen = sizeof(from);
321           len = recvfrom(s, pkt2, sizeof(pkt2), 0, (struct sockaddr *)&from, 
322                          &fromlen);
323           if (len < 0) {
324             perror("recvfrom");
325             echo_responce=1;
326           } else {
327             if (show6_icmp(pkt2, len, &from, host)) {
328               ok_recv++;
329               echo_responce=1;
330             }
331           }
332         }
333     }
334     diag_printf("Sent %d packets, received %d OK, %d bad\n", NUM_PINGS, 
335                 ok_recv, bogus_recv);
336 }
337
338 static void
339 ping6_test( struct sockaddr_in6 *host)
340 {
341     struct protoent *p;
342     struct timeval tv;
343     struct sockaddr_in6 addr;
344     int s;
345
346     if ((p = getprotobyname("ipv6-icmp")) == (struct protoent *)0) {
347         perror("getprotobyname");
348         return;
349     }
350     s = socket(AF_INET6, SOCK_RAW, p->p_proto);
351     if (s < 0) {
352         perror("socket");
353         return;
354     }
355     tv.tv_sec = 1;
356     tv.tv_usec = 0;
357     setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
358
359     ping6_host(s, host);
360
361     // Now try a bogus host
362     memcpy(&addr,host,sizeof(addr));
363     addr.sin6_addr.s6_addr[15] = addr.sin6_addr.s6_addr[15] + 32;
364     ping6_host(s, &addr);
365 }
366 #endif
367
368 #ifdef CYGPKG_PROFILE_GPROF
369 #include <cyg/profile/profile.h>
370
371 extern char _stext, _etext;  // Defined by the linker
372
373 static void
374 start_profile(void)
375 {
376     // This starts up the system-wide profiling, gathering
377     // profile information on all of the code, with a 16 byte
378     // "bucket" size, at a rate of 100us/profile hit.
379     // Note: a bucket size of 16 will give pretty good function
380     //       resolution.  Much smaller and the buffer becomes
381     //       much too large for very little gain.
382     // Note: a timer period of 100us is also a reasonable
383     //       compromise.  Any smaller and the overhead of 
384     //       handling the timter (profile) interrupt could
385     //       swamp the system.  A fast processor might get
386     //       by with a smaller value, but a slow one could
387     //       even be swamped by this value.  If the value is
388     //       too large, the usefulness of the profile is reduced.
389     profile_on(&_stext, &_etext, 16, 100);
390 }
391 #endif
392
393 static void
394 ping_test(struct bootp *bp)
395 {
396     struct protoent *p;
397     struct timeval tv;
398     struct sockaddr_in host;
399     int s;
400
401     if ((p = getprotobyname("icmp")) == (struct protoent *)0) {
402         pexit("getprotobyname");
403         return;
404     }
405     s = socket(AF_INET, SOCK_RAW, p->p_proto);
406     if (s < 0) {
407         pexit("socket");
408         return;
409     }
410     tv.tv_sec = 1;
411     tv.tv_usec = 0;
412     setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
413     // Set up host address
414     host.sin_family = AF_INET;
415     host.sin_len = sizeof(host);
416     host.sin_addr = bp->bp_siaddr;
417     host.sin_port = 0;
418     ping_host(s, &host);
419     // Now try a bogus host
420     host.sin_addr.s_addr = htonl(ntohl(host.sin_addr.s_addr) + 32);
421     ping_host(s, &host);
422 }
423
424 void
425 net_test(cyg_addrword_t p)
426 {
427 #ifdef CYGPKG_NET_INET6
428     struct sockaddr_in6 ipv6router;
429 #endif
430
431     diag_printf("Start PING test\n");
432     TNR_INIT();
433     init_all_network_interfaces();
434 #ifdef CYGPKG_PROFILE_GPROF
435     start_profile();
436 #endif
437 #ifdef CYGHWR_NET_DRIVER_ETH0
438     if (eth0_up) {
439         ping_test(&eth0_bootp_data);
440     }
441 #endif
442 #ifdef CYGHWR_NET_DRIVER_ETH1
443     if (eth1_up) {
444         ping_test(&eth1_bootp_data);
445     }
446 #endif
447 #ifdef CYGPKG_NET_INET6
448     if (cyg_net_get_ipv6_advrouter(&ipv6router)) {
449       ping6_test(&ipv6router);
450     } else {
451       CYG_TEST_FAIL("No router advertisement recieved");
452     }
453 #endif
454     TNR_PRINT_ACTIVITY();
455     CYG_TEST_PASS_FINISH("Ping test OK");
456 }
457
458 void
459 cyg_start(void)
460 {
461     // Create a main thread, so we can run the scheduler and have time 'pass'
462     cyg_thread_create(10,                // Priority - just a number
463                       net_test,          // entry
464                       0,                 // entry parameter
465                       "Network test",    // Name
466                       &stack[0],         // Stack
467                       STACK_SIZE,        // Size
468                       &thread_handle,    // Handle
469                       &thread_data       // Thread data structure
470             );
471     cyg_thread_resume(thread_handle);  // Start it
472     cyg_scheduler_start();
473 }