License change
[olsrd.git] / src / linux / link_layer.c
1 /*
2  * The olsr.org Optimized Link-State Routing daemon(olsrd)
3  * Copyright (c) 2004, Andreas T√łnnesen(andreto@olsr.org)
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without 
7  * modification, are permitted provided that the following conditions 
8  * are met:
9  *
10  * * Redistributions of source code must retain the above copyright 
11  *   notice, this list of conditions and the following disclaimer.
12  * * Redistributions in binary form must reproduce the above copyright 
13  *   notice, this list of conditions and the following disclaimer in 
14  *   the documentation and/or other materials provided with the 
15  *   distribution.
16  * * Neither the name of olsr.org, olsrd nor the names of its 
17  *   contributors may be used to endorse or promote products derived 
18  *   from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
24  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
27  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
28  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
30  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
31  * POSSIBILITY OF SUCH DAMAGE.
32  *
33  * Visit http://www.olsr.org for more information.
34  *
35  * If you find this software useful feel free to make a donation
36  * to the projcet. For more information see the website or contact
37  * the copyright holders.
38  *
39  * $Id: link_layer.c,v 1.8 2004/11/21 00:00:13 kattemat Exp $
40  */
41
42 /* Ugly fix to make this compile on wireless extentions < 16 */
43 #define _LINUX_ETHTOOL_H
44
45 #include "../link_layer.h"
46 #include "../olsr_protocol.h"
47 #include "../scheduler.h"
48 #include "../interfaces.h"
49 #include <linux/wireless.h>
50 #include <linux/icmp.h>
51 #include <errno.h>
52 #include <string.h>
53 #include <sys/ioctl.h>
54 #include <net/if_arp.h>
55 #include <unistd.h>
56 #include <stdlib.h>
57
58
59 extern char *
60 sockaddr_to_string(struct sockaddr *);
61
62 extern char *
63 olsr_ip_to_string(union olsr_ip_addr *);
64
65 extern int
66 olsr_printf(int, char *, ...);
67
68
69 #define MAXIPLEN        60
70 #define MAXICMPLEN      76
71
72 extern size_t ipsize;
73
74 extern int ioctl_s;
75
76 float poll_int = 0.2;
77
78 int
79 iw_get_range_info(char *, struct iw_range *);
80
81 int
82 clear_spy_list(char *);
83
84 int
85 convert_ip_to_mac(union olsr_ip_addr *, struct sockaddr *, char *);
86
87 void
88 send_ping(union olsr_ip_addr *);
89
90
91 void
92 init_link_layer_notification()
93 {
94   struct interface *ifd;
95
96   olsr_printf(1, "Initializing link-layer notification...\n");
97
98
99   for (ifd = ifnet; ifd ; ifd = ifd->int_next) 
100     {
101       if(ifd->is_wireless)
102         clear_spy_list(ifd->int_name);
103     }
104
105   olsr_register_scheduler_event(&poll_link_layer, NULL, poll_int, 0, NULL);
106
107   return;
108 }
109
110 int
111 clear_spy_list(char *ifname)
112 {
113   struct iwreq  wrq;
114
115   /* Time to do send addresses to the driver */
116   wrq.u.data.pointer = NULL;//(caddr_t) hw_address;
117   wrq.u.data.length = 0;
118   wrq.u.data.flags = 0;
119
120   /* Set device name */
121   strncpy(wrq.ifr_name, ifname, IFNAMSIZ);
122
123   if(ioctl(ioctl_s, SIOCSIWSPY, &wrq) < 0)
124     {
125       olsr_printf(1, "Could not clear spylist %s\n", strerror(errno));
126       return -1;
127     }
128
129   return 1;
130 }
131
132
133
134 int
135 add_spy_node(union olsr_ip_addr *addr, char *interface)
136 {
137   struct sockaddr       new_node;
138   struct iwreq          wrq;
139   int                   nbr;            /* Number of valid addresses */
140   struct sockaddr       hw_address[IW_MAX_SPY];
141   char  buffer[(sizeof(struct iw_quality) +
142                 sizeof(struct sockaddr)) * IW_MAX_SPY];
143   
144   olsr_printf(1, "Adding spynode!\n\n");
145   
146   /* get all addresses already in the driver */
147
148   wrq.u.data.pointer = (caddr_t) buffer;
149   wrq.u.data.length = IW_MAX_SPY;
150   wrq.u.data.flags = 0;
151
152   strncpy(wrq.ifr_name, interface, IFNAMSIZ);
153
154   if(ioctl(ioctl_s, SIOCGIWSPY, &wrq) < 0)
155     {
156       olsr_printf(1, "Could not get old spylist %s\n", strerror(errno));
157       return 0;
158     }
159
160   /* Copy old addresses */
161   nbr = wrq.u.data.length;
162   memcpy(hw_address, buffer, nbr * sizeof(struct sockaddr));
163
164   olsr_printf(1, "Old addresses: %d\n\n", nbr);
165
166   /* Check upper limit */
167   if(nbr >= IW_MAX_SPY)
168     return 0;
169
170   /* Add new address if MAC exists in ARP cache */
171   if(convert_ip_to_mac(addr, &new_node, interface) > 0)
172     {
173       memcpy(&hw_address[nbr], &new_node, sizeof(struct sockaddr));
174       nbr++;
175     }
176   else
177     return 0;
178   
179   /* Add all addresses */
180   wrq.u.data.pointer = (caddr_t) hw_address;
181   wrq.u.data.length = nbr;
182   wrq.u.data.flags = 0;
183   
184   /* Set device name */
185   strncpy(wrq.ifr_name, interface, IFNAMSIZ);
186   
187   if(ioctl(ioctl_s, SIOCSIWSPY, &wrq) < 0)
188     {
189       olsr_printf(1, "Could not clear spylist %s\n", strerror(errno));
190       return 0;
191     }
192
193
194   return 1;
195 }
196
197
198 int
199 convert_ip_to_mac(union olsr_ip_addr *ip, struct sockaddr *mac, char *interface)
200 {
201   struct arpreq arp_query;
202   struct sockaddr_in tmp_sockaddr;
203
204
205   memset(&arp_query, 0, sizeof(struct arpreq));
206
207   olsr_printf(1, "\nARP conversion for %s interface %s\n", 
208               olsr_ip_to_string(ip),
209               interface);
210
211   tmp_sockaddr.sin_family = AF_INET;
212   tmp_sockaddr.sin_port = 0;
213
214   memcpy(&tmp_sockaddr.sin_addr, ip, ipsize);
215
216   /* Translate IP addresses to MAC addresses */
217   memcpy(&arp_query.arp_pa, &tmp_sockaddr, sizeof(struct sockaddr_in));
218   arp_query.arp_ha.sa_family = 0;
219   arp_query.arp_flags = 0;
220
221   strncpy(arp_query.arp_dev, interface, IFNAMSIZ);
222   
223   if((ioctl(ioctl_s, SIOCGARP, &arp_query) < 0) ||
224      !(arp_query.arp_flags & ATF_COM)) /* ATF_COM - hw addr valid */
225     {
226       olsr_printf(1, "Arp failed: (%s) - trying lookup\n", strerror(errno));
227
228       /* No address - create a thread that sends a PING */
229       send_ping(ip);
230   
231       return -1;
232     }
233
234   olsr_printf(1, "Arp success!\n");
235
236   memcpy(mac, &arp_query.arp_ha, sizeof(struct sockaddr));
237
238   return 1;
239 }
240
241
242
243 /**
244  *A thread that sends a ICMP echo "ping" packet
245  *to a given destination to force the ARP cache
246  *to be updated... kind of a kludge....
247  *
248  *@param _ip the IP address to ping
249  */
250 /* ONLY IPv4 FOR NOW!!! */
251
252 void
253 send_ping(union olsr_ip_addr *ip)
254 {
255   int ping_s;
256   struct sockaddr dst;
257   struct sockaddr_in *dst_in;
258   char *packet;
259   struct icmphdr *icp;
260
261   dst_in = (struct sockaddr_in *) &dst;
262
263   dst_in->sin_family = AF_INET;
264   memcpy(&dst_in->sin_addr, ip, ipsize);
265
266   olsr_printf(1, "pinging %s\n\n", olsr_ip_to_string(ip));
267
268   if ((ping_s = socket(AF_INET, SOCK_RAW, PF_INET)) < 0) 
269     {
270       olsr_printf(1, "Could not create RAW socket for ping!\n%s\n", strerror(errno));
271       return;
272     }
273
274   /* Create packet */
275   packet = malloc(MAXIPLEN + MAXICMPLEN);
276   
277   
278   icp = (struct icmphdr *)packet;
279   icp->type = ICMP_ECHO;
280   icp->code = 0;
281   icp->checksum = 0;
282   icp->un.echo.sequence = 1;
283   icp->un.echo.id = getpid() & 0xFFFF;
284
285   if((sendto(ping_s, packet, MAXIPLEN + MAXICMPLEN + 8, 0, &dst, sizeof(struct sockaddr))) !=
286      MAXIPLEN + MAXICMPLEN + 8)
287     {
288       olsr_printf(1, "Error PING: %s\n", strerror(errno));
289     }
290
291   /* Nevermind the pong ;-) */
292
293   olsr_printf(1, "Ping complete...\n");
294   close(ping_s);
295
296   free(packet);
297
298   return;
299 }
300
301 void
302 poll_link_layer(void *foo)
303 {
304   struct iwreq          wrq;
305   char                  buffer[(sizeof(struct iw_quality) +
306                                sizeof(struct sockaddr)) * IW_MAX_SPY];
307   struct sockaddr       *hwa;
308   struct iw_quality     *qual;
309   int                   n;
310   struct iw_range       range;
311   int                   i, j;
312   int                   has_range = 0;
313   struct interface      *iflist;
314
315   //olsr_printf(1, "Polling link-layer notification...\n");
316
317   for(iflist = ifnet; iflist != NULL; iflist = iflist->int_next)
318     {
319       if(!iflist->is_wireless)
320         continue;
321
322       /* Collect stats */
323       wrq.u.data.pointer = (caddr_t) buffer;
324       wrq.u.data.length = IW_MAX_SPY;
325       wrq.u.data.flags = 0;
326       
327       /* Set device name */
328       strncpy(wrq.ifr_name, iflist->int_name, IFNAMSIZ);
329       
330       /* Do the request */
331       if(ioctl(ioctl_s, SIOCGIWSPY, &wrq) < 0)
332         {
333           olsr_printf(1, "%-8.16s  Interface doesn't support wireless statistic collection\n\n", iflist->int_name);
334           return;
335         }
336       
337       /* Get range info if we can */
338       if(iw_get_range_info(iflist->int_name, &(range)) >= 0)
339         has_range = 1;
340       
341       /* Number of addresses */
342       n = wrq.u.data.length;
343       
344       /* The two lists */
345       hwa = (struct sockaddr *) buffer;
346       qual = (struct iw_quality *) (buffer + (sizeof(struct sockaddr) * n));
347       
348       for(i = 0; i < n; i++)
349         {
350           if(!(qual->updated & 0x7))
351             continue;
352           
353           /* Print stats for each address */
354           olsr_printf(1, "MAC");
355           for(j = 0; j < 6; j++)
356             {
357               olsr_printf(1, ":%02x", (hwa[i].sa_data[j] % 0xffffff00));
358             }
359           if(!has_range)
360             olsr_printf(1, " : Quality:%d  Signal level:%d dBm  Noise level:%d dBm",
361                         qual[i].qual,
362                         qual[i].level - 0x100, 
363                         qual[i].noise - 0x100);
364           else
365             olsr_printf(1, " : Quality:%d/%d  Signal level:%d dBm  Noise level:%d dBm",
366                         qual[i].qual,
367                         range.max_qual.qual,
368                         qual[i].level - 0x100, 
369                         qual[i].noise - 0x100);
370           
371           olsr_printf(1, "\n");
372           
373         }
374     }
375
376   //olsr_printf(1, "\n");
377   return;
378 }
379
380
381
382
383
384 /*
385  * Get the range information out of the driver
386  */
387 int
388 iw_get_range_info(char            *ifname,
389                   struct iw_range *range)
390 {
391   struct iwreq          wrq;
392   char                  buffer[sizeof(struct iw_range) * 2];    /* Large enough */
393   union iw_range_raw    *range_raw;
394
395   /* Cleanup */
396   bzero(buffer, sizeof(buffer));
397
398   wrq.u.data.pointer = (caddr_t) buffer;
399   wrq.u.data.length = sizeof(buffer);
400   wrq.u.data.flags = 0;
401
402   /* Set device name */
403   strncpy(wrq.ifr_name, ifname, IFNAMSIZ);
404
405   if(ioctl(ioctl_s, SIOCGIWRANGE, &wrq) < 0)
406     {
407       olsr_printf(1, "NO RANGE\n");
408       return -1;
409     }
410
411   /* Point to the buffer */
412   range_raw = (union iw_range_raw *) buffer;
413
414   memcpy((char *) range, buffer, sizeof(struct iw_range));
415
416   return 1;
417 }