First run at integrating LWIP into the tree (again)
[akaros.git] / user / apps / parlib / ping.c
1 /**
2  * @file
3  * Ping sender module
4  *
5  */
6
7 /*
8  * Redistribution and use in source and binary forms, with or without modification, 
9  * are permitted provided that the following conditions are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright notice,
12  *    this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright notice,
14  *    this list of conditions and the following disclaimer in the documentation
15  *    and/or other materials provided with the distribution.
16  * 3. The name of the author may not be used to endorse or promote products
17  *    derived from this software without specific prior written permission. 
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
20  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
21  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
22  * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
23  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
24  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
27  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
28  * OF SUCH DAMAGE.
29  *
30  * This file is part of the lwIP TCP/IP stack.
31  * 
32  */
33
34 /** 
35  * This is an example of a "ping" sender (with raw API and socket API).
36  * It can be used as a start point to maintain opened a network connection, or
37  * like a network "watchdog" for your device.
38  *
39  */
40
41 #include "lwip/opt.h"
42
43 #if LWIP_RAW && LWIP_ICMP /* don't build if not configured for use in lwipopts.h */
44
45 #include "lwip/mem.h"
46 #include "lwip/raw.h"
47 #include "lwip/icmp.h"
48 #include "lwip/netif.h"
49 #include "lwip/sys.h"
50 #include "lwip/sockets.h"
51 #include "lwip/inet.h"
52 #include "lwip/inet_chksum.h"
53
54 /**
55  * PING_DEBUG: Enable debugging for PING.
56  */
57 #ifndef PING_DEBUG
58 #define PING_DEBUG     LWIP_DBG_ON
59 #endif
60
61 /** ping target - should be a "struct ip_addr" */
62 #ifndef PING_TARGET
63 #define PING_TARGET   (netif_default?netif_default->gw:ip_addr_any)
64 #endif
65
66 /** ping receive timeout - in milliseconds */
67 #ifndef PING_RCV_TIMEO
68 #define PING_RCV_TIMEO 1000
69 #endif
70
71 /** ping delay - in milliseconds */
72 #ifndef PING_DELAY
73 #define PING_DELAY     1000
74 #endif
75
76 /** ping identifier - must fit on a u16_t */
77 #ifndef PING_ID
78 #define PING_ID        0xAFAF
79 #endif
80
81 /** ping additional data size to include in the packet */
82 #ifndef PING_DATA_SIZE
83 #define PING_DATA_SIZE 32
84 #endif
85
86 /** ping result action - no default action */
87 #ifndef PING_RESULT
88 #define PING_RESULT(ping_ok)
89 #endif
90
91 /* ping variables */
92 static u16_t ping_seq_num;
93 static u32_t ping_time;
94
95 #if NO_SYS
96 /* port-defined functions used for timer execution */
97 void sys_msleep(u32_t ms);
98 u32_t sys_now();
99 #endif /* NO_SYS */
100
101 void ping_init(void);
102
103 /** Prepare a echo ICMP request */
104 static void
105 ping_prepare_echo( struct icmp_echo_hdr *iecho, u16_t len)
106 {
107   int i;
108
109   ICMPH_TYPE_SET(iecho,ICMP_ECHO);
110   ICMPH_CODE_SET(iecho, 0);
111   iecho->chksum = 0;
112   iecho->id     = PING_ID;
113   iecho->seqno  = htons(++ping_seq_num);
114   iecho->chksum = inet_chksum(iecho, len);
115
116   /* fill the additional data buffer with some data */
117   for(i = 0; i < PING_DATA_SIZE; i++) {
118     ((char*)iecho)[sizeof(struct icmp_echo_hdr) + i] = i;
119   }
120 }
121
122 #if LWIP_SOCKET
123
124
125
126
127
128 void ping() {
129
130         printf("Starting the ping nightmare....\n");
131
132         int s;
133
134         static struct ip_addr ping_addr;
135         
136         IP4_ADDR(&ping_addr, 192,168,0,3);
137         
138         if ((s = lwip_socket(AF_INET, SOCK_RAW, IP_PROTO_ICMP)) < 0) {
139                 return;
140         }
141
142         ping_init();
143 }
144
145
146 /* Ping using the socket ip */
147 static err_t
148 ping_send(int s, struct ip_addr *addr)
149 {
150   int err;
151   struct icmp_echo_hdr *iecho;
152   struct sockaddr_in to;
153   size_t ping_size = sizeof(struct icmp_echo_hdr) + PING_DATA_SIZE;
154
155   if (!(iecho = mem_malloc(ping_size))) {
156     return ERR_MEM;
157   }
158
159   ping_prepare_echo(iecho, ping_size);
160
161   to.sin_len = sizeof(to);
162   to.sin_family = AF_INET;
163   to.sin_addr.s_addr = addr->addr;
164
165   err = lwip_sendto(s, iecho, ping_size, 0, (struct sockaddr*)&to, sizeof(to));
166
167   mem_free(iecho);
168
169   return (err ? ERR_OK : ERR_VAL);
170 }
171
172 static void
173 ping_recv(int s)
174 {
175   char buf[64];
176   int fromlen, len;
177   struct sockaddr_in from;
178   struct ip_hdr *iphdr;
179   struct icmp_echo_hdr *iecho;
180
181   while((len = lwip_recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr*)&from, (socklen_t*)&fromlen)) > 0) {
182     if (len >= (sizeof(struct ip_hdr)+sizeof(struct icmp_echo_hdr))) {
183       LWIP_DEBUGF( PING_DEBUG, ("ping: recv "));
184       ip_addr_debug_print(PING_DEBUG, (struct ip_addr *)&(from.sin_addr));
185       LWIP_DEBUGF( PING_DEBUG, (" %lu ms\n", (sys_now()-ping_time)));
186
187       iphdr = (struct ip_hdr *)buf;
188       iecho = (struct icmp_echo_hdr *)(buf+(IPH_HL(iphdr) * 4));
189       if ((iecho->id == PING_ID) && (iecho->seqno == htons(ping_seq_num))) {
190         /* do some ping result processing */
191         PING_RESULT((ICMPH_TYPE(iecho) == ICMP_ER));
192         return;
193       } else {
194         LWIP_DEBUGF( PING_DEBUG, ("ping: drop\n"));
195       }
196     }
197   }
198
199   if (len == 0) {
200     LWIP_DEBUGF( PING_DEBUG, ("ping: recv - %lu ms - timeout\n", (sys_now()-ping_time)));
201   }
202
203   /* do some ping result processing */
204   PING_RESULT(0);
205 }
206
207 static void
208 ping_thread(void *arg)
209 {
210   int s;
211   int timeout = PING_RCV_TIMEO;
212   struct ip_addr ping_target;
213
214   LWIP_UNUSED_ARG(arg);
215
216   if ((s = lwip_socket(AF_INET, SOCK_RAW, IP_PROTO_ICMP)) < 0) {
217     return;
218   }
219
220   lwip_setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
221
222   while (1) {
223     ping_target = PING_TARGET;
224
225     if (ping_send(s, &ping_target) == ERR_OK) {
226       LWIP_DEBUGF( PING_DEBUG, ("ping: send "));
227       ip_addr_debug_print(PING_DEBUG, &ping_target);
228       LWIP_DEBUGF( PING_DEBUG, ("\n"));
229
230       ping_time = sys_now();
231       ping_recv(s);
232     } else {
233       LWIP_DEBUGF( PING_DEBUG, ("ping: send "));
234       ip_addr_debug_print(PING_DEBUG, &ping_target);
235       LWIP_DEBUGF( PING_DEBUG, (" - error\n"));
236     }
237     sys_msleep(PING_DELAY);
238   }
239 }
240
241 #else /* LWIP_SOCKET */
242
243 /* Ping using the raw ip */
244 static u8_t
245 ping_recv(void *arg, struct raw_pcb *pcb, struct pbuf *p, struct ip_addr *addr)
246 {
247   struct icmp_echo_hdr *iecho;
248
249   if (pbuf_header( p, -PBUF_IP_HLEN)==0) {
250     iecho = p->payload;
251
252     if ((iecho->id == PING_ID) && (iecho->seqno == htons(ping_seq_num))) {
253       LWIP_DEBUGF( PING_DEBUG, ("ping: recv "));
254       ip_addr_debug_print(PING_DEBUG, addr);
255       LWIP_DEBUGF( PING_DEBUG, (" %lu ms\n", (sys_now()-ping_time)));
256
257       /* do some ping result processing */
258       PING_RESULT(1);
259     }
260   }
261
262   return 1; /* eat the event */
263 }
264
265 static void
266 ping_send(struct raw_pcb *raw, struct ip_addr *addr)
267 {
268   struct pbuf *p;
269   struct icmp_echo_hdr *iecho;
270   size_t ping_size = sizeof(struct icmp_echo_hdr) + PING_DATA_SIZE;
271
272   if (!(p = pbuf_alloc(PBUF_IP, ping_size, PBUF_RAM))) {
273     return;
274   }
275   if ((p->len == p->tot_len) && (p->next == NULL)) {
276     iecho = p->payload;
277
278     ping_prepare_echo(iecho, ping_size);
279
280     raw_sendto(raw, p, addr);
281     ping_time = sys_now();
282   }
283   pbuf_free(p);
284 }
285
286 static void
287 ping_timeout(void *arg)
288 {
289   struct raw_pcb *pcb = (struct raw_pcb*)arg;
290   struct ip_addr ping_target = PING_TARGET;
291   
292   LWIP_ASSERT("ping_timeout: no pcb given!", pcb != NULL);
293
294   LWIP_DEBUGF( PING_DEBUG, ("ping: send "));
295   ip_addr_debug_print(PING_DEBUG, &ping_target);
296   LWIP_DEBUGF( PING_DEBUG, ("\n"));
297
298   ping_send(pcb, &ping_target);
299
300   sys_timeout(PING_DELAY, ping_timeout, pcb);
301 }
302
303 static void
304 ping_raw_init(void)
305 {
306   struct raw_pcb *pcb;
307
308   if (!(pcb = raw_new(IP_PROTO_ICMP))) {
309     return;
310   }
311
312   raw_recv(pcb, ping_recv, NULL);
313   raw_bind(pcb, IP_ADDR_ANY);
314   sys_timeout(PING_DELAY, ping_timeout, pcb);
315 }
316
317 #endif /* LWIP_SOCKET */
318
319 void
320 ping_init(void)
321 {
322 #if LWIP_SOCKET
323   sys_thread_new("ping_thread", ping_thread, NULL, DEFAULT_THREAD_STACKSIZE, DEFAULT_THREAD_PRIO);
324 #else /* LWIP_SOCKET */
325   ping_raw_init();
326 #endif /* LWIP_SOCKET */
327 }
328
329 #endif /* LWIP_RAW && LWIP_ICMP */