Removed the use of threads
[olsrd.git] / src / linux / link_layer.c
1 /*
2  * OLSR ad-hoc routing table management protocol
3  * Copyright (C) 2004 Andreas T√łnnesen (andreto@ifi.uio.no)
4  *
5  * This file is part of olsr.org.
6  *
7  * olsr.org is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * olsr.org is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with olsr.org; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  * 
21  * 
22  * $Id: link_layer.c,v 1.7 2004/11/12 21:20:23 kattemat Exp $
23  *
24  */
25
26 /* Ugly fix to make this compile on wireless extentions < 16 */
27 #define _LINUX_ETHTOOL_H
28
29 #include "../link_layer.h"
30 #include "../olsr_protocol.h"
31 #include "../scheduler.h"
32 #include "../interfaces.h"
33 #include <linux/wireless.h>
34 #include <linux/icmp.h>
35 #include <errno.h>
36 #include <string.h>
37 #include <sys/ioctl.h>
38 #include <net/if_arp.h>
39 #include <unistd.h>
40 #include <stdlib.h>
41
42
43 extern char *
44 sockaddr_to_string(struct sockaddr *);
45
46 extern char *
47 olsr_ip_to_string(union olsr_ip_addr *);
48
49 extern int
50 olsr_printf(int, char *, ...);
51
52
53 #define MAXIPLEN        60
54 #define MAXICMPLEN      76
55
56 extern size_t ipsize;
57
58 extern int ioctl_s;
59
60 float poll_int = 0.2;
61
62 int
63 iw_get_range_info(char *, struct iw_range *);
64
65 int
66 clear_spy_list(char *);
67
68 int
69 convert_ip_to_mac(union olsr_ip_addr *, struct sockaddr *, char *);
70
71 void
72 send_ping(union olsr_ip_addr *);
73
74
75 void
76 init_link_layer_notification()
77 {
78   struct interface *ifd;
79
80   olsr_printf(1, "Initializing link-layer notification...\n");
81
82
83   for (ifd = ifnet; ifd ; ifd = ifd->int_next) 
84     {
85       if(ifd->is_wireless)
86         clear_spy_list(ifd->int_name);
87     }
88
89   olsr_register_scheduler_event(&poll_link_layer, NULL, poll_int, 0, NULL);
90
91   return;
92 }
93
94 int
95 clear_spy_list(char *ifname)
96 {
97   struct iwreq  wrq;
98
99   /* Time to do send addresses to the driver */
100   wrq.u.data.pointer = NULL;//(caddr_t) hw_address;
101   wrq.u.data.length = 0;
102   wrq.u.data.flags = 0;
103
104   /* Set device name */
105   strncpy(wrq.ifr_name, ifname, IFNAMSIZ);
106
107   if(ioctl(ioctl_s, SIOCSIWSPY, &wrq) < 0)
108     {
109       olsr_printf(1, "Could not clear spylist %s\n", strerror(errno));
110       return -1;
111     }
112
113   return 1;
114 }
115
116
117
118 int
119 add_spy_node(union olsr_ip_addr *addr, char *interface)
120 {
121   struct sockaddr       new_node;
122   struct iwreq          wrq;
123   int                   nbr;            /* Number of valid addresses */
124   struct sockaddr       hw_address[IW_MAX_SPY];
125   char  buffer[(sizeof(struct iw_quality) +
126                 sizeof(struct sockaddr)) * IW_MAX_SPY];
127   
128   olsr_printf(1, "Adding spynode!\n\n");
129   
130   /* get all addresses already in the driver */
131
132   wrq.u.data.pointer = (caddr_t) buffer;
133   wrq.u.data.length = IW_MAX_SPY;
134   wrq.u.data.flags = 0;
135
136   strncpy(wrq.ifr_name, interface, IFNAMSIZ);
137
138   if(ioctl(ioctl_s, SIOCGIWSPY, &wrq) < 0)
139     {
140       olsr_printf(1, "Could not get old spylist %s\n", strerror(errno));
141       return 0;
142     }
143
144   /* Copy old addresses */
145   nbr = wrq.u.data.length;
146   memcpy(hw_address, buffer, nbr * sizeof(struct sockaddr));
147
148   olsr_printf(1, "Old addresses: %d\n\n", nbr);
149
150   /* Check upper limit */
151   if(nbr >= IW_MAX_SPY)
152     return 0;
153
154   /* Add new address if MAC exists in ARP cache */
155   if(convert_ip_to_mac(addr, &new_node, interface) > 0)
156     {
157       memcpy(&hw_address[nbr], &new_node, sizeof(struct sockaddr));
158       nbr++;
159     }
160   else
161     return 0;
162   
163   /* Add all addresses */
164   wrq.u.data.pointer = (caddr_t) hw_address;
165   wrq.u.data.length = nbr;
166   wrq.u.data.flags = 0;
167   
168   /* Set device name */
169   strncpy(wrq.ifr_name, interface, IFNAMSIZ);
170   
171   if(ioctl(ioctl_s, SIOCSIWSPY, &wrq) < 0)
172     {
173       olsr_printf(1, "Could not clear spylist %s\n", strerror(errno));
174       return 0;
175     }
176
177
178   return 1;
179 }
180
181
182 int
183 convert_ip_to_mac(union olsr_ip_addr *ip, struct sockaddr *mac, char *interface)
184 {
185   struct arpreq arp_query;
186   struct sockaddr_in tmp_sockaddr;
187
188
189   memset(&arp_query, 0, sizeof(struct arpreq));
190
191   olsr_printf(1, "\nARP conversion for %s interface %s\n", 
192               olsr_ip_to_string(ip),
193               interface);
194
195   tmp_sockaddr.sin_family = AF_INET;
196   tmp_sockaddr.sin_port = 0;
197
198   memcpy(&tmp_sockaddr.sin_addr, ip, ipsize);
199
200   /* Translate IP addresses to MAC addresses */
201   memcpy(&arp_query.arp_pa, &tmp_sockaddr, sizeof(struct sockaddr_in));
202   arp_query.arp_ha.sa_family = 0;
203   arp_query.arp_flags = 0;
204
205   strncpy(arp_query.arp_dev, interface, IFNAMSIZ);
206   
207   if((ioctl(ioctl_s, SIOCGARP, &arp_query) < 0) ||
208      !(arp_query.arp_flags & ATF_COM)) /* ATF_COM - hw addr valid */
209     {
210       olsr_printf(1, "Arp failed: (%s) - trying lookup\n", strerror(errno));
211
212       /* No address - create a thread that sends a PING */
213       send_ping(ip);
214   
215       return -1;
216     }
217
218   olsr_printf(1, "Arp success!\n");
219
220   memcpy(mac, &arp_query.arp_ha, sizeof(struct sockaddr));
221
222   return 1;
223 }
224
225
226
227 /**
228  *A thread that sends a ICMP echo "ping" packet
229  *to a given destination to force the ARP cache
230  *to be updated... kind of a kludge....
231  *
232  *@param _ip the IP address to ping
233  */
234 /* ONLY IPv4 FOR NOW!!! */
235
236 void
237 send_ping(union olsr_ip_addr *ip)
238 {
239   int ping_s;
240   struct sockaddr dst;
241   struct sockaddr_in *dst_in;
242   char *packet;
243   struct icmphdr *icp;
244
245   dst_in = (struct sockaddr_in *) &dst;
246
247   dst_in->sin_family = AF_INET;
248   memcpy(&dst_in->sin_addr, ip, ipsize);
249
250   olsr_printf(1, "pinging %s\n\n", olsr_ip_to_string(ip));
251
252   if ((ping_s = socket(AF_INET, SOCK_RAW, PF_INET)) < 0) 
253     {
254       olsr_printf(1, "Could not create RAW socket for ping!\n%s\n", strerror(errno));
255       return;
256     }
257
258   /* Create packet */
259   packet = malloc(MAXIPLEN + MAXICMPLEN);
260   
261   
262   icp = (struct icmphdr *)packet;
263   icp->type = ICMP_ECHO;
264   icp->code = 0;
265   icp->checksum = 0;
266   icp->un.echo.sequence = 1;
267   icp->un.echo.id = getpid() & 0xFFFF;
268
269   if((sendto(ping_s, packet, MAXIPLEN + MAXICMPLEN + 8, 0, &dst, sizeof(struct sockaddr))) !=
270      MAXIPLEN + MAXICMPLEN + 8)
271     {
272       olsr_printf(1, "Error PING: %s\n", strerror(errno));
273     }
274
275   /* Nevermind the pong ;-) */
276
277   olsr_printf(1, "Ping complete...\n");
278   close(ping_s);
279
280   free(packet);
281
282   return;
283 }
284
285 void
286 poll_link_layer(void *foo)
287 {
288   struct iwreq          wrq;
289   char                  buffer[(sizeof(struct iw_quality) +
290                                sizeof(struct sockaddr)) * IW_MAX_SPY];
291   struct sockaddr       *hwa;
292   struct iw_quality     *qual;
293   int                   n;
294   struct iw_range       range;
295   int                   i, j;
296   int                   has_range = 0;
297   struct interface      *iflist;
298
299   //olsr_printf(1, "Polling link-layer notification...\n");
300
301   for(iflist = ifnet; iflist != NULL; iflist = iflist->int_next)
302     {
303       if(!iflist->is_wireless)
304         continue;
305
306       /* Collect stats */
307       wrq.u.data.pointer = (caddr_t) buffer;
308       wrq.u.data.length = IW_MAX_SPY;
309       wrq.u.data.flags = 0;
310       
311       /* Set device name */
312       strncpy(wrq.ifr_name, iflist->int_name, IFNAMSIZ);
313       
314       /* Do the request */
315       if(ioctl(ioctl_s, SIOCGIWSPY, &wrq) < 0)
316         {
317           olsr_printf(1, "%-8.16s  Interface doesn't support wireless statistic collection\n\n", iflist->int_name);
318           return;
319         }
320       
321       /* Get range info if we can */
322       if(iw_get_range_info(iflist->int_name, &(range)) >= 0)
323         has_range = 1;
324       
325       /* Number of addresses */
326       n = wrq.u.data.length;
327       
328       /* The two lists */
329       hwa = (struct sockaddr *) buffer;
330       qual = (struct iw_quality *) (buffer + (sizeof(struct sockaddr) * n));
331       
332       for(i = 0; i < n; i++)
333         {
334           if(!(qual->updated & 0x7))
335             continue;
336           
337           /* Print stats for each address */
338           olsr_printf(1, "MAC");
339           for(j = 0; j < 6; j++)
340             {
341               olsr_printf(1, ":%02x", (hwa[i].sa_data[j] % 0xffffff00));
342             }
343           if(!has_range)
344             olsr_printf(1, " : Quality:%d  Signal level:%d dBm  Noise level:%d dBm",
345                         qual[i].qual,
346                         qual[i].level - 0x100, 
347                         qual[i].noise - 0x100);
348           else
349             olsr_printf(1, " : Quality:%d/%d  Signal level:%d dBm  Noise level:%d dBm",
350                         qual[i].qual,
351                         range.max_qual.qual,
352                         qual[i].level - 0x100, 
353                         qual[i].noise - 0x100);
354           
355           olsr_printf(1, "\n");
356           
357         }
358     }
359
360   //olsr_printf(1, "\n");
361   return;
362 }
363
364
365
366
367
368 /*
369  * Get the range information out of the driver
370  */
371 int
372 iw_get_range_info(char            *ifname,
373                   struct iw_range *range)
374 {
375   struct iwreq          wrq;
376   char                  buffer[sizeof(struct iw_range) * 2];    /* Large enough */
377   union iw_range_raw    *range_raw;
378
379   /* Cleanup */
380   bzero(buffer, sizeof(buffer));
381
382   wrq.u.data.pointer = (caddr_t) buffer;
383   wrq.u.data.length = sizeof(buffer);
384   wrq.u.data.flags = 0;
385
386   /* Set device name */
387   strncpy(wrq.ifr_name, ifname, IFNAMSIZ);
388
389   if(ioctl(ioctl_s, SIOCGIWRANGE, &wrq) < 0)
390     {
391       olsr_printf(1, "NO RANGE\n");
392       return -1;
393     }
394
395   /* Point to the buffer */
396   range_raw = (union iw_range_raw *) buffer;
397
398   memcpy((char *) range, buffer, sizeof(struct iw_range));
399
400   return 1;
401 }