pud: only forward downlinked messages when the smart gateway system is enabled
[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 (only when the smart gateway system is enabled) */
232                         if (olsr_cnf->smart_gw_active)
233                         {
234                                 int r;
235                                 struct interface *ifn;
236                                 for (ifn = ifnet; ifn; ifn = ifn->int_next) {
237                                         /* force the pending buffer out if there's not enough space for our message */
238                                         if ((int)olsrMessageLength > net_outbuffer_bytes_left(ifn)) {
239                                           net_output(ifn);
240                                         }
241                                         r = net_outbuffer_push(ifn, olsrMessage, olsrMessageLength);
242                                         if (r != (int) olsrMessageLength) {
243                                                 pudError(
244                                                                 false,
245                                                                 "Could not send to OLSR interface %s: %s"
246                                                                                 " (length=%u, r=%d)",
247                                                                 ifn->int_name,
248                                                                 ((r == -1) ? "no buffer was found" :
249                                                                         (r == 0) ? "there was not enough room in the buffer" :
250                                                                                         "unknown reason"), olsrMessageLength, r);
251                                         }
252                                 }
253                         }
254
255                         /* send out over tx interfaces */
256                         (void) packetReceivedFromOlsr(olsrMessage, NULL, NULL);
257                 }
258         }
259 }
260
261 /**
262  Called by OLSR core when a packet for the plugin is received from the non-OLSR
263  network. It converts the packet into the internal OLSR wire format for a
264  position update and transmits it over all OLSR network interfaces.
265
266  @param skfd
267  the socket file descriptor on which the packet is received
268  @param data
269  a pointer to the network interface structure on which the packet was received
270  @param flags
271  unused
272  */
273 static void packetReceivedForOlsr(int skfd, void *data __attribute__ ((unused)), unsigned int flags __attribute__ ((unused))) {
274         if (skfd >= 0) {
275                 unsigned char rxBuffer[BUFFER_SIZE_RX_NMEA];
276                 ssize_t rxCount;
277                 union olsr_sockaddr sender;
278                 socklen_t senderSize = sizeof(sender);
279
280                 assert(data != NULL);
281
282                 /* Receive the captured Ethernet frame */
283                 memset(&sender, 0, senderSize);
284                 errno = 0;
285                 rxCount = recvfrom(skfd, &rxBuffer[0], (sizeof(rxBuffer) - 1), 0,
286                                 (struct sockaddr *)&sender, &senderSize);
287                 if (rxCount < 0) {
288                         pudError(true, "Receive error in %s, ignoring message.", __func__);
289                         return;
290                 }
291
292                 /* make sure the string is null-terminated */
293                 rxBuffer[rxCount] = '\0';
294
295                 /* only accept messages from configured IP addresses */
296                 if (!isRxAllowedSourceIpAddress(&sender)) {
297                         return;
298                 }
299
300                 /* we have the received string in the rxBuffer now */
301
302                 /* hand the NMEA information to the receiver */
303                 (void) receiverUpdateGpsInformation(&rxBuffer[0], rxCount);
304         }
305 }
306
307 /**
308  * Timer callback that reads the pud position file
309  */
310 static void pud_read_position_file(void *context __attribute__ ((unused))) {
311         updatePositionFromFile();
312         return;
313 }
314
315 /** The timer cookie, used to trace back the originator in debug */
316 static struct olsr_cookie_info *pud_position_file_timer_cookie = NULL;
317
318 /** The timer */
319 static struct timer_entry * pud_position_file_timer = NULL;
320
321 /**
322  Initialise the plugin: check the configuration, initialise the NMEA parser,
323  create network interface sockets, hookup the plugin to OLSR and setup data
324  that can be setup in advance.
325
326  @return
327  - false upon failure
328  - true otherwise
329  */
330 bool initPud(void) {
331         unsigned long long positionFilePeriod;
332
333         if (!checkConfig()) {
334                 pudError(false, "Invalid configuration");
335                 goto error;
336         }
337
338         initState();
339
340         if (!initDeDupList(&deDupList, getDeDupDepth())) {
341                 pudError(false, "Could not initialise de-duplication list");
342                 goto error;
343         }
344
345         if (!startPositionFile()) {
346                 goto error;
347         }
348
349         if (!startReceiver()) {
350                 pudError(false, "Could not start receiver");
351                 goto error;
352         }
353
354         /*
355          * Creates receive and transmit sockets and register the receive sockets
356          * with the OLSR stack
357          */
358         if (!createNetworkInterfaces(&packetReceivedForOlsr,
359                         &packetReceivedFromDownlink)) {
360                 pudError(false, "Could not create require network interfaces");
361                 goto error;
362         }
363
364         if (!checkRunSetup()) {
365                 pudError(false, "Invalid configuration");
366                 goto error;
367         }
368
369         /*
370          * Tell OLSR to call packetReceivedFromOlsr when the packets for this
371          * plugin arrive from the OLSR network
372          */
373         olsr_parser_add_function(&packetReceivedFromOlsr, PUD_OLSR_MSG_TYPE);
374
375         /* switch to syslog logging, load was succesful */
376         pudErrorUseSysLog = !olsr_cnf->no_fork;
377
378         positionFilePeriod = getPositionFilePeriod();
379         if (getPositionFile() && positionFilePeriod) {
380                 if (pud_position_file_timer_cookie == NULL) {
381                         pud_position_file_timer_cookie = olsr_alloc_cookie("pud position file", OLSR_COOKIE_TYPE_TIMER);
382                         if (pud_position_file_timer_cookie == NULL) {
383                                 pudError(false, "Could not allocate pud position file cookie");
384                                 return false;
385                         }
386                 }
387                 if (pud_position_file_timer == NULL) {
388                         pud_position_file_timer = olsr_start_timer(positionFilePeriod, 0, OLSR_TIMER_PERIODIC, &pud_read_position_file,
389                                         NULL, pud_position_file_timer_cookie);
390                         if (pud_position_file_timer == NULL) {
391                                 pudError(false, "Could not start pud position file timer");
392                                 return false;
393                         }
394                 }
395         }
396
397         return true;
398
399         error: closePud();
400         return false;
401 }
402
403 /**
404  Stop the plugin: shut down all created network interface sockets and destroy
405  the NMEA parser.
406  */
407 void closePud(void) {
408         if (pud_position_file_timer != NULL) {
409                 olsr_stop_timer(pud_position_file_timer);
410                 pud_position_file_timer = NULL;
411         }
412         if (pud_position_file_timer_cookie != NULL) {
413                 olsr_free_cookie(pud_position_file_timer_cookie);
414                 pud_position_file_timer_cookie = NULL;
415         }
416         stopPositionFile();
417         closeNetworkInterfaces();
418         stopReceiver();
419         destroyDeDupList(&deDupList);
420 }