692c9dfa97d586ff964b0889fd4eebaa85e6b4f6
[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         assert(transmitStringLength <= sizeof(buffer));
158         if (unlikely(transmitStringLength == 0)) {
159                 return false;
160         }
161
162         sendToAllTxInterfaces(&buffer[0], transmitStringLength);
163
164         return true;
165 }
166
167 /**
168  Called by OLSR core when a packet for the plugin is received from the downlink.
169  It unpacks the messages and distributes them into OLSR and on the LAN.
170
171  @param skfd
172  the socket file descriptor on which the packet is received
173  @param data
174  a pointer to the network interface structure on which the packet was received
175  @param flags
176  unused
177  */
178 static void packetReceivedFromDownlink(int skfd, void *data __attribute__ ((unused)), unsigned int flags __attribute__ ((unused))) {
179         if (skfd >= 0) {
180                 unsigned char rxBuffer[BUFFER_SIZE_RX_DOWNLINK];
181                 ssize_t rxCount = 0;
182                 ssize_t rxIndex = 0;
183
184                 /* Receive the captured Ethernet frame */
185                 errno = 0;
186                 rxCount = recvfrom(skfd, &rxBuffer[0], (sizeof(rxBuffer) - 1), 0, NULL, NULL);
187                 if (rxCount < 0) {
188                         pudError(true, "Receive error in %s, ignoring message.", __func__);
189                         return;
190                 }
191
192                 while (rxIndex < rxCount) {
193                         UplinkMessage * msg = (UplinkMessage *) &rxBuffer[rxIndex];
194                         uint8_t type;
195                         uint16_t uplinkMessageLength;
196                         uint16_t olsrMessageLength;
197                         bool ipv6;
198                         union olsr_message * olsrMessage;
199
200                         type = getUplinkMessageType(&msg->header);
201                         olsrMessageLength = getUplinkMessageLength(&msg->header);
202                         uplinkMessageLength = olsrMessageLength + sizeof(UplinkHeader);
203
204                         if (unlikely((rxIndex + uplinkMessageLength) > rxCount)) {
205                                 pudError(false, "Received wrong length (%d) in %s,"
206                                                 " ignoring the rest of the messages.", olsrMessageLength,
207                                                 __func__);
208                                 return;
209                         }
210
211                         rxIndex += uplinkMessageLength;
212
213                         if (type != POSITION) {
214                                 pudError(false, "Received wrong type (%d) in %s,"
215                                                 " ignoring message.", type, __func__);
216                                 continue;
217                         }
218
219                         ipv6 = getUplinkMessageIPv6(&msg->header);
220                         if (unlikely(!ipv6 && (olsr_cnf->ip_version == AF_INET6)) || unlikely(ipv6 && (olsr_cnf->ip_version == AF_INET))) {
221                                 pudError(false, "Received wrong IPv6 status (%s) in %s,"
222                                                 " ignoring message.", (ipv6 ? "true" : "false"),
223                                                 __func__);
224                                 continue;
225                         }
226
227                         olsrMessage = &msg->msg.olsrMessage;
228
229                         /* we now have a position update (olsrMessage) of a certain length
230                          * (olsrMessageLength). this needs to be transmitted over OLSR and on the LAN */
231
232                         /* send out over OLSR interfaces (only when the smart gateway system is enabled) */
233                         if (olsr_cnf->smart_gw_active)
234                         {
235                                 int r;
236                                 struct interface *ifn;
237                                 for (ifn = ifnet; ifn; ifn = ifn->int_next) {
238                                         /* force the pending buffer out if there's not enough space for our message */
239                                         if ((int)olsrMessageLength > net_outbuffer_bytes_left(ifn)) {
240                                           net_output(ifn);
241                                         }
242                                         r = net_outbuffer_push(ifn, olsrMessage, olsrMessageLength);
243                                         if (r != (int) olsrMessageLength) {
244                                                 pudError(
245                                                                 false,
246                                                                 "Could not send to OLSR interface %s: %s"
247                                                                                 " (length=%u, r=%d)",
248                                                                 ifn->int_name,
249                                                                 ((r == -1) ? "no buffer was found" :
250                                                                         (r == 0) ? "there was not enough room in the buffer" :
251                                                                                         "unknown reason"), olsrMessageLength, r);
252                                         }
253                                 }
254                         }
255
256                         /* send out over tx interfaces */
257                         (void) packetReceivedFromOlsr(olsrMessage, NULL, NULL);
258                 }
259         }
260 }
261
262 /**
263  Called by OLSR core when a packet for the plugin is received from the non-OLSR
264  network. It converts the packet into the internal OLSR wire format for a
265  position update and transmits it over all OLSR network interfaces.
266
267  @param skfd
268  the socket file descriptor on which the packet is received
269  @param data
270  a pointer to the network interface structure on which the packet was received
271  @param flags
272  unused
273  */
274 static void packetReceivedForOlsr(int skfd, void *data __attribute__ ((unused)), unsigned int flags __attribute__ ((unused))) {
275         if (skfd >= 0) {
276                 unsigned char rxBuffer[BUFFER_SIZE_RX_NMEA];
277                 ssize_t rxCount;
278                 union olsr_sockaddr sender;
279                 socklen_t senderSize = sizeof(sender);
280
281                 assert(data != NULL);
282
283                 /* Receive the captured Ethernet frame */
284                 memset(&sender, 0, senderSize);
285                 errno = 0;
286                 rxCount = recvfrom(skfd, &rxBuffer[0], (sizeof(rxBuffer) - 1), 0,
287                                 (struct sockaddr *)&sender, &senderSize);
288                 if (rxCount < 0) {
289                         pudError(true, "Receive error in %s, ignoring message.", __func__);
290                         return;
291                 }
292
293                 /* make sure the string is null-terminated */
294                 rxBuffer[rxCount] = '\0';
295
296                 /* only accept messages from configured IP addresses */
297                 if (!isRxAllowedSourceIpAddress(&sender)) {
298                         return;
299                 }
300
301                 /* we have the received string in the rxBuffer now */
302
303                 /* hand the NMEA information to the receiver */
304                 (void) receiverUpdateGpsInformation(&rxBuffer[0], rxCount);
305         }
306 }
307
308 /**
309  * Timer callback that reads the pud position file
310  */
311 static void pud_read_position_file(void *context __attribute__ ((unused))) {
312         updatePositionFromFile();
313         return;
314 }
315
316 /** The timer cookie, used to trace back the originator in debug */
317 static struct olsr_cookie_info *pud_position_file_timer_cookie = NULL;
318
319 /** The timer */
320 static struct timer_entry * pud_position_file_timer = NULL;
321
322 /**
323  Initialise the plugin: check the configuration, initialise the NMEA parser,
324  create network interface sockets, hookup the plugin to OLSR and setup data
325  that can be setup in advance.
326
327  @return
328  - false upon failure
329  - true otherwise
330  */
331 bool initPud(void) {
332         unsigned long long positionFilePeriod;
333
334         if (!checkConfig()) {
335                 pudError(false, "Invalid configuration");
336                 goto error;
337         }
338
339         initState();
340
341         if (!initDeDupList(&deDupList, getDeDupDepth())) {
342                 pudError(false, "Could not initialise de-duplication list");
343                 goto error;
344         }
345
346         if (!startPositionFile()) {
347                 goto error;
348         }
349
350         if (!startReceiver()) {
351                 pudError(false, "Could not start receiver");
352                 goto error;
353         }
354
355         /*
356          * Creates receive and transmit sockets and register the receive sockets
357          * with the OLSR stack
358          */
359         if (!createNetworkInterfaces(&packetReceivedForOlsr,
360                         &packetReceivedFromDownlink)) {
361                 pudError(false, "Could not create require network interfaces");
362                 goto error;
363         }
364
365         if (!checkRunSetup()) {
366                 pudError(false, "Invalid configuration");
367                 goto error;
368         }
369
370         /*
371          * Tell OLSR to call packetReceivedFromOlsr when the packets for this
372          * plugin arrive from the OLSR network
373          */
374         olsr_parser_add_function(&packetReceivedFromOlsr, PUD_OLSR_MSG_TYPE);
375
376         /* switch to syslog logging, load was succesful */
377         pudErrorUseSysLog = !olsr_cnf->no_fork;
378
379         positionFilePeriod = getPositionFilePeriod();
380         if (getPositionFile() && positionFilePeriod) {
381                 if (pud_position_file_timer_cookie == NULL) {
382                         pud_position_file_timer_cookie = olsr_alloc_cookie("pud position file", OLSR_COOKIE_TYPE_TIMER);
383                         if (pud_position_file_timer_cookie == NULL) {
384                                 pudError(false, "Could not allocate pud position file cookie");
385                                 return false;
386                         }
387                 }
388                 if (pud_position_file_timer == NULL) {
389                         pud_position_file_timer = olsr_start_timer(positionFilePeriod, 0, OLSR_TIMER_PERIODIC, &pud_read_position_file,
390                                         NULL, pud_position_file_timer_cookie);
391                         if (pud_position_file_timer == NULL) {
392                                 pudError(false, "Could not start pud position file timer");
393                                 return false;
394                         }
395                 }
396         }
397
398         return true;
399
400         error: closePud();
401         return false;
402 }
403
404 /**
405  Stop the plugin: shut down all created network interface sockets and destroy
406  the NMEA parser.
407  */
408 void closePud(void) {
409         if (pud_position_file_timer != NULL) {
410                 olsr_stop_timer(pud_position_file_timer);
411                 pud_position_file_timer = NULL;
412         }
413         if (pud_position_file_timer_cookie != NULL) {
414                 olsr_free_cookie(pud_position_file_timer_cookie);
415                 pud_position_file_timer_cookie = NULL;
416         }
417         stopPositionFile();
418         closeNetworkInterfaces();
419         stopReceiver();
420         destroyDeDupList(&deDupList);
421 }