609ffe7f50ae76568149d46cc93e96a021dc5c9a
[olsrd.git] / lib / arprefresh / src / olsrd_arprefresh.c
1
2 /*
3  * Copyright (c) 2007, Sven-Ola Tuecke <sven-ola-aet-gmx.de>
4  * Copyright (c) 2004, Andreas Tonnesen(andreto-at-olsr.org)
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright notice,
12  *   this list of conditions and the following disclaimer.
13  * * 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  * * Neither the name of the UniK olsr daemon nor the names of its contributors
17  *   may be used to endorse or promote products derived from this software
18  *   without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
28  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
29  * OF THE POSSIBILITY OF SUCH DAMAGE.
30  *
31  */
32
33 /*
34  * Plugin to refresh the local ARP cache from received OLSR broadcasts
35  */
36
37 #include <stdio.h>
38 #include <string.h>
39 #include <stdlib.h>
40 #include <fcntl.h>
41 #include <sys/ioctl.h>
42 #include <sys/socket.h>
43 #include <net/if.h>
44 #include <net/if_arp.h>
45 #include <net/ethernet.h>
46 #include <netinet/ip.h>
47 #include <netinet/udp.h>
48 #include <netpacket/packet.h>
49 #include <linux/types.h>
50 #include <linux/filter.h>
51 #include <unistd.h>
52
53 #include "olsrd_arprefresh.h"
54 #include "kernel_routes.h"
55 #include "scheduler.h"
56
57 #undef ARPREFRESH_DEBUG
58 #define PLUGIN_INTERFACE_VERSION 5
59
60 /****************************************************************************
61  *                Functions that the plugin MUST provide                    *
62  ****************************************************************************/
63
64 /**
65  * Plugin interface version
66  * Used by main olsrd to check plugin interface version
67  */
68 int
69 olsrd_plugin_interface_version(void)
70 {
71   return PLUGIN_INTERFACE_VERSION;
72 }
73
74 static const struct olsrd_plugin_parameters plugin_parameters[] = {
75 };
76
77 void
78 olsrd_get_plugin_parameters(const struct olsrd_plugin_parameters **params, int *size)
79 {
80   *params = plugin_parameters;
81   *size = sizeof(plugin_parameters) / sizeof(*plugin_parameters);
82 }
83
84 typedef struct {
85   struct ethhdr eth;
86   struct iphdr ip;
87   struct udphdr udp;
88 } __attribute__ ((packed)) arprefresh_buf;
89
90 static int arprefresh_sockfd = -1;
91 static const int arprefresh_portnum = 698;
92
93 /**
94  * Scheduled event to fetch gathered packets and update the ARP cache
95  * called from olsrd main thread
96  */
97 static void
98 olsr_arp_event(void *foo __attribute__ ((unused)))
99 {
100   if (0 <= arprefresh_sockfd) {
101     arprefresh_buf buf;
102     struct sockaddr_ll from;
103     socklen_t fromlen = sizeof(from);
104
105     /*
106      * Grab a single snapshot on the packet socket. This only works
107      * if not too much IP traffic is currently flowing through.
108      */
109     ssize_t size = recvfrom(arprefresh_sockfd, &buf, sizeof(buf),
110                             MSG_TRUNC, (struct sockaddr *)&from,
111                             &fromlen);
112
113     if (0 <= size && size >= (ssize_t) sizeof(buf)
114
115                                                  /*** &&
116                     ETH_P_IP == ntohs(buf.eth.h_proto) &&
117                     IPPROTO_UDP == buf.ip.protocol &&
118                     arprefresh_portnum == ntohs(buf.udp.source) &&
119                     arprefresh_portnum == ntohs(buf.udp.dest) ***/
120       ) {
121       union {
122         struct arpreq arp;
123         struct sockaddr_in in_pa;
124         struct sockaddr_in6 in_pa6;
125       } req;
126
127       memset(&req, 0, sizeof(req));
128       req.in_pa.sin_family = AF_INET;
129       memcpy(&req.in_pa.sin_addr, &buf.ip.saddr, sizeof(buf.ip.saddr));
130       req.arp.arp_ha.sa_family = AF_LOCAL;
131       memcpy(&req.arp.arp_ha.sa_data, &buf.eth.h_source, sizeof(buf.eth.h_source));
132       req.arp.arp_flags = ATF_COM;
133       if_indextoname(from.sll_ifindex, req.arp.arp_dev);
134 #ifdef ARPREFRESH_DEBUG
135       {
136         int i;
137         OLSR_PRINTF(0, "Refresh on %s, %s=", req.arp.arp_dev, inet_ntoa(*((struct in_addr *)&buf.ip.saddr)));
138         for (i = 0; i < (ssize_t) sizeof(buf.eth.h_source); i++) {
139           OLSR_PRINTF(0, "%02x%s", ((unsigned char *)&buf.eth.h_source)[i],
140                       i < (ssize_t) sizeof(buf.eth.h_source) - 1 ? ":" : "\n");
141         }
142       }
143 #endif
144       if (ioctl(arprefresh_sockfd, SIOCSARP, &req) < 0) {
145         OLSR_PRINTF(1, "*** ARPREFRESH: SIOCSARP: %s\n", strerror(errno));
146         close(arprefresh_sockfd);
147         arprefresh_sockfd = -1;
148         return;
149       }
150     }
151   }
152 }
153
154 /**
155  * Initialize plugin
156  * Called after all parameters are passed
157  */
158 int
159 olsrd_plugin_init(void)
160 {
161   int ret = 0;
162   if (AF_INET == olsr_cnf->ip_version) {
163     int flags;
164     struct sock_fprog filter;
165     struct sock_filter BPF_code[] = {
166       /* tcpdump -s [sizeof(arprefresh_buf)] -ni lo udp and dst port [arprefresh_portnum] -dd */
167       {0x28, 0, 0, 0x0000000c},
168       {0x15, 0, 4, 0x000086dd},
169       {0x30, 0, 0, 0x00000014},
170       {0x15, 0, 11, 0x00000011},
171       {0x28, 0, 0, 0x00000038},
172       {0x15, 8, 9, arprefresh_portnum},
173       {0x15, 0, 8, 0x00000800},
174       {0x30, 0, 0, 0x00000017},
175       {0x15, 0, 6, 0x00000011},
176       {0x28, 0, 0, 0x00000014},
177       {0x45, 4, 0, 0x00001fff},
178       {0xb1, 0, 0, 0x0000000e},
179       {0x48, 0, 0, 0x00000010},
180       {0x15, 0, 1, arprefresh_portnum},
181       {0x6, 0, 0, sizeof(arprefresh_buf)}
182       ,
183       {0x6, 0, 0, 0x00000000}
184     };
185     filter.len = sizeof(BPF_code) / sizeof(BPF_code[0]);
186     filter.filter = BPF_code;
187     if (0 <= (arprefresh_sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP)))
188         && 0 <= (flags = fcntl(arprefresh_sockfd, F_GETFL))
189         && 0 <= fcntl(arprefresh_sockfd, F_SETFL, flags | O_NONBLOCK)
190         && 0 <= setsockopt(arprefresh_sockfd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter))) {
191       /* Register the ARP refresh event */
192       olsr_start_timer(2 * MSEC_PER_SEC, 0, OLSR_TIMER_PERIODIC, &olsr_arp_event, NULL, 0);
193       ret = 1;
194     } else {
195       OLSR_PRINTF(1, "*** ARPREFRESH: Cannot create non-blocking filtering packet socket: %s\n", strerror(errno));
196     }
197   } else {
198     OLSR_PRINTF(1, "*** ARPREFRESH: IPv6 not supported\n");
199   }
200   return ret;
201 }
202
203 /****************************************************************************
204  *       Optional private constructor and destructor functions              *
205  ****************************************************************************/
206
207 static void __attribute__ ((constructor)) my_init(void);
208 static void __attribute__ ((destructor)) my_fini(void);
209
210 static void
211 my_init(void)
212 {
213   printf("OLSRD arprefresh plugin by Sven-Ola\n");
214 }
215
216 /**
217  * Optional Private Destructor
218  */
219 static void
220 my_fini(void)
221 {
222   if (0 <= arprefresh_sockfd) {
223     close(arprefresh_sockfd);
224     arprefresh_sockfd = -1;
225   }
226 }
227
228 /*
229  * Local Variables:
230  * c-basic-offset: 2
231  * indent-tabs-mode: nil
232  * End:
233  */