arprefresh: add a note about VLANs and IPv6
[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  * Note: this code does not work with IPv6 and not with VLANs (on IPv4 or IPv6)
37  */
38
39 #include <stdio.h>
40 #include <string.h>
41 #include <stdlib.h>
42 #include <fcntl.h>
43 #include <sys/ioctl.h>
44 #include <sys/socket.h>
45 #include <net/if.h>
46 #include <net/if_arp.h>
47 #ifndef __ANDROID__
48 #include <net/ethernet.h>
49 #endif /* __ANDROID__ */
50 #include <netinet/ip.h>
51 #include <netinet/udp.h>
52 #include <netpacket/packet.h>
53 #include <linux/types.h>
54 #include <linux/filter.h>
55 #include <unistd.h>
56
57 #include "olsrd_arprefresh.h"
58 #include "kernel_routes.h"
59 #include "scheduler.h"
60
61 #undef ARPREFRESH_DEBUG
62 #define PLUGIN_INTERFACE_VERSION 5
63
64 /****************************************************************************
65  *                Functions that the plugin MUST provide                    *
66  ****************************************************************************/
67
68 /**
69  * Plugin interface version
70  * Used by main olsrd to check plugin interface version
71  */
72 int
73 olsrd_plugin_interface_version(void)
74 {
75   return PLUGIN_INTERFACE_VERSION;
76 }
77
78 static const struct olsrd_plugin_parameters plugin_parameters[] = {
79 };
80
81 void
82 olsrd_get_plugin_parameters(const struct olsrd_plugin_parameters **params, int *size)
83 {
84   *params = plugin_parameters;
85   *size = sizeof(plugin_parameters) / sizeof(*plugin_parameters);
86 }
87
88 typedef struct {
89   struct ethhdr eth;
90   struct iphdr ip;
91   struct udphdr udp;
92 } __attribute__ ((packed)) arprefresh_buf;
93
94 static int arprefresh_sockfd = -1;
95 static const int arprefresh_portnum = 698;
96
97 /**
98  * Scheduled event to fetch gathered packets and update the ARP cache
99  * called from olsrd main thread
100  */
101 static void
102 olsr_arp_event(void *foo __attribute__ ((unused)))
103 {
104   if (0 <= arprefresh_sockfd) {
105     arprefresh_buf buf;
106     struct sockaddr_ll from;
107     socklen_t fromlen = sizeof(from);
108
109     /*
110      * Grab a single snapshot on the packet socket. This only works
111      * if not too much IP traffic is currently flowing through.
112      */
113     ssize_t size = recvfrom(arprefresh_sockfd, &buf, sizeof(buf),
114                             MSG_TRUNC, (struct sockaddr *)&from,
115                             &fromlen);
116
117     if (0 <= size && size >= (ssize_t) sizeof(buf)) {
118       union {
119         struct arpreq arp;
120         struct sockaddr_in in_pa;
121         struct sockaddr_in6 in_pa6;
122       } req;
123
124       memset(&req, 0, sizeof(req));
125       req.in_pa.sin_family = AF_INET;
126       memcpy(&req.in_pa.sin_addr, &buf.ip.saddr, sizeof(buf.ip.saddr));
127       req.arp.arp_ha.sa_family = AF_LOCAL;
128       memcpy(&req.arp.arp_ha.sa_data, &buf.eth.h_source, sizeof(buf.eth.h_source));
129       req.arp.arp_flags = ATF_COM;
130       if_indextoname(from.sll_ifindex, req.arp.arp_dev);
131 #ifdef ARPREFRESH_DEBUG
132       {
133         int i;
134         OLSR_PRINTF(0, "Refresh on %s, %s=", req.arp.arp_dev, inet_ntoa(*((struct in_addr *)&buf.ip.saddr)));
135         for (i = 0; i < (ssize_t) sizeof(buf.eth.h_source); i++) {
136           OLSR_PRINTF(0, "%02x%s", ((unsigned char *)&buf.eth.h_source)[i],
137                       i < (ssize_t) sizeof(buf.eth.h_source) - 1 ? ":" : "\n");
138         }
139       }
140 #endif /* ARPREFRESH_DEBUG */
141       if (ioctl(arprefresh_sockfd, SIOCSARP, &req) < 0) {
142         OLSR_PRINTF(1, "*** ARPREFRESH: SIOCSARP: %s\n", strerror(errno));
143         close(arprefresh_sockfd);
144         arprefresh_sockfd = -1;
145         return;
146       }
147     }
148   }
149 }
150
151 /**
152  * Initialize plugin
153  * Called after all parameters are passed
154  */
155 int
156 olsrd_plugin_init(void)
157 {
158   int ret = 0;
159   if (AF_INET == olsr_cnf->ip_version) {
160     int flags;
161     struct sock_fprog filter;
162     struct sock_filter BPF_code[] = {
163       /* tcpdump -s [sizeof(arprefresh_buf)] -ni lo udp and dst port [arprefresh_portnum] -dd */
164
165       /* Note: This program assumes NON-VLAN */
166       /* See also http://www.unix.com/man-page/FreeBSD/4/bpf/ */
167       /*      code                          jump-true jump-false generic-multiuse-field */
168              {BPF_LD  | BPF_H    | BPF_ABS , 0,        0,         0x0000000c},             // 0x28: A <- P[k:2], get 2-bytes from offset 0x0c : get Ethernet type
169
170 /* test4  */ {BPF_JMP | BPF_JEQ  | BPF_K   , 0,        8,         0x00000800},             // 0x15: pc += (A == k) ? ipv4 : ignore            : IPv4?
171
172 /* ipv4   */ {BPF_LD  | BPF_B    | BPF_ABS , 0,        0,         0x00000017},             // 0x30: A <- P[k:1], get 1-byte from offset 0x17  : get IPv4 protocol
173              {BPF_JMP | BPF_JEQ  | BPF_K   , 0,        6,         0x00000011},             // 0x15: pc += (A == k) ? udp4 : ignore            : UDP?
174 /* udp4   */ {BPF_LD  | BPF_H    | BPF_ABS , 0,        0,         0x00000014},             // 0x28: A <- P[k:2], get 2-bytes from offset 0x14 : get fragment offset
175              {BPF_JMP | BPF_JSET | BPF_K   , 4,        0,         0x00001fff},             // 0x45: pc += (A & k) ? ignore : nofrag           : is this a fragment?
176 /* nofrag */ {BPF_LDX | BPF_B    | BPF_MSH , 0,        0,         0x0000000e},             // 0xb1: X <- 4*(P[k:1]&0xf)                       : get the IP header length in bytes
177              {BPF_LD  | BPF_H    | BPF_IND , 0,        0,         0x00000010},             // 0x48: A <- P[X+k:2]                             : get UDP destination port
178              {BPF_JMP | BPF_JEQ  | BPF_K   , 0,        1,         arprefresh_portnum},     // 0x15: pc += (A == k) ? ok : ignore              : sent to port arprefresh_portnum?
179
180 /* ok     */ {BPF_RET | BPF_K              , 0,        0,         sizeof(arprefresh_buf)}, //                                                 : accept sizeof(arprefresh_buf) bytes (all headers)
181 /* ignore */ {BPF_RET | BPF_K              , 0,        0,         0x00000000}              //                                                 : accept 0 bytes
182
183     };
184     filter.len = ARRAYSIZE(BPF_code);
185     filter.filter = BPF_code;
186     if (0 <= (arprefresh_sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP)))
187         && 0 <= (flags = fcntl(arprefresh_sockfd, F_GETFL))
188         && 0 <= fcntl(arprefresh_sockfd, F_SETFL, flags | O_NONBLOCK)
189         && 0 <= setsockopt(arprefresh_sockfd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter))) {
190       /* Register the ARP refresh event */
191       olsr_start_timer(2 * MSEC_PER_SEC, 0, OLSR_TIMER_PERIODIC, &olsr_arp_event, NULL, 0);
192       ret = 1;
193     } else {
194       OLSR_PRINTF(1, "*** ARPREFRESH: Cannot create non-blocking filtering packet socket: %s\n", strerror(errno));
195     }
196   } else {
197     OLSR_PRINTF(1, "*** ARPREFRESH: IPv6 not supported\n");
198   }
199   return ret;
200 }
201
202 /****************************************************************************
203  *       Optional private constructor and destructor functions              *
204  ****************************************************************************/
205
206 static void __attribute__ ((constructor)) my_init(void);
207 static void __attribute__ ((destructor)) my_fini(void);
208
209 static void
210 my_init(void)
211 {
212   printf("OLSRD arprefresh plugin by Sven-Ola\n");
213 }
214
215 /**
216  * Optional Private Destructor
217  */
218 static void
219 my_fini(void)
220 {
221   if (0 <= arprefresh_sockfd) {
222     close(arprefresh_sockfd);
223     arprefresh_sockfd = -1;
224   }
225 }
226
227 /*
228  * Local Variables:
229  * c-basic-offset: 2
230  * indent-tabs-mode: nil
231  * End:
232  */