preliminary multi-interface hack for FreeBSD
[olsrd.git] / src / bsd / net.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 project. For more information see the website or contact
37  * the copyright holders.
38  *
39  * $Id: net.c,v 1.18 2005/03/02 20:53:43 spoggle Exp $
40  */
41
42 #include "defs.h"
43 #include "net_os.h"
44 #include "parser.h" /* dnc: needed for call to packet_parser() */
45 #include "net.h"
46
47 #ifdef __NetBSD__
48 #include <sys/param.h>
49 #endif
50
51 #include <net/if.h>
52 #include <net/if_var.h>
53 #include <net/ethernet.h>
54
55 #include <net80211/ieee80211.h>
56 #include <net80211/ieee80211_ioctl.h>
57 #include <dev/wi/if_wavelan_ieee.h>
58 #include <dev/wi/if_wireg.h>
59
60 #ifdef SPOOF
61 #include <net/if_dl.h>
62 #include <libnet.h>
63 #endif /* SPOOF */
64
65 //#define       SIOCGIFGENERIC  _IOWR('i', 58, struct ifreq)    /* generic IF get op */
66 //#define SIOCGWAVELAN SIOCGIFGENERIC
67
68 #include <sys/sysctl.h>
69
70 static int ignore_redir;
71 static int send_redir;
72 static int gateway;
73
74 static int first_time = 1;
75
76 static int set_sysctl_int(char *name, int new)
77 {
78   int old;
79   unsigned int len = sizeof (old);
80
81   if (sysctlbyname(name, &old, &len, &new, sizeof (new)) < 0)
82     return -1;
83
84   return old;
85 }
86
87 int enable_ip_forwarding(int version)
88 {
89   char *name;
90
91   if (olsr_cnf->ip_version == AF_INET)
92     name = "net.inet.ip.forwarding";
93
94   else
95     name = "net.inet6.ip6.forwarding";
96
97   gateway = set_sysctl_int(name, 1);
98
99   if (gateway < 0)
100     {
101       fprintf(stderr, "Cannot enable IP forwarding. Please enable IP forwarding manually. Continuing in 3 seconds...\n");
102       sleep(3);
103     }
104
105   return 1;
106 }
107
108 int disable_redirects(char *if_name, int index, int version)
109 {
110   char *name;
111
112   // this function gets called for each interface olsrd uses; however,
113   // FreeBSD can only globally control ICMP redirects, and not on a
114   // per-interface basis; hence, only disable ICMP redirects on the first
115   // invocation
116
117   if (first_time == 0)
118     return 1;
119
120   first_time = 0;
121
122   // do not accept ICMP redirects
123
124   if (olsr_cnf->ip_version == AF_INET)
125     name = "net.inet.icmp.drop_redirect";
126
127   else
128     name = "net.inet6.icmp6.drop_redirect";
129
130   ignore_redir = set_sysctl_int(name, 1);
131
132   if (ignore_redir < 0)
133     {
134       fprintf(stderr, "Cannot disable incoming ICMP redirect messages. Please disable them manually. Continuing in 3 seconds...\n");
135       sleep(3);
136     }
137
138   // do not send ICMP redirects
139
140   if (olsr_cnf->ip_version == AF_INET)
141     name = "net.inet.ip.redirect";
142
143   else
144     name = "net.inet6.ip6.redirect";
145
146   send_redir = set_sysctl_int(name, 0);
147
148   if (send_redir < 0)
149     {
150       fprintf(stderr, "Cannot disable outgoing ICMP redirect messages. Please disable them manually. Continuing in 3 seconds...\n");
151       sleep(3);
152     }
153
154   return 1;
155 }
156
157 int deactivate_spoof(char *if_name, int index, int version)
158 {
159   return 1;
160 }
161
162 int restore_settings(int version)
163 {
164   char *name;
165
166   // reset IP forwarding
167
168   if (olsr_cnf->ip_version == AF_INET)
169     name = "net.inet.ip.forwarding";
170
171   else
172     name = "net.inet6.ip6.forwarding";
173
174   set_sysctl_int(name, gateway);
175
176   // reset incoming ICMP redirects
177
178   if (olsr_cnf->ip_version == AF_INET)
179     name = "net.inet.icmp.drop_redirect";
180
181   else
182     name = "net.inet6.icmp6.drop_redirect";
183
184   set_sysctl_int(name, ignore_redir);
185
186   // reset outgoing ICMP redirects
187
188   if (olsr_cnf->ip_version == AF_INET)
189     name = "net.inet.ip.redirect";
190
191   else
192     name = "net.inet6.ip6.redirect";
193
194   set_sysctl_int(name, send_redir);
195
196   return 1;
197 }
198
199 int
200 getsocket(struct sockaddr *sa, int bufspace, char *int_name)
201 {
202   struct sockaddr_in *sin = (struct sockaddr_in *)sa;
203   int sock, on = 1;
204
205   if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) 
206     {
207       perror("socket");
208       syslog(LOG_ERR, "socket: %m");
209       return (-1);
210     }
211
212   if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &on, sizeof (on)) < 0)
213     {
214       perror("setsockopt");
215       syslog(LOG_ERR, "setsockopt SO_BROADCAST: %m");
216       close(sock);
217       return (-1);
218     }
219
220   if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) 
221     {
222       perror("SO_REUSEADDR failed");
223       return (-1);
224     }
225
226 #ifdef SPOOF
227   if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)) < 0) 
228     {
229       perror("SO_REUSEPORT failed");
230       return (-1);
231     }
232
233   if (setsockopt(sock, IPPROTO_IP, IP_RECVIF, &on, sizeof(on)) < 0) 
234     {
235       perror("IP_RECVIF failed");
236       return (-1);
237     }
238 #endif /* SPOOF */
239
240   for (on = bufspace; ; on -= 1024) 
241     {
242       if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
243                      &on, sizeof (on)) == 0)
244         break;
245       if (on <= 8*1024) 
246         {
247           perror("setsockopt");
248           syslog(LOG_ERR, "setsockopt SO_RCVBUF: %m");
249           break;
250         }
251     }
252
253   if (bind(sock, (struct sockaddr *)sin, sizeof (*sin)) < 0) 
254     {
255       perror("bind");
256       syslog(LOG_ERR, "bind: %m");
257       close(sock);
258       return (-1);
259     }
260
261   if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1)
262     syslog(LOG_ERR, "fcntl O_NONBLOCK: %m\n");
263
264   return (sock);
265 }
266
267 int getsocket6(struct sockaddr_in6 *sin, int bufspace, char *int_name)
268 {
269   int sock, on = 1;
270
271   if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) 
272     {
273       perror("socket");
274       syslog(LOG_ERR, "socket: %m");
275       return (-1);
276     }
277
278   for (on = bufspace; ; on -= 1024) 
279     {
280       if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
281                      &on, sizeof (on)) == 0)
282         break;
283       if (on <= 8*1024) 
284         {
285           perror("setsockopt");
286           syslog(LOG_ERR, "setsockopt SO_RCVBUF: %m");
287           break;
288         }
289     }
290
291   if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) 
292     {
293       perror("SO_REUSEADDR failed");
294       return (-1);
295     }
296
297   if (bind(sock, (struct sockaddr *)sin, sizeof (*sin)) < 0) 
298     {
299       perror("bind");
300       syslog(LOG_ERR, "bind: %m");
301       close(sock);
302       return (-1);
303     }
304
305   if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1)
306     syslog(LOG_ERR, "fcntl O_NONBLOCK: %m\n");
307
308   return (sock);
309 }
310
311 int get_ipv6_address(char *ifname, struct sockaddr_in6 *saddr6, int scope_in)
312 {
313   return 0;
314 }
315
316
317
318
319 /**
320  * Wrapper for sendto(2)
321  */
322
323 #ifdef SPOOF
324 static u_int16_t ip_id = 0;
325 #endif /* SPOOF */
326
327 ssize_t
328 olsr_sendto(int s, 
329             const void *buf, 
330             size_t len, 
331             int flags, 
332             const struct sockaddr *to, 
333             socklen_t tolen)
334 {
335 #ifdef SPOOF
336   /* IPv4 for now! */
337
338   libnet_t *context;
339   char errbuf[LIBNET_ERRBUF_SIZE];
340   libnet_ptag_t udp_tag, ip_tag, ether_tag;
341   unsigned char enet_broadcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
342   int status;
343   struct sockaddr_in *to_in = (struct sockaddr_in *) to;
344   u_int32_t destip;
345   struct interface *iface;
346
347   udp_tag = ip_tag = ether_tag = 0;
348   destip = to_in->sin_addr.s_addr;
349   iface = if_ifwithsock (s);
350
351   /* initialize libnet */
352   context = libnet_init(LIBNET_LINK, iface->int_name, errbuf);
353   if (context == NULL)
354     {
355       OLSR_PRINTF (1, "libnet init: %s\n", libnet_geterror (context))
356       return (0);
357     }
358
359   /* initialize IP ID field if necessary */
360   if (ip_id == 0)
361     {
362       ip_id = (u_int16_t) (arc4random () & 0xffff);
363     }
364
365   udp_tag = libnet_build_udp (698,                              /* src port */
366                               698,                              /* dest port */
367                               LIBNET_UDP_H + len,               /* length */
368                               0,                                /* checksum */
369                               buf,                              /* payload */
370                               len,                              /* payload size */
371                               context,                          /* context */
372                               udp_tag);                         /* pblock */
373   if (udp_tag == -1)
374     {
375       OLSR_PRINTF (1, "libnet UDP header: %s\n", libnet_geterror (context))
376         return (0);
377     }
378
379   ip_tag = libnet_build_ipv4 (LIBNET_IPV4_H + LIBNET_UDP_H + len, /* len */
380                               0,                                /* TOS */
381                               ip_id++,                          /* IP id */
382                               0,                                /* IP frag */
383                               1,                                /* IP TTL */
384                               IPPROTO_UDP,                      /* protocol */
385                               0,                                /* checksum */
386                               libnet_get_ipaddr4 (context),     /* src IP */
387                               destip,                           /* dest IP */
388                               NULL,                             /* payload */
389                               0,                                /* payload len */
390                               context,                          /* context */
391                               ip_tag);                          /* pblock */
392   if (ip_tag == -1)
393     {
394       OLSR_PRINTF (1, "libnet IP header: %s\n", libnet_geterror (context))
395       return (0);
396     }
397
398   ether_tag = libnet_build_ethernet (enet_broadcast,            /* ethernet dest */
399                                      libnet_get_hwaddr (context), /* ethernet source */
400                                      ETHERTYPE_IP,              /* protocol type */
401                                      NULL,                      /* payload */
402                                      0,                         /* payload size */
403                                      context,                   /* libnet handle */
404                                      ether_tag);                /* pblock tag */
405   if (ether_tag == -1)
406     {
407       OLSR_PRINTF (1, "libnet ethernet header: %s\n", libnet_geterror (context))
408       return (0);
409     }
410  
411   status = libnet_write (context);
412   if (status == -1)
413     {
414       OLSR_PRINTF (1, "libnet packet write: %s\n", libnet_geterror (context))
415       return (0);
416     }
417
418   libnet_destroy (context);
419
420   return (len);
421
422 #else
423   return sendto(s, buf, len, flags, to, tolen);
424 #endif
425 }
426
427
428 /**
429  * Wrapper for recvfrom(2)
430  */
431
432 ssize_t  
433 olsr_recvfrom(int  s, 
434               void *buf, 
435               size_t len, 
436               int flags, 
437               struct sockaddr *from,
438               socklen_t *fromlen)
439 {
440 #if SPOOF
441   struct msghdr mhdr;
442   struct iovec iov;
443   struct cmsghdr *cm;
444   struct sockaddr_dl *sdl;
445   struct sockaddr_in *sin = (struct sockaddr_in *) from; //XXX
446   unsigned char chdr[4096];
447   int count;
448   struct interface *ifc;
449   char iname[32];
450
451   bzero(&mhdr, sizeof(mhdr));
452   bzero(&iov, sizeof(iov));
453
454   mhdr.msg_name = (caddr_t) from;
455   mhdr.msg_namelen = *fromlen;
456   mhdr.msg_iov = &iov;
457   mhdr.msg_iovlen = 1;
458   mhdr.msg_control = (caddr_t) chdr;
459   mhdr.msg_controllen = sizeof (chdr);
460
461   iov.iov_len = MAXMESSAGESIZE;
462   iov.iov_base = buf;
463
464   count = recvmsg (s, &mhdr, MSG_DONTWAIT);
465   if (count <= 0)
466     {
467       return (count);
468     }
469
470   /* this needs to get communicated back to caller */
471   *fromlen = mhdr.msg_namelen;
472
473   cm = (struct cmsghdr *) chdr;
474   sdl = (struct sockaddr_dl *) CMSG_DATA (cm);
475   bzero (iname, sizeof (iname));
476   memcpy (iname, sdl->sdl_data, sdl->sdl_nlen);
477
478   ifc = if_ifwithsock (s);
479
480   if (strcmp (ifc->int_name, iname) != 0)
481     {
482       return (0);
483     }
484
485   OLSR_PRINTF (2, "%d bytes from %s, socket associated %s really received on %s\n",
486                count,
487                inet_ntoa (sin->sin_addr),
488                ifc->int_name,
489                iname);
490
491   return (count);
492
493 #else /* SPOOF */
494   return recvfrom(s, 
495                   buf, 
496                   len, 
497                   0, 
498                   from, 
499                   fromlen);
500 #endif /* SPOOF */
501 }
502
503 /**
504  * Wrapper for select(2)
505  */
506
507 int
508 olsr_select(int nfds,
509             fd_set *readfds,
510             fd_set *writefds,
511             fd_set *exceptfds,
512             struct timeval *timeout)
513 {
514   return select(nfds,
515                 readfds,
516                 writefds,
517                 exceptfds,
518                 timeout);
519 }
520
521
522 int 
523 check_wireless_interface(char *ifname)
524 {
525 #ifdef __FreeBSD__
526   struct wi_req wreq;
527   struct ifreq ifr;
528
529   memset((char *)&wreq, 0, sizeof(wreq));
530   memset((char *)&ifr, 0, sizeof(ifr));
531
532   wreq.wi_len = WI_MAX_DATALEN;
533   wreq.wi_type = WI_RID_IFACE_STATS;
534
535   strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
536   ifr.ifr_data = (caddr_t)&wreq;
537
538   return (ioctl(ioctl_s, SIOCGWAVELAN, &ifr) >= 0) ? 1 : 0;
539 #else
540   return 0;
541 #endif
542 }
543
544 #include <sys/sockio.h>
545
546 int
547 calculate_if_metric(char *ifname)
548 {
549   if(check_wireless_interface(ifname))
550     {
551       /* Wireless */
552       return 1;
553     }
554   else
555     {
556       /* Ethernet */
557 #if 0
558       /* Andreas: Perhaps SIOCGIFMEDIA is the way to do this? */
559       struct ifmediareq ifm;
560
561       memset(&ifm, 0, sizeof(ifm));
562       strlcpy(ifm.ifm_name, ifname, sizeof(ifm.ifm_name));
563
564       if(ioctl(ioctl_s, SIOCGIFMEDIA, &ifm) < 0)
565         {
566           OLSR_PRINTF(1, "Error SIOCGIFMEDIA(%s)\n", ifm.ifm_name)
567           return WEIGHT_ETHERNET_DEFAULT;
568         }
569
570       OLSR_PRINTF(1, "%s: STATUS 0x%08x\n", ifm.ifm_name, ifm.ifm_status)
571 #endif
572       return WEIGHT_ETHERNET_DEFAULT;
573     }
574 }