1 /*
2  *
3  * XASTIR, Amateur Station Tracking and Information Reporting
4  * Copyright (C) 1999,2000  Frank Giannandrea
5  * Copyright (C) 2000-2019 The Xastir Group
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20  *
21  * Look at the README for more information on the program.
22  */
23 
24 #ifdef HAVE_CONFIG_H
25   #include "config.h"
26 #endif  // HAVE_CONFIG_H
27 
28 #include "snprintf.h"
29 
30 #include <termios.h>
31 #include <stdio.h>
32 #include <fcntl.h>
33 #include <ctype.h>
34 #include <unistd.h>
35 #include <signal.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <Xm/XmAll.h>
39 #include <X11/Xatom.h>
40 #include <X11/Shell.h>
41 
42 #include "xastir.h"
43 #include "igate.h"
44 #include "main.h"
45 #include "interface.h"
46 #include "xa_config.h"
47 #include "util.h"
48 
49 // Must be last include file
50 #include "leak_detection.h"
51 
52 
53 
54 time_t last_nws_stations_file_time = 0;
55 int NWS_stations = 0;
56 int max_NWS_stations = 0;
57 NWS_Data *NWS_station_data;
58 
59 
60 void load_NWS_stations(char *file);
61 int check_NWS_stations(char *call);
62 
63 
64 
65 // Struct for holding packet data.  We use dynamically-allocated
66 // singly-linked lists.  The last record should have its "next"
67 // pointer set to NULL.
68 //
69 typedef struct _DupeRecord
70 {
71   char    data[MAX_LINE_SIZE+15]; // Packet data
72   time_t  time;                       // The time the record was inserted
73   struct  _DupeRecord *next;          // pointer to next record in list
74 } DupeRecord;
75 
76 
77 
78 // Sent and Heard queue pointers.  These are used for the dupe-checking
79 // we do in the below routines.  We have one Sent and one Heard queue
80 // for each interface device.  These pointers will point to the head of
81 // each queue.  We really only need these queues for each TNC interface,
82 // but the user might destroy a NET interface and create a TNC interface
83 // during a single runtime, so we need to populate all of the pointers
84 // just in case.  If people switch types, the old queue will empty out
85 // (effectively anyway) within XX seconds, so we don't have to worry
86 // about cleaning out the queues in this case.
87 //
88 DupeRecord *heard_queue[MAX_IFACE_DEVICES];
89 DupeRecord  *sent_queue[MAX_IFACE_DEVICES];
90 
91 
92 
93 // Types of queues for each interface
94 #define HEARD               0
95 #define SENT                1
96 
97 
98 
99 // Insert mode for the queues
100 #define NO_FORCED_INSERT    0
101 #define FORCED_INSERT       1
102 
103 
104 
105 
106 
107 // Initialization routine for this module which sets up the queue
108 // pointers when Xastir first starts.  Called from main.c:main()
109 //
igate_init(void)110 void igate_init(void)
111 {
112   int i;
113 
114   for (i = 0; i < MAX_IFACE_DEVICES; i++)
115   {
116     heard_queue[i] = NULL;
117     sent_queue[i] = NULL;
118   }
119 }
120 
121 
122 
123 
124 
125 //
126 // not_a_dupe:  Function which checks for duplicate packets.
127 //
128 // Returns: 1 if it's _not_ a duplicate record or we have an error
129 //          0 if it _is_ a duplicate record
130 //
131 // Since we need to run through every record checking for dupes anyway,
132 // we check the timestamp on each one as we go through.  If too old, we
133 // delete it from the head of the chain.  We add new records to the end.
134 // This makes it easy to keep it as a singly-linked list, and only have
135 // to deal with one record at a time.
136 //
137 // The way this is set up we keep a thirty second queue for each
138 // interface.  Any records older than this are at the head of the chain
139 // and are deleted one by one whenever this routine is called again due
140 // to a new packet coming through.  It's ok if these records sit around
141 // on the queue for a long time due to no igate activity.  It doesn't
142 // take long to delete them!
143 //
not_a_dupe(int queue_type,int port,char * line,int insert_mode)144 int not_a_dupe(int queue_type, int port, char *line, int insert_mode)
145 {
146   DupeRecord *ptr;
147   DupeRecord *ptr_last;
148   int insert_new;
149   time_t time_cutoff;
150   int found_dupe = 0;
151   char match_line[MAX_LINE_SIZE*2];
152   char line2[MAX_LINE_SIZE+1];
153   char *c0, *c1, *c2;
154 
155 
156   if ( (line == NULL) || (line[0] == '\0') )
157   {
158     return(1);
159   }
160 
161 
162   // Figure out what's "old"
163   time_cutoff = sec_now() - (time_t)29;   // 29 seconds ago
164 
165 
166   // Fill the destination string with zeroes.  This is a nice
167   // segfault-prevention technique.  Whatever strings we throw in here
168   // will be automatically terminated.
169   memset(match_line, 0, MAX_LINE_SIZE*2);
170 
171 
172   switch (queue_type)
173   {
174 
175     case HEARD:
176       ptr_last = ptr = heard_queue[port]; // May be NULL!
177 
178       // The insert_into_heard_queue() function below (called by
179       // db.c decode routines in turn) will call this function
180       // with FORCED_INSERT.  Other routines in igate.c will call
181       // it with NO_FORCED_INSERT.  For the Heard queue we only
182       // want the db.c decode routines inserting records.
183       if (insert_mode == FORCED_INSERT)
184       {
185         insert_new = 1;  // Insert new records.
186       }
187       else
188       {
189         insert_new = 0;  // Don't insert new records.
190       }
191 
192       // RF packets will have third-party headers and regular
193       // headers that must be stripped off.  We only want to store
194       // 3rd party RF strings in the Heard queue as the others
195       // aren't going to be igated anyway.  For matching and
196       // storage purposes the 3rd party packets should look
197       // identical to how they were originally passed on the 'net,
198       // so that we can try to find duplicates before transmitting
199       // them again.
200 
201 // NOTE:  Below is the parsing code for an internet packet for the Sent
202 // queue.  Modify it to parse 3rd party packets for the Heard queue.
203 
204 // VE7VFM-12>APD214,VE7VAN-3*,WIDE3*:}WA7JAK>APK002,TCPIP*,VE7VFM-12*::N7WGR-7  :does{2
205 
206       // Changes needed before parsing code:  Get rid of first
207       // part of packet up to the '}' symbol.  After this the
208       // generic parsing code will work.
209 // Note that the REPLY-ACK algorithm also uses the '}' symbol.
210 
211       c0 = strstr(line, ":}"); // Find start of 3rd party packet
212       if (c0 == NULL)     // Not 3rd party packet
213       {
214         if (debug_level & 1024)
215         {
216           fprintf(stderr," Not 3rd party HeardQ: %s\n",line);
217         }
218         return(1);
219       }
220 
221       // Copy original packet into line2 for later parsing.  We
222       // want to keep the '}' character because our own
223       // transmissions out RF have that character as well.
224 // Note that the REPLY-ACK algorithm also uses the '}' symbol.
225       if (debug_level & 1024)
226       {
227         fprintf(stderr,"3rd party HeardQ: %s\n",line);
228       }
229 
230       xastir_snprintf(line2,
231                       sizeof(line2),
232                       "%s",
233                       c0+1);
234 
235       break;
236 
237     case SENT:
238       // For this queue we always want to insert records.  Only
239       // igate.c functions call this.
240       ptr_last = ptr = sent_queue[port];  // May be NULL!
241       insert_new = 1; // Insert new records
242 
243       // No extra changes needed before parsing code, Example:
244       // }VE7VFM-11>APW251,TCPIP,WE7U-14*::VE7VFM-9 :OK GOT EMAIL OK{058
245       xastir_snprintf(line2,
246                       sizeof(line2),
247                       "%s",
248                       line);
249 
250       if (debug_level & 1024)
251       {
252         fprintf(stderr," COMPLETE SENT PACKET: %s\n",line2);
253       }
254 
255       break;
256 
257     default:
258       // We shouldn't be here.
259       return(1);
260 
261       break;
262   }
263 
264 
265   // Create the string we're going to compare against and that we
266   // might store in the queue.  Knock off the path info and just check
267   // source/destination/info portions of the packet for a match.
268 
269   c1 = strstr(line2, ","); // Find comma after destination
270   c2 = strstr(line2, ":"); // Find end of path
271 
272   if ( (c1 != NULL) && (c2 != NULL) )             // Found both separators
273   {
274 
275     // Copy source/destination portion
276     xastir_snprintf(match_line,
277                     sizeof(match_line),
278                     "%s",
279                     line2);
280     match_line[(int)(c1-line2)] = '\0';         // Terminate the substring
281 
282     strncat(match_line,                         // Copy info portion
283             c2+1,
284             sizeof(match_line) - 1 - strlen(match_line));
285   }
286   else    // At least one separator was not found, copy entire string
287   {
288     xastir_snprintf(match_line,
289                     sizeof(match_line),
290                     "%s",
291                     line2);
292   }
293 
294 
295   // Run through the selected queue from beginning to end.  If the
296   // pointer is NULL, the queue is empty and we're already done.
297   while (ptr != NULL && !found_dupe)
298   {
299 
300     // Check the timestamp to determine whether to delete this
301     // record
302     if (ptr->time < time_cutoff)    // Old record, delete it
303     {
304       DupeRecord *temp;
305 
306       if (debug_level & 1024)
307       {
308         switch (queue_type)
309         {
310           case HEARD:
311             fprintf(stderr,"HEARD Deleting record: %s\n",ptr->data);
312             break;
313           case SENT:
314             fprintf(stderr," SENT Deleting record: %s\n",ptr->data);
315             break;
316           default:
317             break;
318         }
319       }
320 
321       // Delete record and free up the space
322       temp = ptr;
323       ptr = ptr->next;    // May be NULL!
324       free(temp);
325 
326       // Point master queue pointer to new head of queue.
327       // Make sure to carry along ptr_last as well, as this is
328       // our possible insertion point later.
329       switch (queue_type)
330       {
331         case HEARD:
332           heard_queue[port] = ptr_last = ptr; // May be NULL!
333           break;
334         case SENT:
335           sent_queue[port] = ptr_last = ptr;  // May be NULL!
336         default:
337           break;
338       }
339     }
340 
341     else    // Record is current.  Check for a match.
342     {
343 
344       //fprintf(stderr,"\n\t\t%s\n\t\t%s\n",ptr->data,match_line);
345 
346       if (strcmp(ptr->data,match_line) == 0)
347       {
348         // We found a dupe!  We're done with the loop.
349         found_dupe++;
350 
351         if (debug_level & 1024)
352         {
353           switch (queue_type)
354           {
355             case HEARD:
356               fprintf(stderr,"HEARD*     Found dupe: %s\n",match_line);
357               break;
358             case SENT:
359               fprintf(stderr,"SENT*      Found dupe: %s\n",match_line);
360             default:
361               break;
362           }
363         }
364       }
365       else    // Not a dupe, skip to the next record in the
366       {
367         // queue.  Keep a pointer to the last record
368         // compared so that we have a possible insertion
369         // point later.  Once we hit the end (NULL), we
370         // can't back up one.
371         ptr_last = ptr;     // Save pointer to last record
372         ptr = ptr->next;    // Advance one.  May be NULL!
373       }
374     }
375   }   // End of while loop
376 
377 
378   if (found_dupe)
379   {
380 
381     if (debug_level & 1024)
382     {
383       switch (port_data[port].device_type)
384       {
385         case DEVICE_SERIAL_TNC_AUX_GPS:
386         case DEVICE_SERIAL_TNC_HSP_GPS:
387         case DEVICE_SERIAL_TNC:
388         case DEVICE_AX25_TNC:
389         case DEVICE_SERIAL_KISS_TNC:
390         case DEVICE_SERIAL_MKISS_TNC:
391         case DEVICE_NET_AGWPE:
392           fprintf(stderr,"        Found RF dupe: %s\n",match_line);
393           break;
394 
395         default:
396           fprintf(stderr,"       Found NET dupe: %s\n",match_line);
397           break;
398       }
399     }
400 
401     return(0);  // Found a dupe, return
402   }
403 
404   else
405   {
406 
407     // If insert_new == 1, insert each non-dupe record into the
408     // queue and give it a timestamp.  ptr_next is currently
409     // either NULL or points to the last record in the chain.
410     if (insert_new)
411     {
412       DupeRecord *temp;
413 
414       if (debug_level & 1024)
415       {
416         switch (queue_type)
417         {
418           case HEARD:
419             fprintf(stderr,"HEARD   Adding record: %s\n",match_line);
420             break;
421           case SENT:
422             fprintf(stderr," SENT   Adding record: %s\n",match_line);
423             break;
424           default:
425             break;
426         }
427       }
428 
429       // Allocate a new storage space for the record and fill
430       // it in.
431       temp = (DupeRecord *)malloc(sizeof(DupeRecord));
432 
433       if (!temp)
434       {
435         fprintf(stderr,"Couldn't allocate memory in not_a_dupe()\n");
436         return(1);  // Send back "not a dupe"
437       }
438 
439       temp->time = (time_t)sec_now();
440 
441       memcpy(temp->data, match_line, sizeof(temp->data));
442       temp->data[sizeof(temp->data)-1] = '\0';  // Terminate string
443 
444       temp->next = NULL;  // Will be the end of the linked list
445 
446       if (ptr_last == NULL)   // Queue is currently empty
447       {
448 
449         // Add record to empty list.  Point master queue pointer
450         // to new head of queue.
451         switch (queue_type)
452         {
453           case HEARD:
454             heard_queue[port] = temp;
455             break;
456           case SENT:
457             sent_queue[port] = temp;
458           default:
459             break;
460         }
461       }
462       else    // Queue is not empty, add the record to the end of
463       {
464         // the list.
465         ptr_last->next = temp;
466       }
467     }
468   }
469   return(1);  // Nope, not a dupe
470 }
471 
472 
473 
474 
475 
476 // Function which the receive routines call to insert a received
477 // packet into the HEARD queue for an interface.  The packet will
478 // get added to the end of the linked list if it's not a duplicate
479 // record.
480 //
481 // Check to make sure it's an RF interface, else return
482 //
insert_into_heard_queue(int port,char * line)483 void insert_into_heard_queue(int port, char *line)
484 {
485 
486   switch (port_data[port].device_type)
487   {
488 
489     case DEVICE_SERIAL_TNC_AUX_GPS:
490     case DEVICE_SERIAL_TNC_HSP_GPS:
491     case DEVICE_SERIAL_TNC:
492     case DEVICE_AX25_TNC:
493     case DEVICE_SERIAL_KISS_TNC:
494     case DEVICE_SERIAL_MKISS_TNC:
495     case DEVICE_NET_AGWPE:
496 
497       // We're not using the dupe check function, but merely the
498       // expiration and insert functions of not_a_dupe()
499 
500       // Don't insert the "Tickle" lines that keep the internet
501       // sockets alive
502       if ( (strncasecmp(line,"# Tickle",8) != 0)
503            && (strncasecmp(line,"#Tickle",7) != 0) )
504       {
505         (void)not_a_dupe(HEARD, port, line, FORCED_INSERT);
506       }
507 
508       break;
509 
510     default:    // Get out if not an RF interface
511       return;
512 
513       break;
514   }
515 }
516 
517 
518 
519 
520 
521 /****************************************************************/
522 /* output data to inet interfaces                               */
523 /* line: data to send out                                       */
524 /* port: port data came from                                    */
525 /****************************************************************/
output_igate_net(char * line,int port,int third_party)526 void output_igate_net(char *line, int port, int third_party)
527 {
528   char data_txt[MAX_LINE_SIZE+5];
529   char temp[MAX_LINE_SIZE+5];
530   char *call_sign;
531   char *path;
532   char *message;
533   int len,i,x,first;
534   int igate_options;
535   char log_file_path[MAX_VALUE];
536 
537   call_sign = NULL;
538   path      = NULL;
539   message   = NULL;
540   first     = 1;
541 
542   if (line == NULL)
543   {
544     return;
545   }
546 
547   if (line[0] == '\0')
548   {
549     return;
550   }
551 
552   // Don't igate packets read in from a log file (port -1).
553   // Packets from x_spider (port -2) are ok to igate.
554   if (port == -1)
555   {
556     return;
557   }
558 
559 //fprintf(stderr,"Igating: %s\n", line);
560 
561   // Should we Igate from RF->NET?
562   if (operate_as_an_igate <= 0)
563   {
564     return;
565   }
566 
567   xastir_snprintf(temp,
568                   sizeof(temp),
569                   "%s",
570                   line);
571 
572   // Check for null call_sign field
573   call_sign = strtok(temp,">");
574   if (call_sign == NULL)
575   {
576     return;
577   }
578 
579   // Check for null path field
580   path = strtok(NULL,":");
581   if (path == NULL)
582   {
583     return;
584   }
585 
586   get_user_base_dir(LOGFILE_IGATE,log_file_path, sizeof(log_file_path));
587   // Check for "TCPIP" or "TCPXX" in the path.  If found, don't
588   // gate this into the internet again, it's already been gated to
589   // RF, which means it's already been on the 'net.  No looping
590   // allowed here...
591   //
592   // We also now support NOGATE and RFONLY options.  If these are
593   // seen in the path, do _not_ gate those packets into the
594   // internet.
595   //
596   // Don't gate OpenTrac expanded packets to the 'net.
597   //
598   if ( (strstr(path,"TCPXX") != NULL)
599        || (strstr(path,"TCPIP") != NULL && port >= 0) // x_spider ok
600        || (strstr(path,"NOGATE") != NULL)
601        || (strstr(path,"RFONLY") != NULL)
602        || (strstr(path,"OPNTRK") != NULL)      // OpenTrac Packet
603        || (strstr(path,"OPNTRC") != NULL) )    // OpenTrac Packet
604   {
605 
606     if (log_igate && (debug_level & 1024) )
607     {
608 
609       xastir_snprintf(temp,
610                       sizeof(temp),
611                       "IGATE RF->NET(%c):%s\n",
612                       third_party ? 'T':'N',
613                       line);
614       log_data( log_file_path, temp );
615 
616       xastir_snprintf(temp,
617                       sizeof(temp),
618                       "REJECT: Packet was gated before or shouldn't be gated!\n");
619       log_data( log_file_path, temp );
620 
621       fprintf(stderr, "%s", temp);
622     }
623     return;
624   }
625 
626   // Check for null message field
627   message = strtok(NULL,"");
628   if (message == NULL)
629   {
630     return;
631   }
632 
633   // Check for third party messages.  We don't want to gate these
634   // back onto the internet feeds
635 // Note that the REPLY-ACK algorithm also uses the '}' symbol.
636   if (message[0] == '}')
637   {
638 
639     if (log_igate && (debug_level & 1024) )
640     {
641 
642       xastir_snprintf(temp,
643                       sizeof(temp),
644                       "IGATE RF->NET(%c):%s\n",
645                       third_party ? 'T':'N',
646                       line);
647       log_data( log_file_path, temp );
648 
649       xastir_snprintf(temp,
650                       sizeof(temp),
651                       "REJECT: Third party traffic!\n");
652       log_data( log_file_path, temp );
653 
654       fprintf(stderr, "%s", temp);
655     }
656     return;
657   }
658 
659   // Check for "general" queries.  We don't wish to gate these in
660   // either direction.  There are exactly three general query
661   // types defined in the spec.
662   //
663   if ( (   strstr(message,"?APRS?" ) != NULL)
664        || (strstr(message,"?IGATE?") != NULL)
665        || (strstr(message,"?WX?"   ) != NULL) )
666   {
667 
668     // We found a general query, don't gate it.
669 
670     if (log_igate && (debug_level & 1024) )
671     {
672 
673       xastir_snprintf(temp,
674                       sizeof(temp),
675                       "IGATE RF->NET(%c):%s\n",
676                       third_party ? 'T':'N',
677                       line);
678       log_data( log_file_path, temp );
679 
680       xastir_snprintf(temp,
681                       sizeof(temp),
682                       "REJECT: General Query!\n");
683       log_data( log_file_path, temp );
684 
685       fprintf(stderr, "%s", temp);
686     }
687     return;
688   }
689 
690   len = (int)strlen(call_sign);
691   for (i=0; i<len; i++)
692   {
693 
694     // just in case we see an asterisk get rid of it
695     if (call_sign[i] == '*')
696     {
697       call_sign[i] = '\0';
698       i = len+1;
699     }
700   }
701 
702   // Check for my callsign
703   if (strcmp(call_sign,my_callsign) == 0)
704   {
705 
706     if (log_igate && (debug_level & 1024) )
707     {
708 
709       xastir_snprintf(temp,
710                       sizeof(temp),
711                       "IGATE RF->NET(%c):%s\n",
712                       third_party ? 'T':'N',
713                       line);
714       log_data( log_file_path, temp );
715 
716       xastir_snprintf(temp,
717                       sizeof(temp),
718                       "REJECT: From my call!\n");
719       log_data( log_file_path, temp );
720 
721       fprintf(stderr, "%s", temp);
722     }
723     return;
724   }
725 
726   // Should I filter out more here.. get rid of all data
727   // or Look in the path for things line "AP" "GPS" "ID" etc..?
728 
729   begin_critical_section(&devices_lock, "igate.c:output_igate_net" );
730 
731   // If received from x_spider port or it's our own tactical call.
732   // Here are the special port numbers we might see:
733   // -1: We're reading in from a log file
734   // -2: Packet came from x_spider server port (therefore it's
735   //     already authenticated)
736   // -3: We're reading in tactical calls from file
737   //
738   if (port == -1)         // Packet came from a log file.
739   {
740     igate_options = 0;  // Don't igate it.
741   }
742 
743   else if (port == -2)    // Packet came from x_spider server port
744   {
745     igate_options = 1;  // Ok to igate.
746   }
747 
748   else if (port == -3)    // We're reading tactical call from file.
749   {
750     igate_options = 0;  // Don't igate it.
751   }
752 
753   else if (port < -2)     // Errant port number.
754   {
755     igate_options = 0;  // Don't igate it.
756   }
757 
758   else    // Port number is 0 or positive number.  A real port.
759     // Decide whether to igate it based on the port's
760     // configuration.
761   {
762     igate_options = devices[port].igate_options;
763   }
764 
765 
766   end_critical_section(&devices_lock, "igate.c:output_igate_net" );
767 
768   if (igate_options <= 0 )
769   {
770 
771     if (log_igate && (debug_level & 1024) )
772     {
773 
774       xastir_snprintf(temp,
775                       sizeof(temp),
776                       "IGATE RF->NET(%c):%s\n",
777                       third_party ? 'T':'N',
778                       line);
779       log_data( log_file_path, temp );
780 
781       xastir_snprintf(temp,
782                       sizeof(temp),
783                       "REJECT: No RF->NET from input port [%d]!\n",
784                       port);
785       log_data( log_file_path, temp );
786 
787       fprintf(stderr, "%s", temp);
788     }
789     return;
790   }
791 
792   xastir_snprintf(data_txt,
793                   sizeof(data_txt),
794                   "%s%c%c",
795                   line,
796                   '\r',
797                   '\n');
798 
799   // write data out to net interfaces
800   for (x = 0; x < MAX_IFACE_DEVICES; x++)
801   {
802 
803     // Find all internet interfaces that are "up"
804     if (port_data[x].device_type == DEVICE_NET_STREAM
805         && x!=port && port_data[x].status == DEVICE_UP)
806     {
807       int pcode;
808 
809       // Check whether we have a valid callsign/password
810       // combination for this interface.  If not, don't gate
811       // packets to it.
812       pcode = atoi(port_data[x].device_host_pswd);
813       if (checkHash(my_callsign, pcode))
814       {
815         // The pcode checks out.  Allow sending the
816         // packet out to the internet.
817 
818         // log traffic for the first "up" interface only
819         if (log_igate && first)
820         {
821           xastir_snprintf(temp,
822                           sizeof(temp),
823                           "IGATE RF->NET(%c):%s\n",
824                           third_party ? 'T':'N',
825                           line);
826           log_data( log_file_path, temp );
827 
828           first = 0;
829         }
830 
831         // Now log the interface that each bit of traffic
832         // goes out on.
833         xastir_snprintf(temp,
834                         sizeof(temp),
835                         "TRANSMIT: IGate RF->NET packet on device:%d\n",
836                         x);
837 
838         // log output
839         if (log_igate)
840         {
841           log_data( log_file_path, temp );
842         }
843 
844         if (debug_level & 1024)
845         {
846           fprintf(stderr,"%s\n",temp);
847         }
848 
849         // Write this data out to the Inet port The "1"
850         // means raw format, the last digit says to _not_
851         // use the unproto_igate path
852         output_my_data(data_txt,x,1,0,0,NULL);
853 //fprintf(stderr,"Sending: %s\n", data_txt);
854       }
855     }
856   }
857 }
858 
859 
860 
861 
862 
863 /****************************************************************/
864 /* output data to tnc interfaces                                */
865 /* from: type of port heard from (No! It's the source call!)    */
866 /* call: call sign heard from (No! It's the destination call!)  */
867 /* line: data to gate to rf                                     */
868 /* port: port data came from                                    */
869 /****************************************************************/
output_igate_rf(char * from,char * call,char * path,char * line,int port,int third_party,char * object_name)870 void output_igate_rf(char *from, char *call, char *path, char *line,
871                      int port, int third_party, char *object_name)
872 {
873 
874   char temp[MAX_LINE_SIZE+20];
875   int x;
876   int first = 1;
877   int found_in_nws_file = 0;
878   char log_file_path[MAX_VALUE];
879   char nws_file_path[MAX_VALUE];
880 
881 
882   if ( (from == NULL) || (call == NULL) || (path == NULL) || (line == NULL) )
883   {
884     return;
885   }
886 
887   if ( (from[0] == '\0') || (call[0] == '\0') || (path[0] == '\0') || (line[0] == '\0') )
888   {
889     return;
890   }
891 
892   // Should we Igate from NET->RF?
893   if (operate_as_an_igate <= 1)
894   {
895     return;
896   }
897 
898 
899   get_user_base_dir(LOGFILE_IGATE,log_file_path, sizeof(log_file_path));
900 
901   // Don't gate anything with NOGATE in it, in either direction.
902   // Same for OpenTrac packets.
903   if ( (strstr(path,"NOGATE") != NULL)
904        || (strstr(path,"OPNTRK") != NULL)     // OpenTrac Packet
905        || (strstr(path,"OPNTRC") != NULL) )   // OpenTrac Packet
906   {
907     // "NOGATE" was found in the header.  Don't gate it.
908     if (log_igate && (debug_level & 1024) )
909     {
910       xastir_snprintf(temp,
911                       sizeof(temp),
912                       "IGATE NET->RF(%c):%s\n",
913                       third_party ? 'T':'N',
914                       line);
915       log_data( log_file_path, temp );
916 
917       xastir_snprintf(temp,
918                       sizeof(temp),
919                       "REJECT: NOGATE found in path or shouldn't be gated!\n");
920       log_data( log_file_path, temp );
921       fprintf(stderr, "%s", temp);
922     }
923     return;
924   }
925 
926   // Don't gate "general" queries in any direction.  There are
927   // exactly three general query types defined in the spec.
928   //
929   if (   (strstr(line,"?APRS?" ) != NULL)
930          || (strstr(line,"?IGATE?") != NULL)
931          || (strstr(line,"?WX?"   ) != NULL) )
932   {
933 
934     // We found a general query, don't gate it.
935 
936     if (log_igate && (debug_level & 1024) )
937     {
938       xastir_snprintf(temp,
939                       sizeof(temp),
940                       "IGATE NET->RF(%c):%s\n",
941                       third_party ? 'T':'N',
942                       line);
943       log_data( log_file_path, temp );
944 
945       xastir_snprintf(temp,
946                       sizeof(temp),
947                       "REJECT: General Query!\n");
948       log_data( log_file_path, temp );
949       fprintf(stderr, "%s", temp);
950     }
951     return;
952   }
953 
954 
955   // check to see if the nws-stations file is newer than last read
956   get_user_base_dir("data/nws-stations.txt",nws_file_path,
957                     sizeof(nws_file_path));
958   if (last_nws_stations_file_time < file_time(nws_file_path))
959   {
960     last_nws_stations_file_time = file_time(nws_file_path);
961     load_NWS_stations(nws_file_path);
962     //fprintf(stderr,"NWS Station file time is old\n");
963   }
964 
965 
966   // Check whether gating of packets from this station/object/item
967   // has been specifically authorized via the nws-stations.txt
968   // mechanism.
969   //
970   if (object_name)    // It's an object or item name
971   {
972 
973     if ( check_NWS_stations( object_name ) || group_active(object_name))
974     {
975 
976       found_in_nws_file++; // Object/Item is in nws-stations.txt
977     }
978   }
979   else                // It's a station callsign
980   {
981 
982     if ( check_NWS_stations( from ) || group_active(from))
983     {
984 
985       found_in_nws_file++; // Source callsign is in nws-stations.txt
986     }
987   }
988 // The above is really the same as the following code, but less
989 // confusing:
990 //    if (check_NWS_stations( (object_name) ? object_name : from ) ) {
991 //        found_in_nws_file++;
992 //    }
993 
994 
995   // Check for TCPXX in string only if station wasn't found in the
996   // nws-stations.txt file.  If TCPXX found, we have an
997   // unregistered net user and the packet shouldn't normally head
998   // to RF.
999   //
1000   // I removed the trailing asterisk -we7u
1001   //
1002   // Note that we CAN now gate stations to RF that have TCPXX in
1003   // the string if they are authorized via the nws-stations.txt
1004   // mechanism.
1005   //
1006   if (!found_in_nws_file)   // Skip this check if they're always authorized via the file
1007   {
1008 
1009     if (strstr(path,"TCPXX") != NULL)
1010     {
1011 
1012       // "TCPXX" was found in the header.  We have an
1013       // unregistered user.
1014 
1015       if (log_igate && (debug_level & 1024) )
1016       {
1017         xastir_snprintf(temp,
1018                         sizeof(temp),
1019                         "IGATE NET->RF(%c):%s\n",
1020                         third_party ? 'T':'N',
1021                         line);
1022         log_data( log_file_path, temp );
1023 
1024         xastir_snprintf(temp,
1025                         sizeof(temp),
1026                         "REJECT: Unregistered net user!\n");
1027         log_data( log_file_path, temp );
1028         fprintf(stderr, "%s", temp);
1029       }
1030       return;
1031     }
1032   }
1033 
1034 
1035 
1036 // If we made it to this point, the packet is from an authorized net
1037 // user (no TCPXX found in the path), or the callsign has been
1038 // authorized via the nws-stations.txt file (whether or not TCPXX
1039 // was found in the path).
1040 //
1041 // Found in file: Gate always
1042 //
1043 // Not found:     Gate if TCPXX not in path -AND- if destination
1044 //                station was heard in last hour on RF -AND- if
1045 //                source station was NOT heard in last hour on RF.
1046 
1047 
1048 
1049   // Check whether the source and destination calls have been
1050   // heard on local RF.
1051   if ( !found_in_nws_file // Skip this check if they're always authorized via the file
1052        && ( !heard_via_tnc_in_past_hour(call)    // Haven't heard destination call in previous hour
1053             || heard_via_tnc_in_past_hour(from)) )    // Have heard source call in previous hour
1054   {
1055 
1056     if (log_igate && (debug_level & 1024) )
1057     {
1058       xastir_snprintf(temp,
1059                       sizeof(temp),
1060                       "IGATE NET->RF(%c):%s\n",
1061                       third_party ? 'T':'N',
1062                       line);
1063       log_data( log_file_path, temp );
1064 
1065       //  heard(call),  heard(from) : RF-to-RF talk
1066       // !heard(call),  heard(from) : Destination not heard on TNC
1067       // !heard(call), !heard(from) : Destination/source not heard on TNC
1068 
1069       if (!heard_via_tnc_in_past_hour(call))
1070         xastir_snprintf(temp,
1071                         sizeof(temp),
1072                         "REJECT: Destination not heard on TNC within an hour %s!\n",
1073                         call );
1074       else
1075         xastir_snprintf(temp,
1076                         sizeof(temp),
1077                         "REJECT: RF->RF talk!\n");
1078       log_data( log_file_path, temp );
1079       fprintf(stderr, "%s", temp);
1080     }
1081     return;
1082   }
1083 
1084 
1085 
1086   // Station we are going to is heard via tnc but station sending
1087   // shouldn't be heard via TNC.  Write data out to interfaces.
1088   for (x=0; x<MAX_IFACE_DEVICES; x++)
1089   {
1090 
1091     //fprintf(stderr,"%d\n",x);
1092 
1093 //WE7U
1094     // Check here against "heard" queue for each interface.
1095     // Drop the packet on the floor if it was already received
1096     // on this interface within the last XX seconds.  This means
1097     // that some other igate beat us to the punch.  The receive
1098     // routines have to fill this queue.  Prune outdated
1099     // records.
1100     //
1101     // Also check here against "sent" queue for each interface.
1102     // Drop the packet on the floor if already sent within the
1103     // last XX seconds.  Add this packet to the queue if it
1104     // isn't already in it.  Prune outdated records.
1105     if (x != port
1106         && not_a_dupe(HEARD, x, line, NO_FORCED_INSERT)
1107         && not_a_dupe( SENT, x, line, NO_FORCED_INSERT) )
1108     {
1109 
1110       //fprintf(stderr,"output_igate_rf: Not a dupe port %d, transmitting\n",x);
1111 
1112       switch (port_data[x].device_type)
1113       {
1114 
1115         case DEVICE_SERIAL_TNC_AUX_GPS:
1116         case DEVICE_SERIAL_TNC_HSP_GPS:
1117         case DEVICE_SERIAL_TNC:
1118         case DEVICE_AX25_TNC:
1119         case DEVICE_SERIAL_KISS_TNC:
1120         case DEVICE_SERIAL_MKISS_TNC:
1121         case DEVICE_NET_AGWPE:
1122 
1123           begin_critical_section(&devices_lock, "igate.c:output_igate_rf" );
1124 
1125           if (devices[x].igate_options>1 && port_data[x].status==DEVICE_UP)
1126           {
1127 
1128             // log traffic for first "up" interface only
1129             if (log_igate && first)
1130             {
1131               xastir_snprintf(temp,
1132                               sizeof(temp),
1133                               "IGATE NET->RF(%c):%s\n",
1134                               third_party ? 'T':'N',
1135                               line);
1136               log_data( log_file_path, temp );
1137               first = 0;
1138             }
1139 
1140             xastir_snprintf(temp,
1141                             sizeof(temp),
1142                             "TRANSMIT: IGate NET->RF packet on device:%d\n",
1143                             x);
1144 
1145             // log output
1146             if (log_igate)
1147             {
1148               log_data( log_file_path, temp );
1149             }
1150 
1151             if (debug_level & 1024)
1152             {
1153               fprintf(stderr, "%s", temp);
1154             }
1155 
1156             // ok write this data out to the RF port
1157 
1158             end_critical_section(&devices_lock, "igate.c:output_igate_rf" );
1159 
1160             // First "0" means "cooked"
1161             // format, last digit: use
1162             // unproto_igate path
1163             output_my_data(line,x,0,0,1,NULL);
1164 
1165 //fprintf(stderr, "Igating->RF: %s\n", line);
1166 
1167             begin_critical_section(&devices_lock, "igate.c:output_igate_rf" );
1168 
1169           }
1170           else
1171           {
1172             if (log_igate && (debug_level & 1024) )
1173             {
1174               xastir_snprintf(temp,
1175                               sizeof(temp),
1176                               "IGATE NET->RF(%c):%s\n",
1177                               third_party ? 'T':'N',
1178                               line);
1179               log_data( log_file_path, temp );
1180 
1181               xastir_snprintf(temp,
1182                               sizeof(temp),
1183                               "REJECT: NET->RF on port [%d]!\n",
1184                               x);
1185               log_data( log_file_path, temp );
1186               fprintf(stderr, "%s", temp);
1187             }
1188           }
1189 
1190           end_critical_section(&devices_lock, "igate.c:output_igate_rf" );
1191 
1192           break;
1193 
1194         default:
1195           break;
1196       }   // End of switch
1197     }   // End of if
1198   }   // End of for
1199 }
1200 
1201 
1202 
1203 
1204 
add_NWS_stations(void)1205 void add_NWS_stations(void)
1206 {
1207   void *tmp_ptr;
1208 
1209   if (NWS_stations>=max_NWS_stations)
1210   {
1211     if ((tmp_ptr = realloc(NWS_station_data, sizeof(NWS_Data)*(max_NWS_stations+11))))
1212     {
1213       NWS_station_data = tmp_ptr;
1214       max_NWS_stations += 10;
1215     }
1216     else
1217     {
1218       fprintf(stderr,"Unable to allocate more space for NWS_station_data\n");
1219     }
1220   }
1221 }
1222 
1223 
1224 
1225 
1226 
1227 /****************************************************************/
1228 /* Load NWS stations file                                       */
1229 /* file: file to read                                           */
1230 /****************************************************************/
load_NWS_stations(char * file)1231 void load_NWS_stations(char *file)
1232 {
1233   FILE *f;
1234   char line[40];
1235 
1236   if (file == NULL)
1237   {
1238     return;
1239   }
1240 
1241   if (file[0] == '\0')
1242   {
1243     return;
1244   }
1245 
1246   if (NWS_station_data)
1247   {
1248     free(NWS_station_data);
1249     NWS_station_data = NULL;
1250   }
1251 
1252   NWS_stations = 0;
1253   max_NWS_stations = 0;
1254 
1255   f = fopen(file,"r");
1256   if (f!=NULL)
1257   {
1258     while (!feof(f))
1259     {
1260       if (strlen(get_line(f,line,40))>0)
1261       {
1262         // look for comment
1263         if (line[0] != '#' )
1264         {
1265           NWS_stations++;
1266           add_NWS_stations();
1267           if (NWS_station_data != NULL)
1268           {
1269             // add data
1270             // Note:  Size of string variable is 12
1271             // bytes, defined in igate.h
1272             if (1 != sscanf(line,"%11s",NWS_station_data[NWS_stations-1].call))
1273             {
1274               fprintf(stderr,"load_NWS_stations: sscanf parsing error\n");
1275             }
1276             if (debug_level & 1024)
1277             {
1278               fprintf(stderr,"LINE:%s\n",line);
1279             }
1280           }
1281           else
1282           {
1283             fprintf(stderr,"Can't allocate data space for NWS station\n");
1284           }
1285         }
1286       }
1287     }
1288     (void)fclose(f);
1289   }
1290   else
1291   {
1292     fprintf(stderr,"Couldn't open NWS stations file: %s\n", file);
1293   }
1294 }
1295 
1296 
1297 
1298 
1299 
1300 // check NWS stations file
1301 //
1302 // call: call to check
1303 // returns 1 for found
1304 //
1305 // Both the incoming call and the stored call we're matching against
1306 // have to be >= 3 characters long.  This routine will match only up
1307 // to the length of the stored string, so we now allow partial
1308 // matches.
1309 //
check_NWS_stations(char * call)1310 int check_NWS_stations(char *call)
1311 {
1312   int ok, i, length, length_incoming;
1313 
1314 
1315   if (call == NULL)
1316   {
1317     return(0);
1318   }
1319 
1320   if (call[0] == '\0')
1321   {
1322     return(0);
1323   }
1324 
1325   if (NWS_station_data == NULL)
1326   {
1327     return(0);
1328   }
1329 
1330   if (debug_level & 1024)
1331   {
1332     fprintf(stderr,"igate.c::check_NWS_stations %s\n", call);
1333   }
1334 
1335   // Make sure that the incoming call is longer than three
1336   // characters.  If not, skip it.
1337   length_incoming = strlen(call);
1338   if (length_incoming < 3)
1339   {
1340     return(0);
1341   }
1342 
1343   ok=0;
1344   for (i=0; i<NWS_stations && !ok; i++)
1345   {
1346 
1347     // Compute length of stored string.  If it's shorter than
1348     // three characters, skip it and go on to the next one.
1349     length = strlen(NWS_station_data[i].call);
1350 
1351     if (length >= 3)
1352     {
1353       // Compare the incoming call only up to the length of the
1354       // stored call.  This allows partial matches.  The
1355       // stored call could be significantly shorter than the
1356       // incoming call, but at least three characters.
1357       if (strncasecmp(call, NWS_station_data[i].call, length)==0)
1358       {
1359 
1360         ok=1; // match found
1361         if (debug_level & 1024)
1362         {
1363           fprintf(stderr,"NWS-MATCH:(%s) (%s)\n",NWS_station_data[i].call,call);
1364         }
1365       }
1366     }
1367     else
1368     {
1369       // Do nothing.  Stored call is too short.
1370     }
1371   }
1372   return(ok);
1373 }
1374 
1375 
1376 
1377 
1378 
1379 /****************************************************************/
1380 /* output NWS data to tnc interfaces                            */
1381 /* from: type of port heard from                                */
1382 /* call: call sign heard from                                   */
1383 /* line: data to gate to rf                                     */
1384 /* port: port data came from                                    */
1385 /****************************************************************/
output_nws_igate_rf(char * from,char * path,char * line,int port,int third_party)1386 void output_nws_igate_rf(char *from, char *path, char *line, int port, int third_party)
1387 {
1388   char temp[MAX_LINE_SIZE+20];
1389   int x;
1390   int first = 1;
1391   char log_file_path[MAX_VALUE];
1392   char nws_file_path[MAX_VALUE];
1393 
1394 
1395   if ( (from == NULL) || (path == NULL) || (line == NULL) )
1396   {
1397     return;
1398   }
1399 
1400   if ( (from[0] == '\0') || (path[0] == '\0') || (line[0] == '\0') )
1401   {
1402     return;
1403   }
1404 
1405   // Should we Igate from NET->RF?
1406   if (operate_as_an_igate <= 1)
1407   {
1408     return;
1409   }
1410 
1411   get_user_base_dir(LOGFILE_IGATE,log_file_path, sizeof(log_file_path));
1412   get_user_base_dir("data/nws-stations.txt",nws_file_path,
1413                     sizeof(nws_file_path));
1414 
1415   // Check for TCPXX in string!  If found, we have an
1416   // unregistered net user.
1417   // I removed the trailing asterisk --we7u
1418   if (strstr(path,"TCPXX") != NULL)
1419   {
1420     // "TCPXX" was found in the header.  We have an
1421     // unregistered user.
1422     if (log_igate && (debug_level & 1024) )
1423     {
1424       xastir_snprintf(temp,
1425                       sizeof(temp),
1426                       "NWS IGATE NET->RF(%c):%s\n",
1427                       third_party ? 'T':'N',
1428                       line);
1429       log_data( log_file_path, temp );
1430 
1431       xastir_snprintf(temp,
1432                       sizeof(temp),
1433                       "REJECT: Unregistered net user!\n");
1434       log_data( log_file_path, temp );
1435       fprintf(stderr, "%s", temp);
1436     }
1437     return;
1438   }
1439 
1440   // no unregistered net user found in string.  Look for NOGATE
1441   // next.
1442 
1443   if ( strstr(path,"NOGATE") != NULL )
1444   {
1445     // "NOGATE" was found in the header.  Don't gate it.
1446     if (log_igate && (debug_level & 1024) )
1447     {
1448       xastir_snprintf(temp,
1449                       sizeof(temp),
1450                       "NWS IGATE NET->RF(%c):%s\n",
1451                       third_party ? 'T':'N',
1452                       line);
1453       log_data( log_file_path, temp );
1454 
1455       xastir_snprintf(temp,
1456                       sizeof(temp),
1457                       "REJECT: NOGATE found in path!\n");
1458       log_data( log_file_path, temp );
1459       fprintf(stderr, "%s", temp);
1460     }
1461     return;
1462   }
1463 
1464   // see if we can gate NWS messages
1465   if (!filethere(nws_file_path))
1466   {
1467     if (log_igate && (debug_level & 1024) )
1468     {
1469       xastir_snprintf(temp,
1470                       sizeof(temp),
1471                       "NWS IGATE NET->RF(%c):%s\n",
1472                       third_party ? 'T':'N',
1473                       line);
1474       log_data( log_file_path, temp );
1475 
1476       xastir_snprintf(temp,
1477                       sizeof(temp),
1478                       "REJECT: No nws-stations.txt file!\n");
1479       log_data( log_file_path, temp );
1480       fprintf(stderr, "%s", temp);
1481     }
1482     return;
1483   }
1484 
1485   // check to see if the nws-stations file is newer than last read
1486   if (last_nws_stations_file_time < file_time(nws_file_path))
1487   {
1488     last_nws_stations_file_time = file_time(nws_file_path);
1489     load_NWS_stations(nws_file_path);
1490     //fprintf(stderr,"NWS Station file time is old\n");
1491   }
1492 
1493   // Look for NWS station in file data
1494   if (!check_NWS_stations(from) || !group_active(from))  // Couldn't find the station
1495   {
1496 
1497     if (log_igate && (debug_level & 1024) )
1498     {
1499       xastir_snprintf(temp,
1500                       sizeof(temp),
1501                       "NWS IGATE NET->RF(%c):%s\n",
1502                       third_party ? 'T':'N',
1503                       line);
1504       log_data( log_file_path, temp );
1505 
1506       xastir_snprintf(temp,
1507                       sizeof(temp),
1508                       "REJECT: No matching station in nws-stations.txt file!\n");
1509       log_data( log_file_path, temp );
1510       fprintf(stderr, "%s", temp);
1511     }
1512     return; // Match for station not found in file
1513   }
1514 
1515   //fprintf(stderr,"SENDING NWS VIA TNC!!!!\n");
1516   // write data out to interfaces
1517   for (x=0; x<MAX_IFACE_DEVICES; x++)
1518   {
1519 
1520 //WE7U
1521     // Check here against "heard" queue for each interface.
1522     // Drop the packet on the floor if it was already received
1523     // on this interface within the last XX seconds.  This means
1524     // that some other igate beat us to the punch.  The receive
1525     // routines have to fill this queue.  Prune outdated
1526     // records.
1527     //
1528     // Also check here against "sent" queue for each interface.
1529     // Drop the packet on the floor if already sent within the
1530     // last XX seconds.  Add this packet to the queue if it
1531     // isn't already in it.  Prune outdated records.
1532     if (x != port
1533         && not_a_dupe(HEARD, x, line, NO_FORCED_INSERT)
1534         && not_a_dupe( SENT, x, line, NO_FORCED_INSERT) )
1535     {
1536 
1537       switch (port_data[x].device_type)
1538       {
1539 
1540         case DEVICE_SERIAL_TNC_AUX_GPS:
1541         case DEVICE_SERIAL_TNC_HSP_GPS:
1542         case DEVICE_SERIAL_TNC:
1543         case DEVICE_AX25_TNC:
1544         case DEVICE_SERIAL_KISS_TNC:
1545         case DEVICE_SERIAL_MKISS_TNC:
1546         case DEVICE_NET_AGWPE:
1547 
1548           begin_critical_section(&devices_lock, "igate.c:output_nws_igate_rf" );
1549 
1550           if (devices[x].igate_options>1
1551               && port_data[x].status==DEVICE_UP)
1552           {
1553 
1554             // log traffic for first "up" interface only
1555             if (log_igate && first)
1556             {
1557               xastir_snprintf(temp,
1558                               sizeof(temp),
1559                               "NWS IGATE NET->RF(%c):%s\n",
1560                               third_party ? 'T':'N',
1561                               line);
1562               log_data( log_file_path, temp );
1563               first = 0;
1564             }
1565 
1566             xastir_snprintf(temp,
1567                             sizeof(temp),
1568                             "TRANSMIT: IGate NET->RF packet on device:%d\n",
1569                             x);
1570 
1571             // log output
1572             if (log_igate)
1573             {
1574               log_data( log_file_path, temp );
1575             }
1576 
1577             if (debug_level & 1024)
1578             {
1579               fprintf(stderr, "%s", temp);
1580             }
1581 
1582             // ok write this data out to the RF port
1583 
1584             end_critical_section(&devices_lock, "igate.c:output_nws_igate_rf" );
1585 
1586             // First "0" means "cooked"
1587             // format, last digit: use
1588             // unproto_igate path
1589             output_my_data(line,x,0,0,1,NULL);
1590 
1591             begin_critical_section(&devices_lock, "igate.c:output_nws_igate_rf" );
1592 
1593           }
1594           else
1595           {
1596             if (log_igate && (debug_level & 1024) )
1597             {
1598               xastir_snprintf(temp,
1599                               sizeof(temp),
1600                               "NWS IGATE NET->RF(%c):%s\n",
1601                               third_party ? 'T':'N',
1602                               line);
1603               log_data( log_file_path, temp );
1604 
1605               xastir_snprintf(temp,
1606                               sizeof(temp),
1607                               "REJECT: NET->RF on port [%d]!\n",
1608                               x);
1609               log_data( log_file_path, temp );
1610               fprintf(stderr, "%s", temp);
1611             }
1612           }
1613 
1614           end_critical_section(&devices_lock, "igate.c:output_nws_igate_rf" );
1615 
1616           break;
1617 
1618         default:
1619           break;
1620       }
1621     }
1622   }
1623 }
1624 
1625 
1626