# Sample Networking Program

This example is derived from the test program `".../net/tcpip/v1_3_1/tests/ping_test.c"`.

> `//==========================================================================`
> `//`
> `// tests/ping_test.c`
> `//`
> `// Simple test of PING (ICMP) and networking support`
> `//`
> `//==========================================================================`
> `//####BSDCOPYRIGHTBEGIN####`
> `//`
> `// -------------------------------------------`
> `//`
> `// Portions of this software may have been derived from OpenBSD or other sources,`
> `// and are covered by the appropriate copyright disclaimers included herein.`
> `//`
> `// -------------------------------------------`
> `//`
> `//####BSDCOPYRIGHTEND####`
> `//==========================================================================`
> `//#####DESCRIPTIONBEGIN####`
> `//`
> `// Author(s): gthomas`
> `// Contributors: gthomas`
> `// Date: 2000-01-10`
> `// Purpose:`
> `// Description:`
> `//`
> `//`
> `//####DESCRIPTIONEND####`
> `//`
> `//==========================================================================```

> `// PING test code```

> `#include <network.h>`

This single include directive is all that is required to pick up the networking support, including complete configuration information.

> `#define STACK_SIZE CYGNUM_HAL_STACK_SIZE_TYPICAL`
> `static char stack[STACK_SIZE];`
> `static cyg_thread thread_data;`
> `static cyg_handle_t thread_handle;```

> `#define NUM_PINGS 16`
> `#define MAX_PACKET 4096`
> `static unsigned char pkt1[MAX_PACKET], pkt2[MAX_PACKET];```

> `#define UNIQUEID 0x1234```

> `void`
> `cyg_test_exit(void)`
> `{`
> `diag_printf("... Done\n");`
> `while (1) ;`
> `}```

> `void`
> `pexit(char *s)`
> `{`
> `perror(s);`
> `cyg_test_exit();`
> `}```

> `// Compute INET checksum`
> `int`
> `inet_cksum(u_short *addr, int len)`
> `{`
> `register int nleft = len;`
> `register u_short *w = addr;`
> `register u_short answer;`
> `register u_int sum = 0;`
> `u_short odd_byte = 0;```

> `/*`
> `* Our algorithm is simple, using a 32 bit accumulator (sum),`
> `* we add sequential 16 bit words to it, and at the end, fold`
> `* back all the carry bits from the top 16 bits into the lower`
> `* 16 bits.`
> `*/`
> `while( nleft > 1 ) {`
> `sum += *w++;`
> `nleft -= 2;`
> `}```

> `/* mop up an odd byte, if necessary */`
> `if( nleft == 1 ) {`
> `*(u_char *)(&odd_byte) = *(u_char *)w;`
> `sum += odd_byte;`
> `}```

> `/*`
> `* add back carry outs from top 16 bits to low 16 bits`
> `*/`
> `sum = (sum >> 16) + (sum & 0x0000ffff); /* add hi 16 to low 16 */`
> `sum += (sum >> 16); /* add carry */`
> `answer = ~sum; /* truncate to 16 bits */`
> `return (answer);`
> `}```

> `static int`
> `show_icmp(unsigned char *pkt, int len,`
> `struct sockaddr_in *from, struct sockaddr_in *to)`
> `{`
> `cyg_tick_count_t *tp, tv;`
> `struct ip *ip;`
> `struct icmp *icmp;`
> `tv = cyg_current_time();`
> `ip = (struct ip *)pkt;`
> `if ((len < sizeof(*ip)) || ip->ip_v != IPVERSION) {`
> `diag_printf("%s: Short packet or not IP! - Len: %d, Version: %d\n",`
> `inet_ntoa(from->sin_addr), len, ip->ip_v);`
> `return 0;`
> `}`
> `icmp = (struct icmp *)(pkt + sizeof(*ip));`
> `len -= (sizeof(*ip) + 8);`
> `tp = (cyg_tick_count_t *)&icmp->icmp_data;`
> `if (icmp->icmp_type != ICMP_ECHOREPLY) {`
> `diag_printf("%s: Invalid ICMP - type: %d\n",`
> `inet_ntoa(from->sin_addr), icmp->icmp_type);`
> `return 0;`
> `}`
> `if (icmp->icmp_id != UNIQUEID) {`
> `diag_printf("%s: ICMP received for wrong id - sent: %x, recvd: %x\n",`
> `inet_ntoa(from->sin_addr), UNIQUEID, icmp->icmp_id);`
> `}`
> `diag_printf("%d bytes from %s: ", len, inet_ntoa(from->sin_addr));`
> `diag_printf("icmp_seq=%d", icmp->icmp_seq);`
> `diag_printf(", time=%dms\n", (int)(tv - *tp)*10);`
> `return (from->sin_addr.s_addr == to->sin_addr.s_addr);`
> `}```

> `static void`
> `ping_host(int s, struct sockaddr_in *host)`
> `{`
> `struct icmp *icmp = (struct icmp *)pkt1;`
> `int icmp_len = 64;`
> `int seq, ok_recv, bogus_recv;`
> `cyg_tick_count_t *tp;`
> `long *dp;`
> `struct sockaddr_in from;`
> `int i, len, fromlen;```

> `ok_recv = 0;`
> `bogus_recv = 0;`
> `diag_printf("PING server %s\n", inet_ntoa(host->sin_addr));`

This function takes an IP address and returns a string representation. Useful for printing the address.

> `for (seq = 0; seq < NUM_PINGS; seq++) {`
> `// Build ICMP packet`
> `icmp->icmp_type = ICMP_ECHO;`
> `icmp->icmp_code = 0;`
> `icmp->icmp_cksum = 0;`
> `icmp->icmp_seq = seq;`
> `icmp->icmp_id = 0x1234;`
> `// Set up ping data`
> `tp = (cyg_tick_count_t *)&icmp->icmp_data;`
> `*tp++ = cyg_current_time();`
> `dp = (long *)tp;`
> `for (i = sizeof(*tp); i < icmp_len; i += sizeof(*dp)) {`
> `*dp++ = i;`
> `}`
> `// Add checksum`
> `icmp->icmp_cksum = inet_cksum( (u_short *)icmp, icmp_len+8);`
> `// Send it off`
> `if (sendto(s, icmp, icmp_len+8, 0, (struct sockaddr *)host, sizeof(*host)) < 0) {`

This function sends a single packet, in this case via the ICMP protocol. The destination address is specified by the `host` argument.

> `perror("sendto");`
> `continue;`
> `}`
> `// Wait for a response`
> `fromlen = sizeof(from);`
> `len = recvfrom(s, pkt2, sizeof(pkt2), 0, (struct sockaddr *)&from, &fromlen);`

This function waits for a packet to be sent to this interface. Note: this operation can fail if no packet is received with a given amount of time (this timeout value is setup in the `ping_test()` routine below).

> `if (len < 0) {`
> `perror("recvfrom");`
> `} else {`
> `if (show_icmp(pkt2, len, &from, host)) {`
> `ok_recv++;`
> `} else {`
> `bogus_recv++;`
> `}`
> `}`
> `}`
> `diag_printf("Sent %d packets, received %d OK, %d bad\n", NUM_PINGS, ok_recv, bogus_recv);`
> `}```

> `static void`
> `ping_test(struct bootp *bp)`
> `{`
> `struct protoent *p;`
> `struct timeval tv;`
> `struct sockaddr_in host;`
> `int s;```

> `if ((p = getprotobyname("icmp")) == (struct protoent *)0) {`

This function gets information about the ICMP protocol. This will be used below to set up the "socket" (the handle to a network connection).

> `perror("getprotobyname");`
> `return;`
> `}`
> `s = socket(AF_INET, SOCK_RAW, p->p_proto);`

Create the socket "s". A socket is an abstract object (sometimes called a handle or endpoint). Sockets come in many flavors, RAW sockets are used for communicating with non-structured protocols. DATAGRAM sockets are used for packet oriented protocols, such as UDP. STREAM sockets are used for reliable, sequenced byte streams, such as those provided by TCP.

> `if (s < 0) {`
> `perror("socket");`
> `return;`
> `}`
> `tv.tv_sec = 1;`
> `tv.tv_usec = 0;`
> `setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));`

This function instructs the system that receive operations (see `ping_host()` above) must complete within 1 second or issue a timed out error condition.

> `// Set up host address`
> `host.sin_family = AF_INET;`
> `host.sin_addr = bp->bp_siaddr;`
> `host.sin_port = 0;`
> `ping_host(s, &host);`
> `// Now try a bogus host`
> `host.sin_addr.s_addr = htonl(ntohl(host.sin_addr.s_addr) + 32);`
> `ping_host(s, &host);`
> `}`
> ``

This function drives the test.

> `void`
> `net_test(cyg_addrword_t p)`
> `{`
> `diag_printf("Start PING test\n");`
> `init_all_network_interfaces();`

This is a call to the network initialization code. Note that it must be executed from a thread context, thus we can't put it in the `cyg_start()` function which follows. The `init_all_network_interfaces()` routine will cause the networking system to be initialized and any hardware interfaces will be set up. Using the system configuration information, an interface can be started using either BOOTP, or a predefined configuration with static IP information. It is also possible to indicate that an interface will be configured by the user code, outside of "`init_all_network_interfaces()`".
``
`#ifdef CYGHWR_NET_DRIVER_ETH0`
`if (eth0_up) {`

> `ping_test(&eth0_bootp_data);`
> `}`
> `#endif`

Notice that there is a BOOTP information structure kept about all hardware interfaces. This is created and initialized by the call to `init_all_network_interfaces()`, unless manual configuration is selected. This information can be used by the application to learn things such as the local IP address, server name, etc.

> `#ifdef CYGHWR_NET_DRIVER_ETH1`
> `if (eth1_up) {`
> `ping_test(&eth1_bootp_data);`
> `}`
> `#endif`

Multiple hardware interfaces can be supported, depending on the hardware platform. If the platform provides a single ethernet device then `CYGHWR_NET_DRIVER_ETH0` will be defined. If there is a second interface, then will `CYGHWR_NET_DRIVER_ETH1` be defined.

> `cyg_test_exit();`
> `}`
> ``

The following function creates an initial thread which runs the program. This could just as easily be done by naming the `net_test()` function `main()`.

> `void`
> `cyg_start(void)`
> `{`
> `// Create a main thread, so we can run the scheduler and have time 'pass'`
> `cyg_thread_create(10, // Priority - just a number`
> `net_test, // entry`
> `0, // entry parameter`
> `"Network test", // Name`
> `&stack[0], // Stack`
> `STACK_SIZE, // Size`
> `&thread_handle, // Handle`
> `&thread_data // Thread data structure`
> `);`
> `cyg_thread_resume(thread_handle); // Start it`
> `cyg_scheduler_start();`
> `}`