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