Merge pull request #78 from ffontaine/master
[olsrd.git] / lib / pud / src / pud.c
1 /*
2  * The olsr.org Optimized Link-State Routing daemon (olsrd)
3  *
4  * (c) by the OLSR project
5  *
6  * See our Git repository to find out who worked on this file
7  * and thus is a copyright holder on it.
8  *
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  *
15  * * Redistributions of source code must retain the above copyright
16  *   notice, this list of conditions and the following disclaimer.
17  * * Redistributions in binary form must reproduce the above copyright
18  *   notice, this list of conditions and the following disclaimer in
19  *   the documentation and/or other materials provided with the
20  *   distribution.
21  * * Neither the name of olsr.org, olsrd nor the names of its
22  *   contributors may be used to endorse or promote products derived
23  *   from this software without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
28  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
29  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
30  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
31  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
35  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  *
38  * Visit http://www.olsr.org for more information.
39  *
40  * If you find this software useful feel free to make a donation
41  * to the project. For more information see the website or contact
42  * the copyright holders.
43  *
44  */
45
46 #include "pud.h"
47
48 /* Plugin includes */
49 #include "dedup.h"
50 #include "networkInterfaces.h"
51 #include "configuration.h"
52 #include "gpsConversion.h"
53 #include "receiver.h"
54 #include "state.h"
55 #include "posFile.h"
56 #include "compiler.h"
57
58 /* OLSRD includes */
59 #include "olsr.h"
60 #include "ipcalc.h"
61 #include "net_olsr.h"
62 #include "parser.h"
63 #include "log.h"
64
65 /* System includes */
66
67 /** The size of the buffer in which the received downlink message is stored */
68 #define BUFFER_SIZE_RX_DOWNLINK 2048
69
70 /** The size of the buffer in which the converted NMEA string is assembled for
71  * transmission over OSLR */
72 #define BUFFER_SIZE_TX_OLSR     512
73
74 /** The de-duplication list */
75 static DeDupList deDupList;
76
77 /** When false, use olsr_printf in pudError, otherwise use olsr_syslog */
78 static bool pudErrorUseSysLog = false;
79
80 /**
81  Report a plugin error.
82
83  @param useErrno
84  when true then errno is used in the error message; the error reason is also
85  reported.
86  @param format
87  a pointer to the format string
88  @param ...
89  arguments to the format string
90  */
91 void pudError(bool useErrno, const char *format, ...) {
92         char strDesc[256];
93         const char *colon;
94         const char *stringErr;
95
96         if ((format == NULL) || (*format == '\0')) {
97                 strDesc[0] = '\0';
98                 colon = "";
99                 if (!useErrno) {
100                         stringErr = "Unknown error";
101                 } else {
102                         stringErr = strerror(errno);
103                 }
104         } else {
105                 va_list arglist;
106
107                 va_start(arglist, format);
108                 vsnprintf(strDesc, sizeof(strDesc), format, arglist);
109                 va_end(arglist);
110
111                 if (useErrno) {
112                         colon = ": ";
113                         stringErr = strerror(errno);
114                 } else {
115                         colon = "";
116                         stringErr = "";
117                 }
118         }
119
120         if (!pudErrorUseSysLog)
121                 olsr_printf(0, "%s: %s%s%s\n", PUD_PLUGIN_ABBR, strDesc, colon, stringErr);
122         else
123                 olsr_syslog(OLSR_LOG_ERR, "%s: %s%s%s\n", PUD_PLUGIN_ABBR, strDesc, colon, stringErr);
124 }
125
126 /**
127  Sends a buffer out on all transmit interfaces
128
129  @param buffer
130  the buffer
131  @param bufferLength
132  the number of bytes in the buffer
133  */
134 static void sendToAllTxInterfaces(unsigned char *buffer,
135                 unsigned int bufferLength) {
136         union olsr_sockaddr * txAddress = getTxMcAddr();
137         void * addr;
138         socklen_t addrSize;
139         TRxTxNetworkInterface *txNetworkInterfaces = getTxNetworkInterfaces();
140
141         if (txAddress->in.sa_family == AF_INET) {
142                 addr = &txAddress->in4;
143                 addrSize = sizeof(struct sockaddr_in);
144         } else {
145                 addr = &txAddress->in6;
146                 addrSize = sizeof(struct sockaddr_in6);
147         }
148
149         while (txNetworkInterfaces != NULL) {
150                 TRxTxNetworkInterface *networkInterface = txNetworkInterfaces;
151                 errno = 0;
152                 if (sendto(networkInterface->socketFd, buffer, bufferLength, 0, addr, addrSize) < 0) {
153                         pudError(true, "Transmit error on interface %s", &networkInterface->name[0]);
154                 }
155                 txNetworkInterfaces = networkInterface->next;
156         }
157 }
158
159 /**
160  Called by OLSR core when a packet for the plugin is received from the OLSR
161  network. It converts the packet into an NMEA string and transmits it over all
162  transmit non-OLSR network interfaces.
163
164  @param olsrMessage
165  a pointer to the received OLSR message
166  @param in_if
167  a pointer to the OLSR network interface on which the packet was received
168  @param ipaddr
169  a pointer to the IP address of the sender
170
171  @return
172  - true when the packet was processed
173  - false otherwise
174  */
175 bool packetReceivedFromOlsr(union olsr_message *olsrMessage,
176                 struct interface_olsr *in_if __attribute__ ((unused)), union olsr_ip_addr *ipaddr __attribute__ ((unused))) {
177         const union olsr_ip_addr * originator = getOlsrMessageOriginator(
178                         olsr_cnf->ip_version, olsrMessage);
179         unsigned int transmitStringLength;
180         unsigned char buffer[BUFFER_SIZE_TX_OLSR];
181
182         /* when we do not loopback then check if the message originated from this
183          * node: back off */
184         if (!getUseLoopback() && ipequal(originator, &olsr_cnf->main_addr)) {
185                 return false;
186         }
187
188         /* do deduplication: when we have already seen this message from the same
189          * originator then just back off */
190         if (likely(getUseDeDup())) {
191                 if (isInDeDupList(&deDupList, olsrMessage)) {
192                         return false;
193                 }
194
195                 addToDeDup(&deDupList, olsrMessage);
196         }
197
198         transmitStringLength = gpsFromOlsr(olsrMessage, &buffer[0], sizeof(buffer));
199         assert(transmitStringLength <= sizeof(buffer));
200         if (unlikely(transmitStringLength == 0)) {
201                 return false;
202         }
203
204         sendToAllTxInterfaces(&buffer[0], transmitStringLength);
205
206         return true;
207 }
208
209 /**
210  Called by OLSR core when a packet for the plugin is received from the downlink.
211  It unpacks the messages and distributes them into OLSR and on the LAN.
212
213  @param skfd
214  the socket file descriptor on which the packet is received
215  @param data
216  a pointer to the network interface structure on which the packet was received
217  @param flags
218  unused
219  */
220 static void packetReceivedFromDownlink(int skfd, void *data __attribute__ ((unused)), unsigned int flags __attribute__ ((unused))) {
221         if (skfd >= 0) {
222                 unsigned char rxBuffer[BUFFER_SIZE_RX_DOWNLINK];
223                 ssize_t rxCount = 0;
224                 ssize_t rxIndex = 0;
225
226                 /* Receive the captured Ethernet frame */
227                 errno = 0;
228                 rxCount = recvfrom(skfd, &rxBuffer[0], (sizeof(rxBuffer) - 1), 0, NULL, NULL);
229                 if (rxCount < 0) {
230                         pudError(true, "Receive error in %s, ignoring message.", __func__);
231                         return;
232                 }
233
234                 while (rxIndex < rxCount) {
235                         UplinkMessage * msg = (UplinkMessage *) &rxBuffer[rxIndex];
236                         uint8_t type;
237                         uint16_t uplinkMessageLength;
238                         uint16_t olsrMessageLength;
239                         bool ipv6;
240                         union olsr_message * olsrMessage;
241
242                         type = getUplinkMessageType(&msg->header);
243                         olsrMessageLength = getUplinkMessageLength(&msg->header);
244                         uplinkMessageLength = olsrMessageLength + sizeof(UplinkHeader);
245
246                         if (unlikely((rxIndex + uplinkMessageLength) > rxCount)) {
247                                 pudError(false, "Received wrong length (%d) in %s,"
248                                                 " ignoring the rest of the messages.", olsrMessageLength,
249                                                 __func__);
250                                 return;
251                         }
252
253                         rxIndex += uplinkMessageLength;
254
255                         if (type != POSITION) {
256                                 pudError(false, "Received wrong type (%d) in %s,"
257                                                 " ignoring message.", type, __func__);
258                                 continue;
259                         }
260
261                         ipv6 = getUplinkMessageIPv6(&msg->header);
262                         if (unlikely(!ipv6 && (olsr_cnf->ip_version == AF_INET6)) || unlikely(ipv6 && (olsr_cnf->ip_version == AF_INET))) {
263                                 pudError(false, "Received wrong IPv6 status (%s) in %s,"
264                                                 " ignoring message.", (ipv6 ? "true" : "false"),
265                                                 __func__);
266                                 continue;
267                         }
268
269                         olsrMessage = &msg->msg.olsrMessage;
270
271                         /* we now have a position update (olsrMessage) of a certain length
272                          * (olsrMessageLength). this needs to be transmitted over OLSR and on the LAN */
273
274                         /* send out over OLSR interfaces (only when the smart gateway system is enabled) */
275                         if (olsr_cnf->smart_gw_active)
276                         {
277                                 int r;
278                                 struct interface_olsr *ifn;
279                                 for (ifn = ifnet; ifn; ifn = ifn->int_next) {
280                                         /* force the pending buffer out if there's not enough space for our message */
281                                         if ((int)olsrMessageLength > net_outbuffer_bytes_left(ifn)) {
282                                           net_output(ifn);
283                                         }
284                                         r = net_outbuffer_push(ifn, olsrMessage, olsrMessageLength);
285                                         if (r != (int) olsrMessageLength) {
286                                                 pudError(
287                                                                 false,
288                                                                 "Could not send to OLSR interface %s: %s"
289                                                                                 " (length=%u, r=%d)",
290                                                                 ifn->int_name,
291                                                                 ((r == -1) ? "no buffer was found" :
292                                                                         (r == 0) ? "there was not enough room in the buffer" :
293                                                                                         "unknown reason"), olsrMessageLength, r);
294                                         }
295                                 }
296                         }
297
298                         /* send out over tx interfaces */
299                         (void) packetReceivedFromOlsr(olsrMessage, NULL, NULL);
300                 }
301         }
302 }
303
304 /**
305  * Timer callback that reads the pud position file
306  */
307 static void pud_read_position_file(void *context __attribute__ ((unused))) {
308         updatePositionFromFile();
309         return;
310 }
311
312 /** The timer cookie, used to trace back the originator in debug */
313 static struct olsr_cookie_info *pud_position_file_timer_cookie = NULL;
314
315 /** The timer */
316 static struct timer_entry * pud_position_file_timer = NULL;
317
318 /**
319  Initialise the plugin: check the configuration, initialise the NMEA parser,
320  create network interface sockets, hookup the plugin to OLSR and setup data
321  that can be setup in advance.
322
323  @return
324  - false upon failure
325  - true otherwise
326  */
327 bool initPud(void) {
328         unsigned long long positionFilePeriod;
329
330         if (!checkConfig()) {
331                 pudError(false, "Invalid configuration");
332                 goto error;
333         }
334
335         initState();
336
337         if (!initDeDupList(&deDupList, getDeDupDepth())) {
338                 pudError(false, "Could not initialise de-duplication list");
339                 goto error;
340         }
341
342         if (!startPositionFile()) {
343                 goto error;
344         }
345
346         if (!startReceiver()) {
347                 pudError(false, "Could not start receiver");
348                 goto error;
349         }
350
351         /*
352          * Creates receive and transmit sockets and register the receive sockets
353          * with the OLSR stack
354          */
355         if (!createNetworkInterfaces(&packetReceivedFromDownlink)) {
356                 pudError(false, "Could not create require network interfaces");
357                 goto error;
358         }
359
360         if (!checkRunSetup()) {
361                 pudError(false, "Invalid configuration");
362                 goto error;
363         }
364
365         /*
366          * Tell OLSR to call packetReceivedFromOlsr when the packets for this
367          * plugin arrive from the OLSR network
368          */
369         olsr_parser_add_function(&packetReceivedFromOlsr, PUD_OLSR_MSG_TYPE);
370
371         /* switch to syslog logging, load was succesful */
372         pudErrorUseSysLog = !olsr_cnf->no_fork;
373
374         positionFilePeriod = getPositionFilePeriod();
375         if (getPositionFile() && positionFilePeriod) {
376                 if (pud_position_file_timer_cookie == NULL) {
377                         pud_position_file_timer_cookie = olsr_alloc_cookie("pud position file", OLSR_COOKIE_TYPE_TIMER);
378                         if (pud_position_file_timer_cookie == NULL) {
379                                 pudError(false, "Could not allocate pud position file cookie");
380                                 return false;
381                         }
382                 }
383                 if (pud_position_file_timer == NULL) {
384                         pud_position_file_timer = olsr_start_timer(positionFilePeriod, 0, OLSR_TIMER_PERIODIC, &pud_read_position_file,
385                                         NULL, pud_position_file_timer_cookie);
386                         if (pud_position_file_timer == NULL) {
387                                 pudError(false, "Could not start pud position file timer");
388                                 return false;
389                         }
390                 }
391         }
392
393         return true;
394
395         error: closePud();
396         return false;
397 }
398
399 /**
400  Stop the plugin: shut down all created network interface sockets and destroy
401  the NMEA parser.
402  */
403 void closePud(void) {
404         if (pud_position_file_timer != NULL) {
405                 olsr_stop_timer(pud_position_file_timer);
406                 pud_position_file_timer = NULL;
407         }
408         if (pud_position_file_timer_cookie != NULL) {
409                 olsr_free_cookie(pud_position_file_timer_cookie);
410                 pud_position_file_timer_cookie = NULL;
411         }
412         stopPositionFile();
413         closeNetworkInterfaces();
414         stopReceiver();
415         destroyDeDupList(&deDupList);
416 }