]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libalias/alias_db.c
Revert the default behaviour for incoming connections so
[FreeBSD/FreeBSD.git] / lib / libalias / alias_db.c
1 /*  -*- mode: c; tab-width: 8; c-basic-indent: 4; -*-
2     Alias_db.c encapsulates all data structures used for storing
3     packet aliasing data.  Other parts of the aliasing software
4     access data through functions provided in this file.
5
6     Data storage is based on the notion of a "link", which is
7     established for ICMP echo/reply packets, UDP datagrams and
8     TCP stream connections.  A link stores the original source
9     and destination addresses.  For UDP and TCP, it also stores
10     source and destination port numbers, as well as an alias
11     port number.  Links are also used to store information about
12     fragments.
13
14     There is a facility for sweeping through and deleting old
15     links as new packets are sent through.  A simple timeout is
16     used for ICMP and UDP links.  TCP links are left alone unless
17     there is an incomplete connection, in which case the link
18     can be deleted after a certain amount of time.
19
20
21     This software is placed into the public domain with no restrictions
22     on its distribution.
23
24     Initial version: August, 1996  (cjm)
25
26     Version 1.4: September 16, 1996 (cjm)
27         Facility for handling incoming links added.
28
29     Version 1.6: September 18, 1996 (cjm)
30         ICMP data handling simplified.
31
32     Version 1.7: January 9, 1997 (cjm)
33         Fragment handling simplified.
34         Saves pointers for unresolved fragments.
35         Permits links for unspecified remote ports
36           or unspecified remote addresses.
37         Fixed bug which did not properly zero port
38           table entries after a link was deleted.
39         Cleaned up some obsolete comments.
40
41     Version 1.8: January 14, 1997 (cjm)
42         Fixed data type error in StartPoint().
43         (This error did not exist prior to v1.7
44         and was discovered and fixed by Ari Suutari)
45
46     Version 1.9: February 1, 1997
47         Optionally, connections initiated from packet aliasing host
48         machine will will not have their port number aliased unless it
49         conflicts with an aliasing port already being used. (cjm)
50
51         All options earlier being #ifdef'ed are now available through
52         a new interface, SetPacketAliasMode().  This allows run time
53         control (which is now available in PPP+pktAlias through the
54         'alias' keyword). (ee)
55
56         Added ability to create an alias port without
57         either destination address or port specified.
58         port type = ALIAS_PORT_UNKNOWN_DEST_ALL (ee)
59
60         Removed K&R style function headers
61         and general cleanup. (ee)
62
63         Added packetAliasMode to replace compiler #defines's (ee)
64
65         Allocates sockets for partially specified
66         ports if ALIAS_USE_SOCKETS defined. (cjm)
67
68     Version 2.0: March, 1997
69         SetAliasAddress() will now clean up alias links
70         if the aliasing address is changed. (cjm)
71
72         PacketAliasPermanentLink() function added to support permanent
73         links.  (J. Fortes suggested the need for this.)
74         Examples:
75
76         (192.168.0.1, port 23)  <-> alias port 6002, unknown dest addr/port
77
78         (192.168.0.2, port 21)  <-> alias port 3604, known dest addr
79                                                      unknown dest port
80
81         These permanent links allow for incoming connections to
82         machines on the local network.  They can be given with a
83         user-chosen amount of specificity, with increasing specificity
84         meaning more security. (cjm)
85
86         Quite a bit of rework to the basic engine.  The portTable[]
87         array, which kept track of which ports were in use was replaced
88         by a table/linked list structure. (cjm)
89
90         SetExpire() function added. (cjm)
91
92         DeleteLink() no longer frees memory association with a pointer
93         to a fragment (this bug was first recognized by E. Eklund in
94         v1.9).
95
96     Version 2.1: May, 1997 (cjm)
97         Packet aliasing engine reworked so that it can handle
98         multiple external addresses rather than just a single
99         host address.
100
101         PacketAliasRedirectPort() and PacketAliasRedirectAddr()
102         added to the API.  The first function is a more generalized
103         version of PacketAliasPermanentLink().  The second function
104         implements static network address translation.
105
106     See HISTORY file for additional revisions.
107
108     $FreeBSD$
109 */
110
111
112 /* System include files */
113 #include <errno.h>
114 #include <stdlib.h>
115 #include <stdio.h>
116 #include <unistd.h>
117
118 #include <sys/socket.h>
119 #include <sys/time.h>
120 #include <sys/types.h>
121
122 /* BSD network include files */
123 #include <netinet/in_systm.h>
124 #include <netinet/in.h>
125 #include <netinet/ip.h>
126 #include <netinet/tcp.h>
127 #include <arpa/inet.h>
128
129 #include "alias.h"
130 #include "alias_local.h"
131
132
133
134 /*
135    Constants (note: constants are also defined
136               near relevant functions or structs)
137 */
138
139 /* Sizes of input and output link tables */
140 #define LINK_TABLE_OUT_SIZE         101
141 #define LINK_TABLE_IN_SIZE         4001
142
143 /* Parameters used for cleanup of expired links */
144 #define ALIAS_CLEANUP_INTERVAL_SECS  60
145 #define ALIAS_CLEANUP_MAX_SPOKES     30
146
147 /* Timeouts (in seconds) for different link types */
148 #define ICMP_EXPIRE_TIME             60
149 #define UDP_EXPIRE_TIME              60
150 #define PROTO_EXPIRE_TIME            60
151 #define FRAGMENT_ID_EXPIRE_TIME      10
152 #define FRAGMENT_PTR_EXPIRE_TIME     30
153
154 /* TCP link expire time for different cases */
155 /* When the link has been used and closed - minimal grace time to
156    allow ACKs and potential re-connect in FTP (XXX - is this allowed?)  */
157 #ifndef TCP_EXPIRE_DEAD
158 #   define TCP_EXPIRE_DEAD           10
159 #endif
160
161 /* When the link has been used and closed on one side - the other side
162    is allowed to still send data */
163 #ifndef TCP_EXPIRE_SINGLEDEAD
164 #   define TCP_EXPIRE_SINGLEDEAD     90
165 #endif
166
167 /* When the link isn't yet up */
168 #ifndef TCP_EXPIRE_INITIAL
169 #   define TCP_EXPIRE_INITIAL       300
170 #endif
171
172 /* When the link is up */
173 #ifndef TCP_EXPIRE_CONNECTED
174 #   define TCP_EXPIRE_CONNECTED   86400
175 #endif
176
177
178 /* Dummy port number codes used for FindLinkIn/Out() and AddLink().
179    These constants can be anything except zero, which indicates an
180    unknown port number. */
181
182 #define NO_DEST_PORT     1
183 #define NO_SRC_PORT      1
184
185
186
187 /* Data Structures
188
189     The fundamental data structure used in this program is
190     "struct alias_link".  Whenever a TCP connection is made,
191     a UDP datagram is sent out, or an ICMP echo request is made,
192     a link record is made (if it has not already been created).
193     The link record is identified by the source address/port
194     and the destination address/port. In the case of an ICMP
195     echo request, the source port is treated as being equivalent
196     with the 16-bit ID number of the ICMP packet.
197
198     The link record also can store some auxiliary data.  For
199     TCP connections that have had sequence and acknowledgment
200     modifications, data space is available to track these changes.
201     A state field is used to keep track in changes to the TCP
202     connection state.  ID numbers of fragments can also be
203     stored in the auxiliary space.  Pointers to unresolved
204     fragments can also be stored.
205
206     The link records support two independent chainings.  Lookup
207     tables for input and out tables hold the initial pointers
208     the link chains.  On input, the lookup table indexes on alias
209     port and link type.  On output, the lookup table indexes on
210     source address, destination address, source port, destination
211     port and link type.
212 */
213
214 struct ack_data_record     /* used to save changes to ACK/sequence numbers */
215 {
216     u_long ack_old;
217     u_long ack_new;
218     int delta;
219     int active;
220 };
221
222 struct tcp_state           /* Information about TCP connection        */
223 {
224     int in;                /* State for outside -> inside             */
225     int out;               /* State for inside  -> outside            */
226     int index;             /* Index to ACK data array                 */
227     int ack_modified;      /* Indicates whether ACK and sequence numbers */
228                            /* been modified                           */
229 };
230
231 #define N_LINK_TCP_DATA   3 /* Number of distinct ACK number changes
232                                saved for a modified TCP stream */
233 struct tcp_dat
234 {
235     struct tcp_state state;
236     struct ack_data_record ack[N_LINK_TCP_DATA];
237     int fwhole;             /* Which firewall record is used for this hole? */
238 };
239
240 struct server              /* LSNAT server pool (circular list) */
241 {
242     struct in_addr addr;
243     u_short port;
244     struct server *next;
245 };
246
247 struct alias_link                /* Main data structure */
248 {
249     struct in_addr src_addr;     /* Address and port information        */
250     struct in_addr dst_addr;
251     struct in_addr alias_addr;
252     struct in_addr proxy_addr;
253     u_short src_port;
254     u_short dst_port;
255     u_short alias_port;
256     u_short proxy_port;
257     struct server *server;
258
259     int link_type;               /* Type of link: TCP, UDP, ICMP, proto, frag */
260
261 /* values for link_type */
262 #define LINK_ICMP                     IPPROTO_ICMP
263 #define LINK_UDP                      IPPROTO_UDP
264 #define LINK_TCP                      IPPROTO_TCP
265 #define LINK_FRAGMENT_ID              (IPPROTO_MAX + 1)
266 #define LINK_FRAGMENT_PTR             (IPPROTO_MAX + 2)
267 #define LINK_ADDR                     (IPPROTO_MAX + 3)
268
269     int flags;                   /* indicates special characteristics   */
270
271 /* flag bits */
272 #define LINK_UNKNOWN_DEST_PORT     0x01
273 #define LINK_UNKNOWN_DEST_ADDR     0x02
274 #define LINK_PERMANENT             0x04
275 #define LINK_PARTIALLY_SPECIFIED   0x03 /* logical-or of first two bits */
276 #define LINK_UNFIREWALLED          0x08
277
278     int timestamp;               /* Time link was last accessed         */
279     int expire_time;             /* Expire time for link                */
280
281     int sockfd;                  /* socket descriptor                   */
282
283     u_int start_point_out;       /* Index number in output lookup table */
284     u_int start_point_in;
285     struct alias_link *next_out; /* Linked list pointers for input and  */
286     struct alias_link *last_out; /* output tables                       */
287     struct alias_link *next_in;  /*  .                                  */
288     struct alias_link *last_in;  /*  .                                  */
289
290     union                        /* Auxiliary data                      */
291     {
292         char *frag_ptr;
293         struct in_addr frag_addr;
294         struct tcp_dat *tcp;
295     } data;
296 };
297
298
299
300
301
302 /* Global Variables 
303
304     The global variables listed here are only accessed from
305     within alias_db.c and so are prefixed with the static 
306     designation.
307 */
308
309 int packetAliasMode;                 /* Mode flags                      */ 
310                                      /*        - documented in alias.h  */
311
312 static struct in_addr aliasAddress;  /* Address written onto source     */
313                                      /*   field of IP packet.           */
314
315 static struct in_addr targetAddress; /* IP address incoming packets     */
316                                      /*   are sent to if no aliasing    */
317                                      /*   link already exists           */
318
319 static struct in_addr nullAddress;   /* Used as a dummy parameter for   */
320                                      /*   some function calls           */
321 static struct alias_link *
322 linkTableOut[LINK_TABLE_OUT_SIZE];   /* Lookup table of pointers to     */
323                                      /*   chains of link records. Each  */
324 static struct alias_link *           /*   link record is doubly indexed */
325 linkTableIn[LINK_TABLE_IN_SIZE];     /*   into input and output lookup  */
326                                      /*   tables.                       */
327
328 static int icmpLinkCount;            /* Link statistics                 */
329 static int udpLinkCount;
330 static int tcpLinkCount;
331 static int protoLinkCount;
332 static int fragmentIdLinkCount;
333 static int fragmentPtrLinkCount;
334 static int sockCount;
335
336 static int cleanupIndex;             /* Index to chain of link table    */
337                                      /* being inspected for old links   */
338
339 static int timeStamp;                /* System time in seconds for      */
340                                      /* current packet                  */
341
342 static int lastCleanupTime;          /* Last time IncrementalCleanup()  */
343                                      /* was called                      */
344
345 static int houseKeepingResidual;     /* used by HouseKeeping()          */
346
347 static int deleteAllLinks;           /* If equal to zero, DeleteLink()  */
348                                      /* will not remove permanent links */
349
350 static FILE *monitorFile;            /* File descriptor for link        */
351                                      /* statistics monitoring file      */
352
353 static int newDefaultLink;           /* Indicates if a new aliasing     */
354                                      /* link has been created after a   */
355                                      /* call to PacketAliasIn/Out().    */
356              
357 #ifndef NO_FW_PUNCH
358 static int fireWallFD = -1;          /* File descriptor to be able to   */
359                                      /* control firewall.  Opened by    */
360                                      /* PacketAliasSetMode on first     */
361                                      /* setting the PKT_ALIAS_PUNCH_FW  */
362                                      /* flag.                           */
363 #endif
364
365
366
367
368
369
370
371 /* Internal utility routines (used only in alias_db.c)
372
373 Lookup table starting points:
374     StartPointIn()           -- link table initial search point for
375                                 incoming packets
376     StartPointOut()          -- link table initial search point for
377                                 outgoing packets
378     
379 Miscellaneous:
380     SeqDiff()                -- difference between two TCP sequences
381     ShowAliasStats()         -- send alias statistics to a monitor file
382 */
383
384
385 /* Local prototypes */
386 static u_int StartPointIn(struct in_addr, u_short, int);
387
388 static u_int StartPointOut(struct in_addr, struct in_addr,
389                            u_short, u_short, int);
390
391 static int SeqDiff(u_long, u_long);
392
393 static void ShowAliasStats(void);
394
395 #ifndef NO_FW_PUNCH
396 /* Firewall control */
397 static void InitPunchFW(void);
398 static void UninitPunchFW(void);
399 static void ClearFWHole(struct alias_link *link);
400 #endif
401
402 /* Log file control */
403 static void InitPacketAliasLog(void);
404 static void UninitPacketAliasLog(void);
405
406 static u_int
407 StartPointIn(struct in_addr alias_addr,
408              u_short alias_port,
409              int link_type)
410 {
411     u_int n;
412
413     n  = alias_addr.s_addr;
414     n += alias_port;
415     n += link_type;
416     return(n % LINK_TABLE_IN_SIZE);
417 }
418
419
420 static u_int
421 StartPointOut(struct in_addr src_addr, struct in_addr dst_addr,
422               u_short src_port, u_short dst_port, int link_type)
423 {
424     u_int n;
425
426     n  = src_addr.s_addr;
427     n += dst_addr.s_addr;
428     n += src_port; 
429     n += dst_port;
430     n += link_type;
431
432     return(n % LINK_TABLE_OUT_SIZE);
433 }
434
435
436 static int
437 SeqDiff(u_long x, u_long y)
438 {
439 /* Return the difference between two TCP sequence numbers */
440
441 /*
442     This function is encapsulated in case there are any unusual
443     arithmetic conditions that need to be considered.
444 */
445
446     return (ntohl(y) - ntohl(x));
447 }
448
449
450 static void
451 ShowAliasStats(void)
452 {
453 /* Used for debugging */
454
455    if (monitorFile)
456    {
457       fprintf(monitorFile, "icmp=%d, udp=%d, tcp=%d, proto=%d, frag_id=%d frag_ptr=%d",
458               icmpLinkCount,
459               udpLinkCount,
460               tcpLinkCount,
461               protoLinkCount,
462               fragmentIdLinkCount,
463               fragmentPtrLinkCount);
464
465       fprintf(monitorFile, " / tot=%d  (sock=%d)\n",
466               icmpLinkCount + udpLinkCount
467                             + tcpLinkCount
468                             + protoLinkCount
469                             + fragmentIdLinkCount
470                             + fragmentPtrLinkCount,
471               sockCount);
472
473       fflush(monitorFile);
474    }
475 }
476
477
478
479
480
481 /* Internal routines for finding, deleting and adding links
482
483 Port Allocation:
484     GetNewPort()             -- find and reserve new alias port number
485     GetSocket()              -- try to allocate a socket for a given port
486
487 Link creation and deletion:
488     CleanupAliasData()      - remove all link chains from lookup table
489     IncrementalCleanup()    - look for stale links in a single chain
490     DeleteLink()            - remove link
491     AddLink()               - add link 
492     ReLink()                - change link 
493
494 Link search:
495     FindLinkOut()           - find link for outgoing packets
496     FindLinkIn()            - find link for incoming packets
497 */
498
499 /* Local prototypes */
500 static int GetNewPort(struct alias_link *, int);
501
502 static u_short GetSocket(u_short, int *, int);
503
504 static void CleanupAliasData(void);
505
506 static void IncrementalCleanup(void);
507
508 static void DeleteLink(struct alias_link *);
509
510 static struct alias_link *
511 AddLink(struct in_addr, struct in_addr, struct in_addr,
512         u_short, u_short, int, int);
513
514 static struct alias_link *
515 ReLink(struct alias_link *,
516        struct in_addr, struct in_addr, struct in_addr,
517         u_short, u_short, int, int);
518
519 static struct alias_link *
520 FindLinkOut(struct in_addr, struct in_addr, u_short, u_short, int, int);
521
522 static struct alias_link *
523 FindLinkIn(struct in_addr, struct in_addr, u_short, u_short, int, int);
524
525
526 #define ALIAS_PORT_BASE            0x08000
527 #define ALIAS_PORT_MASK            0x07fff
528 #define GET_NEW_PORT_MAX_ATTEMPTS       20
529
530 #define GET_ALIAS_PORT                  -1
531 #define GET_ALIAS_ID        GET_ALIAS_PORT
532
533 /* GetNewPort() allocates port numbers.  Note that if a port number
534    is already in use, that does not mean that it cannot be used by
535    another link concurrently.  This is because GetNewPort() looks for
536    unused triplets: (dest addr, dest port, alias port). */
537
538 static int
539 GetNewPort(struct alias_link *link, int alias_port_param)
540 {
541     int i;
542     int max_trials;
543     u_short port_sys;
544     u_short port_net;
545
546 /*
547    Description of alias_port_param for GetNewPort().  When
548    this parameter is zero or positive, it precisely specifies
549    the port number.  GetNewPort() will return this number
550    without check that it is in use.
551
552    When this parameter is -1, it indicates to get a randomly
553    selected port number.
554 */
555  
556     if (alias_port_param == GET_ALIAS_PORT)
557     {
558         /*
559          * The aliasing port is automatically selected
560          * by one of two methods below:
561          */
562         max_trials = GET_NEW_PORT_MAX_ATTEMPTS;
563
564         if (packetAliasMode & PKT_ALIAS_SAME_PORTS)
565         {
566             /*
567              * When the PKT_ALIAS_SAME_PORTS option is
568              * chosen, the first try will be the
569              * actual source port. If this is already
570              * in use, the remainder of the trials
571              * will be random.
572              */
573             port_net = link->src_port;
574             port_sys = ntohs(port_net);
575         }
576         else
577         {
578             /* First trial and all subsequent are random. */
579             port_sys = random() & ALIAS_PORT_MASK;
580             port_sys += ALIAS_PORT_BASE;
581             port_net = htons(port_sys);
582         }
583     }
584     else if (alias_port_param >= 0 && alias_port_param < 0x10000)
585     {
586         link->alias_port = (u_short) alias_port_param;
587         return(0);
588     }
589     else
590     {
591 #ifdef DEBUG
592         fprintf(stderr, "PacketAlias/GetNewPort(): ");
593         fprintf(stderr, "input parameter error\n");
594 #endif
595         return(-1);
596     }
597
598
599 /* Port number search */
600     for (i=0; i<max_trials; i++)
601     {
602         int go_ahead;
603         struct alias_link *search_result;
604
605         search_result = FindLinkIn(link->dst_addr, link->alias_addr,
606                                    link->dst_port, port_net,
607                                    link->link_type, 0);
608
609         if (search_result == NULL)
610             go_ahead = 1;
611         else if (!(link->flags          & LINK_PARTIALLY_SPECIFIED)
612                && (search_result->flags & LINK_PARTIALLY_SPECIFIED))
613             go_ahead = 1;
614         else
615             go_ahead = 0;
616
617         if (go_ahead)
618         {
619             if ((packetAliasMode & PKT_ALIAS_USE_SOCKETS)
620              && (link->flags & LINK_PARTIALLY_SPECIFIED))
621             {
622                 if (GetSocket(port_net, &link->sockfd, link->link_type))
623                 {
624                     link->alias_port = port_net;
625                     return(0);
626                 }
627             }
628             else
629             {
630                 link->alias_port = port_net;
631                 return(0);
632             }
633         }
634
635         port_sys = random() & ALIAS_PORT_MASK;
636         port_sys += ALIAS_PORT_BASE;
637         port_net = htons(port_sys);
638     }
639
640 #ifdef DEBUG
641     fprintf(stderr, "PacketAlias/GetnewPort(): ");
642     fprintf(stderr, "could not find free port\n");
643 #endif
644
645     return(-1);
646 }
647
648
649 static u_short 
650 GetSocket(u_short port_net, int *sockfd, int link_type)
651 {
652     int err;
653     int sock;
654     struct sockaddr_in sock_addr;
655
656     if (link_type == LINK_TCP)
657         sock = socket(AF_INET, SOCK_STREAM, 0);
658     else if (link_type == LINK_UDP)
659         sock = socket(AF_INET, SOCK_DGRAM, 0);
660     else
661     {
662 #ifdef DEBUG
663         fprintf(stderr, "PacketAlias/GetSocket(): ");
664         fprintf(stderr, "incorrect link type\n");
665 #endif
666         return(0);
667     }
668
669     if (sock < 0)
670     {
671 #ifdef DEBUG
672         fprintf(stderr, "PacketAlias/GetSocket(): ");
673         fprintf(stderr, "socket() error %d\n", *sockfd);
674 #endif
675         return(0);
676     }
677
678     sock_addr.sin_family = AF_INET;
679     sock_addr.sin_addr.s_addr = htonl(INADDR_ANY);
680     sock_addr.sin_port = port_net;
681
682     err = bind(sock,
683                (struct sockaddr *) &sock_addr,
684                sizeof(sock_addr));
685     if (err == 0)
686     {
687         sockCount++;
688         *sockfd = sock;
689         return(1);
690     }
691     else
692     {
693         close(sock);
694         return(0);
695     }
696 }
697
698
699 static void
700 CleanupAliasData(void)
701 {
702     struct alias_link *link;
703     int i, icount;
704
705     icount = 0;
706     for (i=0; i<LINK_TABLE_OUT_SIZE; i++)
707     {
708         link = linkTableOut[i];
709         while (link != NULL)
710         {
711             struct alias_link *link_next;
712             link_next = link->next_out;
713             icount++;
714             DeleteLink(link);
715             link = link_next;
716         }
717     }
718
719     cleanupIndex =0;
720 }
721
722
723 static void
724 IncrementalCleanup(void)
725 {
726     int icount;
727     struct alias_link *link;
728
729     icount = 0;
730     link = linkTableOut[cleanupIndex++];
731     while (link != NULL)
732     {
733         int idelta;
734         struct alias_link *link_next;
735
736         link_next = link->next_out; 
737         idelta = timeStamp - link->timestamp;
738         switch (link->link_type)
739         {
740             case LINK_TCP:
741                 if (idelta > link->expire_time)
742                 {
743                     struct tcp_dat *tcp_aux;
744
745                     tcp_aux = link->data.tcp; 
746                     if (tcp_aux->state.in  != ALIAS_TCP_STATE_CONNECTED
747                      || tcp_aux->state.out != ALIAS_TCP_STATE_CONNECTED)
748                     {
749                         DeleteLink(link);
750                         icount++;
751                     }
752                 }
753                 break;
754             default:
755                 if (idelta > link->expire_time)
756                 {
757                     DeleteLink(link);
758                     icount++;
759                 }
760                 break;
761         }
762         link = link_next;
763     }
764
765     if (cleanupIndex == LINK_TABLE_OUT_SIZE)
766         cleanupIndex = 0;
767 }
768
769 void
770 DeleteLink(struct alias_link *link)
771 {
772     struct alias_link *link_last;
773     struct alias_link *link_next;
774
775 /* Don't do anything if the link is marked permanent */
776     if (deleteAllLinks == 0 && link->flags & LINK_PERMANENT)
777         return;
778
779 #ifndef NO_FW_PUNCH
780 /* Delete associated firewall hole, if any */
781     ClearFWHole(link);
782 #endif
783
784 /* Free memory allocated for LSNAT server pool */
785     if (link->server != NULL) {
786         struct server *head, *curr, *next;
787
788         head = curr = link->server;
789         do {
790             next = curr->next;
791             free(curr);
792         } while ((curr = next) != head);
793     }
794
795 /* Adjust output table pointers */
796     link_last = link->last_out;
797     link_next = link->next_out;
798
799     if (link_last != NULL)
800         link_last->next_out = link_next;
801     else
802         linkTableOut[link->start_point_out] = link_next;
803
804     if (link_next != NULL)
805         link_next->last_out = link_last;
806
807 /* Adjust input table pointers */
808     link_last = link->last_in;
809     link_next = link->next_in;
810
811     if (link_last != NULL)
812         link_last->next_in = link_next;
813     else
814         linkTableIn[link->start_point_in] = link_next;
815
816     if (link_next != NULL)
817         link_next->last_in = link_last;
818
819 /* Close socket, if one has been allocated */
820     if (link->sockfd != -1)
821     {
822         sockCount--;
823         close(link->sockfd);
824     }
825
826 /* Link-type dependent cleanup */
827     switch(link->link_type)
828     {
829         case LINK_ICMP:
830             icmpLinkCount--;
831             break;
832         case LINK_UDP:
833             udpLinkCount--;
834             break;
835         case LINK_TCP:
836             tcpLinkCount--;
837             if (link->data.tcp != NULL)
838                 free(link->data.tcp);
839             break;
840         case LINK_FRAGMENT_ID:
841             fragmentIdLinkCount--;
842             break;
843         case LINK_FRAGMENT_PTR:
844             fragmentPtrLinkCount--;
845             if (link->data.frag_ptr != NULL)
846                 free(link->data.frag_ptr);
847             break;
848         case LINK_ADDR:
849             break;
850         default:
851             protoLinkCount--;
852             break;
853     }
854
855 /* Free memory */
856     free(link);
857
858 /* Write statistics, if logging enabled */
859     if (packetAliasMode & PKT_ALIAS_LOG)
860     {
861         ShowAliasStats();
862     }
863 }
864
865
866 static struct alias_link *
867 AddLink(struct in_addr  src_addr,
868         struct in_addr  dst_addr,
869         struct in_addr  alias_addr,
870         u_short         src_port,
871         u_short         dst_port,
872         int             alias_port_param,  /* if less than zero, alias   */
873         int             link_type)         /* port will be automatically */
874 {                                          /* chosen. If greater than    */
875     u_int start_point;                     /* zero, equal to alias port  */
876     struct alias_link *link;
877     struct alias_link *first_link;
878
879     link = malloc(sizeof(struct alias_link));
880     if (link != NULL)
881     {
882     /* Basic initialization */
883         link->src_addr          = src_addr;
884         link->dst_addr          = dst_addr;
885         link->alias_addr        = alias_addr;
886         link->proxy_addr.s_addr = INADDR_ANY;
887         link->src_port          = src_port;
888         link->dst_port          = dst_port;
889         link->proxy_port        = 0;
890         link->server            = NULL;
891         link->link_type         = link_type;
892         link->sockfd            = -1;
893         link->flags             = 0;
894         link->timestamp         = timeStamp;
895
896     /* Expiration time */
897         switch (link_type)
898         {
899         case LINK_ICMP:
900             link->expire_time = ICMP_EXPIRE_TIME;
901             break;
902         case LINK_UDP:
903             link->expire_time = UDP_EXPIRE_TIME;
904             break;
905         case LINK_TCP:
906             link->expire_time = TCP_EXPIRE_INITIAL;
907             break;
908         case LINK_FRAGMENT_ID:
909             link->expire_time = FRAGMENT_ID_EXPIRE_TIME;
910             break;
911         case LINK_FRAGMENT_PTR:
912             link->expire_time = FRAGMENT_PTR_EXPIRE_TIME;
913             break;
914         case LINK_ADDR:
915             break;
916         default:
917             link->expire_time = PROTO_EXPIRE_TIME;
918             break;
919         }
920
921     /* Determine alias flags */
922         if (dst_addr.s_addr == INADDR_ANY)
923             link->flags |= LINK_UNKNOWN_DEST_ADDR;
924         if (dst_port == 0)
925             link->flags |= LINK_UNKNOWN_DEST_PORT;
926
927     /* Determine alias port */
928         if (GetNewPort(link, alias_port_param) != 0)
929         {
930             free(link);
931             return(NULL);
932         }
933
934     /* Set up pointers for output lookup table */
935         start_point = StartPointOut(src_addr, dst_addr, 
936                                     src_port, dst_port, link_type);
937         first_link = linkTableOut[start_point];
938
939         link->last_out        = NULL;
940         link->next_out        = first_link;
941         link->start_point_out = start_point;
942
943         if (first_link != NULL)
944             first_link->last_out = link;
945
946         linkTableOut[start_point] = link;
947
948     /* Set up pointers for input lookup table */
949         start_point = StartPointIn(alias_addr, link->alias_port, link_type); 
950         first_link = linkTableIn[start_point];
951
952         link->last_in        = NULL;
953         link->next_in        = first_link;
954         link->start_point_in = start_point;
955
956         if (first_link != NULL)
957             first_link->last_in = link;
958
959         linkTableIn[start_point] = link;
960
961     /* Link-type dependent initialization */
962         switch(link_type)
963         {
964             struct tcp_dat  *aux_tcp;
965
966             case LINK_ICMP:
967                 icmpLinkCount++;
968                 break;
969             case LINK_UDP:
970                 udpLinkCount++;
971                 break;
972             case LINK_TCP:
973                 aux_tcp = malloc(sizeof(struct tcp_dat));
974                 link->data.tcp = aux_tcp;
975                 if (aux_tcp != NULL)
976                 {
977                     int i;
978
979                     tcpLinkCount++;
980                     aux_tcp->state.in = ALIAS_TCP_STATE_NOT_CONNECTED;
981                     aux_tcp->state.out = ALIAS_TCP_STATE_NOT_CONNECTED;
982                     aux_tcp->state.index = 0;
983                     aux_tcp->state.ack_modified = 0;
984                     for (i=0; i<N_LINK_TCP_DATA; i++)
985                         aux_tcp->ack[i].active = 0;
986                     aux_tcp->fwhole = -1;
987                 }
988                 else
989                 {
990 #ifdef DEBUG
991                     fprintf(stderr, "PacketAlias/AddLink: ");
992                     fprintf(stderr, " cannot allocate auxiliary TCP data\n");
993 #endif
994                 }
995                 break;
996             case LINK_FRAGMENT_ID:
997                 fragmentIdLinkCount++;
998                 break;
999             case LINK_FRAGMENT_PTR:
1000                 fragmentPtrLinkCount++;
1001                 break;
1002             case LINK_ADDR:
1003                 break;
1004             default:
1005                 protoLinkCount++;
1006                 break;
1007         }
1008     }
1009     else
1010     {
1011 #ifdef DEBUG
1012         fprintf(stderr, "PacketAlias/AddLink(): ");
1013         fprintf(stderr, "malloc() call failed.\n");
1014 #endif
1015     }
1016
1017     if (packetAliasMode & PKT_ALIAS_LOG)
1018     {
1019         ShowAliasStats();
1020     }
1021
1022     return(link);
1023 }
1024
1025 static struct alias_link *
1026 ReLink(struct alias_link *old_link,
1027        struct in_addr  src_addr,
1028        struct in_addr  dst_addr,
1029        struct in_addr  alias_addr,
1030        u_short         src_port,
1031        u_short         dst_port,
1032        int             alias_port_param,   /* if less than zero, alias   */
1033        int             link_type)          /* port will be automatically */
1034 {                                          /* chosen. If greater than    */
1035     struct alias_link *new_link;           /* zero, equal to alias port  */
1036
1037     new_link = AddLink(src_addr, dst_addr, alias_addr,
1038                        src_port, dst_port, alias_port_param,
1039                        link_type);
1040 #ifndef NO_FW_PUNCH
1041     if (new_link != NULL &&
1042         old_link->link_type == LINK_TCP &&
1043         old_link->data.tcp &&
1044         old_link->data.tcp->fwhole > 0) {
1045       PunchFWHole(new_link);
1046     }
1047 #endif
1048     DeleteLink(old_link);
1049     return new_link;
1050 }
1051
1052 static struct alias_link *
1053 _FindLinkOut(struct in_addr src_addr,
1054             struct in_addr dst_addr,
1055             u_short src_port,
1056             u_short dst_port,
1057             int link_type,
1058             int replace_partial_links)
1059 {
1060     u_int i;
1061     struct alias_link *link;
1062
1063     i = StartPointOut(src_addr, dst_addr, src_port, dst_port, link_type);
1064     link = linkTableOut[i];
1065     while (link != NULL)
1066     {
1067         if (link->src_addr.s_addr == src_addr.s_addr
1068          && link->server          == NULL
1069          && link->dst_addr.s_addr == dst_addr.s_addr
1070          && link->dst_port        == dst_port
1071          && link->src_port        == src_port
1072          && link->link_type       == link_type)
1073         {
1074             link->timestamp = timeStamp;
1075             break;
1076         }
1077         link = link->next_out;
1078     }
1079
1080 /* Search for partially specified links. */
1081     if (link == NULL && replace_partial_links)
1082     {
1083         if (dst_port != 0 && dst_addr.s_addr != INADDR_ANY)
1084         {
1085             link = _FindLinkOut(src_addr, dst_addr, src_port, 0,
1086                                 link_type, 0);
1087             if (link == NULL)
1088                 link = _FindLinkOut(src_addr, nullAddress, src_port,
1089                                     dst_port, link_type, 0);
1090         }
1091         if (link == NULL &&
1092            (dst_port != 0 || dst_addr.s_addr != INADDR_ANY))
1093         {
1094             link = _FindLinkOut(src_addr, nullAddress, src_port, 0,
1095                                 link_type, 0);
1096         }
1097         if (link != NULL)
1098         {
1099             link = ReLink(link,
1100                           src_addr, dst_addr, link->alias_addr,
1101                           src_port, dst_port, link->alias_port,
1102                           link_type);
1103         }
1104     }
1105
1106     return(link);
1107 }
1108
1109 static struct alias_link *
1110 FindLinkOut(struct in_addr src_addr,
1111             struct in_addr dst_addr,
1112             u_short src_port,
1113             u_short dst_port,
1114             int link_type,
1115             int replace_partial_links)
1116 {
1117     struct alias_link *link;
1118
1119     link = _FindLinkOut(src_addr, dst_addr, src_port, dst_port,
1120                         link_type, replace_partial_links);
1121
1122     if (link == NULL)
1123     {
1124     /* The following allows permanent links to be
1125        specified as using the default source address
1126        (i.e. device interface address) without knowing
1127        in advance what that address is. */
1128         if (aliasAddress.s_addr != 0 &&
1129             src_addr.s_addr == aliasAddress.s_addr)
1130         {
1131             link = _FindLinkOut(nullAddress, dst_addr, src_port, dst_port,
1132                                link_type, replace_partial_links);
1133         }
1134     }
1135
1136     return(link);
1137 }
1138
1139
1140 static struct alias_link *
1141 _FindLinkIn(struct in_addr dst_addr,
1142            struct in_addr  alias_addr,
1143            u_short         dst_port,
1144            u_short         alias_port,
1145            int             link_type,
1146            int             replace_partial_links)
1147 {
1148     int flags_in;
1149     u_int start_point;
1150     struct alias_link *link;
1151     struct alias_link *link_fully_specified;
1152     struct alias_link *link_unknown_all;
1153     struct alias_link *link_unknown_dst_addr;
1154     struct alias_link *link_unknown_dst_port;
1155
1156 /* Initialize pointers */
1157     link_fully_specified  = NULL;
1158     link_unknown_all      = NULL;
1159     link_unknown_dst_addr = NULL;
1160     link_unknown_dst_port = NULL;
1161
1162 /* If either the dest addr or port is unknown, the search
1163    loop will have to know about this. */
1164
1165     flags_in = 0;
1166     if (dst_addr.s_addr == INADDR_ANY)
1167         flags_in |= LINK_UNKNOWN_DEST_ADDR;
1168     if (dst_port == 0)
1169         flags_in |= LINK_UNKNOWN_DEST_PORT;
1170
1171 /* Search loop */
1172     start_point = StartPointIn(alias_addr, alias_port, link_type);
1173     link = linkTableIn[start_point];
1174     while (link != NULL)
1175     {
1176         int flags;
1177
1178         flags = flags_in | link->flags;
1179         if (!(flags & LINK_PARTIALLY_SPECIFIED))
1180         {
1181             if (link->alias_addr.s_addr == alias_addr.s_addr
1182              && link->alias_port        == alias_port 
1183              && link->dst_addr.s_addr   == dst_addr.s_addr
1184              && link->dst_port          == dst_port
1185              && link->link_type         == link_type)
1186             {
1187                 link_fully_specified = link;
1188                 break;
1189             }
1190         }
1191         else if ((flags & LINK_UNKNOWN_DEST_ADDR)
1192               && (flags & LINK_UNKNOWN_DEST_PORT))
1193         {
1194             if (link->alias_addr.s_addr == alias_addr.s_addr
1195              && link->alias_port        == alias_port
1196              && link->link_type         == link_type)
1197             {
1198                 if (link_unknown_all == NULL)
1199                     link_unknown_all = link;
1200             }
1201         }
1202         else if (flags & LINK_UNKNOWN_DEST_ADDR)
1203         {
1204             if (link->alias_addr.s_addr == alias_addr.s_addr
1205              && link->alias_port        == alias_port
1206              && link->link_type         == link_type
1207              && link->dst_port          == dst_port)
1208             {
1209                 if (link_unknown_dst_addr == NULL)
1210                     link_unknown_dst_addr = link;
1211             }
1212         }
1213         else if (flags & LINK_UNKNOWN_DEST_PORT)
1214         {
1215             if (link->alias_addr.s_addr == alias_addr.s_addr
1216              && link->alias_port        == alias_port
1217              && link->link_type         == link_type
1218              && link->dst_addr.s_addr   == dst_addr.s_addr)
1219             {
1220                 if (link_unknown_dst_port == NULL)
1221                     link_unknown_dst_port = link;
1222             }
1223         }
1224         link = link->next_in;
1225     }
1226
1227
1228
1229     if (link_fully_specified != NULL)
1230     {
1231         link_fully_specified->timestamp = timeStamp;
1232         link = link_fully_specified;
1233     }
1234     else if (link_unknown_dst_port != NULL)
1235         link = link_unknown_dst_port;
1236     else if (link_unknown_dst_addr != NULL)
1237         link = link_unknown_dst_addr;
1238     else if (link_unknown_all != NULL)
1239         link = link_unknown_all;
1240     else
1241         return (NULL);
1242
1243     if (replace_partial_links &&
1244         (link->flags & LINK_PARTIALLY_SPECIFIED || link->server != NULL))
1245     {
1246         struct in_addr src_addr;
1247         u_short src_port;
1248
1249         if (link->server != NULL) {             /* LSNAT link */
1250             src_addr = link->server->addr;
1251             src_port = link->server->port;
1252             link->server = link->server->next;
1253         } else {
1254             src_addr = link->src_addr;
1255             src_port = link->src_port;
1256         }
1257
1258         link = ReLink(link,
1259                       src_addr, dst_addr, alias_addr,
1260                       src_port, dst_port, alias_port,
1261                       link_type);
1262     }
1263
1264     return (link);
1265 }
1266
1267 static struct alias_link *
1268 FindLinkIn(struct in_addr dst_addr,
1269            struct in_addr alias_addr,
1270            u_short dst_port,
1271            u_short alias_port,
1272            int link_type,
1273            int replace_partial_links)
1274 {
1275     struct alias_link *link;
1276
1277     link = _FindLinkIn(dst_addr, alias_addr, dst_port, alias_port,
1278                        link_type, replace_partial_links);
1279
1280     if (link == NULL)
1281     {
1282     /* The following allows permanent links to be
1283        specified as using the default aliasing address
1284        (i.e. device interface address) without knowing
1285        in advance what that address is. */
1286         if (aliasAddress.s_addr != 0 &&
1287             alias_addr.s_addr == aliasAddress.s_addr)
1288         {
1289             link = _FindLinkIn(dst_addr, nullAddress, dst_port, alias_port,
1290                                link_type, replace_partial_links);
1291         }
1292     }
1293
1294     return(link);
1295 }
1296
1297
1298
1299
1300 /* External routines for finding/adding links
1301
1302 -- "external" means outside alias_db.c, but within alias*.c --
1303
1304     FindIcmpIn(), FindIcmpOut()
1305     FindFragmentIn1(), FindFragmentIn2()
1306     AddFragmentPtrLink(), FindFragmentPtr()
1307     FindProtoIn(), FindProtoOut()
1308     FindUdpTcpIn(), FindUdpTcpOut()
1309     FindOriginalAddress(), FindAliasAddress()
1310
1311 (prototypes in alias_local.h)
1312 */
1313
1314
1315 struct alias_link *
1316 FindIcmpIn(struct in_addr dst_addr,
1317            struct in_addr alias_addr,
1318            u_short id_alias)
1319 {
1320     return FindLinkIn(dst_addr, alias_addr,
1321                       NO_DEST_PORT, id_alias,
1322                       LINK_ICMP, 0);
1323 }
1324
1325
1326 struct alias_link *
1327 FindIcmpOut(struct in_addr src_addr,
1328             struct in_addr dst_addr,
1329             u_short id)
1330 {
1331     struct alias_link * link;
1332
1333     link = FindLinkOut(src_addr, dst_addr,
1334                        id, NO_DEST_PORT,
1335                        LINK_ICMP, 0);
1336     if (link == NULL)
1337     {
1338         struct in_addr alias_addr;
1339
1340         alias_addr = FindAliasAddress(src_addr);
1341         link = AddLink(src_addr, dst_addr, alias_addr,
1342                        id, NO_DEST_PORT, GET_ALIAS_ID,
1343                        LINK_ICMP);
1344     }
1345
1346     return(link);
1347 }
1348
1349
1350 struct alias_link *
1351 FindFragmentIn1(struct in_addr dst_addr,
1352                 struct in_addr alias_addr,
1353                 u_short ip_id)
1354 {
1355     struct alias_link *link;
1356
1357     link = FindLinkIn(dst_addr, alias_addr,
1358                       NO_DEST_PORT, ip_id,
1359                       LINK_FRAGMENT_ID, 0);
1360
1361     if (link == NULL)
1362     {
1363         link = AddLink(nullAddress, dst_addr, alias_addr,
1364                        NO_SRC_PORT, NO_DEST_PORT, ip_id,
1365                        LINK_FRAGMENT_ID);
1366     }
1367
1368     return(link);
1369 }
1370
1371
1372 struct alias_link *
1373 FindFragmentIn2(struct in_addr dst_addr,   /* Doesn't add a link if one */
1374                 struct in_addr alias_addr, /*   is not found.           */
1375                 u_short ip_id)
1376 {
1377     return FindLinkIn(dst_addr, alias_addr,
1378                       NO_DEST_PORT, ip_id,
1379                       LINK_FRAGMENT_ID, 0);
1380 }
1381
1382
1383 struct alias_link *
1384 AddFragmentPtrLink(struct in_addr dst_addr,
1385                    u_short ip_id)
1386 {
1387     return AddLink(nullAddress, dst_addr, nullAddress,
1388                    NO_SRC_PORT, NO_DEST_PORT, ip_id,
1389                    LINK_FRAGMENT_PTR);
1390 }
1391
1392
1393 struct alias_link *
1394 FindFragmentPtr(struct in_addr dst_addr,
1395                 u_short ip_id)
1396 {
1397     return FindLinkIn(dst_addr, nullAddress,
1398                       NO_DEST_PORT, ip_id,
1399                       LINK_FRAGMENT_PTR, 0);
1400 }
1401
1402
1403 struct alias_link *
1404 FindProtoIn(struct in_addr dst_addr,
1405             struct in_addr alias_addr,
1406             u_char proto)
1407 {
1408     struct alias_link *link;
1409
1410     link = FindLinkIn(dst_addr, alias_addr,
1411                       NO_DEST_PORT, 0,
1412                       proto, 1);
1413
1414     if (link == NULL && !(packetAliasMode & PKT_ALIAS_DENY_INCOMING))
1415     {
1416         struct in_addr target_addr;
1417
1418         target_addr = FindOriginalAddress(alias_addr);
1419         link = AddLink(target_addr, dst_addr, alias_addr,
1420                        NO_SRC_PORT, NO_DEST_PORT, 0,
1421                        proto);
1422     }
1423
1424     return (link);
1425 }
1426
1427
1428 struct alias_link *
1429 FindProtoOut(struct in_addr src_addr,
1430              struct in_addr dst_addr,
1431              u_char proto)
1432 {
1433     struct alias_link *link;
1434
1435     link = FindLinkOut(src_addr, dst_addr,
1436                        NO_SRC_PORT, NO_DEST_PORT,
1437                        proto, 1);
1438
1439     if (link == NULL)
1440     {
1441         struct in_addr alias_addr;
1442
1443         alias_addr = FindAliasAddress(src_addr);
1444         link = AddLink(src_addr, dst_addr, alias_addr,
1445                        NO_SRC_PORT, NO_DEST_PORT, 0,
1446                        proto);
1447     }
1448
1449     return (link);
1450 }
1451
1452
1453 struct alias_link *
1454 FindUdpTcpIn(struct in_addr dst_addr,
1455              struct in_addr alias_addr,
1456              u_short        dst_port,
1457              u_short        alias_port,
1458              u_char         proto)
1459 {
1460     int link_type;
1461     struct alias_link *link;
1462
1463     switch (proto)
1464     {
1465     case IPPROTO_UDP:
1466         link_type = LINK_UDP;
1467         break;
1468     case IPPROTO_TCP:
1469         link_type = LINK_TCP;
1470         break;
1471     default:
1472         return NULL;
1473         break;
1474     }
1475
1476     link = FindLinkIn(dst_addr, alias_addr,
1477                       dst_port, alias_port,
1478                       link_type, 1);
1479
1480     if (!(packetAliasMode & PKT_ALIAS_DENY_INCOMING)
1481      && !(packetAliasMode & PKT_ALIAS_PROXY_ONLY)
1482      && link == NULL)
1483     {
1484         struct in_addr target_addr;
1485
1486         target_addr = FindOriginalAddress(alias_addr);
1487         link = AddLink(target_addr, dst_addr, alias_addr,
1488                        alias_port, dst_port, alias_port,
1489                        link_type);
1490     }
1491
1492     return(link);
1493 }
1494
1495
1496 struct alias_link * 
1497 FindUdpTcpOut(struct in_addr  src_addr,
1498               struct in_addr  dst_addr,
1499               u_short         src_port,
1500               u_short         dst_port,
1501               u_char          proto)
1502 {
1503     int link_type;
1504     struct alias_link *link;
1505
1506     switch (proto)
1507     {
1508     case IPPROTO_UDP:
1509         link_type = LINK_UDP;
1510         break;
1511     case IPPROTO_TCP:
1512         link_type = LINK_TCP;
1513         break;
1514     default:
1515         return NULL;
1516         break;
1517     }
1518
1519     link = FindLinkOut(src_addr, dst_addr, src_port, dst_port, link_type, 1);
1520
1521     if (link == NULL)
1522     {
1523         struct in_addr alias_addr;
1524
1525         alias_addr = FindAliasAddress(src_addr);
1526         link = AddLink(src_addr, dst_addr, alias_addr,
1527                        src_port, dst_port, GET_ALIAS_PORT,
1528                        link_type);
1529     }
1530
1531     return(link);
1532 }
1533
1534
1535 struct in_addr
1536 FindOriginalAddress(struct in_addr alias_addr)
1537 {
1538     struct alias_link *link;
1539     
1540     link = FindLinkIn(nullAddress, alias_addr,
1541                       0, 0, LINK_ADDR, 0);
1542     if (link == NULL)
1543     {
1544         newDefaultLink = 1;
1545         if (targetAddress.s_addr == INADDR_ANY)
1546             return alias_addr;
1547         else if (targetAddress.s_addr == INADDR_NONE)
1548             return aliasAddress;
1549         else
1550             return targetAddress;
1551     }
1552     else
1553     {
1554         if (link->server != NULL) {             /* LSNAT link */
1555             struct in_addr src_addr;
1556
1557             src_addr = link->server->addr;
1558             link->server = link->server->next;
1559             return (src_addr);
1560         } else if (link->src_addr.s_addr == INADDR_ANY)
1561             return aliasAddress;
1562         else
1563             return link->src_addr;
1564     }
1565 }
1566
1567
1568 struct in_addr
1569 FindAliasAddress(struct in_addr original_addr)
1570 {
1571     struct alias_link *link;
1572     
1573     link = FindLinkOut(original_addr, nullAddress,
1574                        0, 0, LINK_ADDR, 0);
1575     if (link == NULL)
1576     {
1577         return aliasAddress;
1578     }
1579     else
1580     {
1581         if (link->alias_addr.s_addr == INADDR_ANY)
1582             return aliasAddress;
1583         else
1584             return link->alias_addr;
1585     }
1586 }
1587
1588
1589 /* External routines for getting or changing link data
1590    (external to alias_db.c, but internal to alias*.c)
1591
1592     SetFragmentData(), GetFragmentData()
1593     SetFragmentPtr(), GetFragmentPtr()
1594     SetStateIn(), SetStateOut(), GetStateIn(), GetStateOut()
1595     GetOriginalAddress(), GetDestAddress(), GetAliasAddress()
1596     GetOriginalPort(), GetAliasPort()
1597     SetAckModified(), GetAckModified()
1598     GetDeltaAckIn(), GetDeltaSeqOut(), AddSeq()
1599 */
1600
1601
1602 void
1603 SetFragmentAddr(struct alias_link *link, struct in_addr src_addr)
1604 {
1605     link->data.frag_addr = src_addr;
1606 }
1607
1608
1609 void
1610 GetFragmentAddr(struct alias_link *link, struct in_addr *src_addr)
1611 {
1612     *src_addr = link->data.frag_addr;
1613 }
1614
1615
1616 void
1617 SetFragmentPtr(struct alias_link *link, char *fptr)
1618 {
1619     link->data.frag_ptr = fptr;
1620 }
1621
1622
1623 void
1624 GetFragmentPtr(struct alias_link *link, char **fptr)
1625 {
1626    *fptr = link->data.frag_ptr;
1627 }
1628
1629
1630 void
1631 SetStateIn(struct alias_link *link, int state)
1632 {
1633     /* TCP input state */
1634     switch (state) {
1635     case ALIAS_TCP_STATE_DISCONNECTED:
1636         if (link->data.tcp->state.out != ALIAS_TCP_STATE_CONNECTED)
1637             link->expire_time = TCP_EXPIRE_DEAD;
1638         else
1639             link->expire_time = TCP_EXPIRE_SINGLEDEAD;
1640         break;
1641     case ALIAS_TCP_STATE_CONNECTED:
1642         if (link->data.tcp->state.out == ALIAS_TCP_STATE_CONNECTED)
1643             link->expire_time = TCP_EXPIRE_CONNECTED;
1644         break;
1645     default:
1646         abort();
1647     }
1648     link->data.tcp->state.in = state;
1649 }
1650
1651
1652 void
1653 SetStateOut(struct alias_link *link, int state)
1654 {
1655     /* TCP output state */
1656     switch (state) {
1657     case ALIAS_TCP_STATE_DISCONNECTED:
1658         if (link->data.tcp->state.in != ALIAS_TCP_STATE_CONNECTED)
1659             link->expire_time = TCP_EXPIRE_DEAD;
1660         else
1661             link->expire_time = TCP_EXPIRE_SINGLEDEAD;
1662         break;
1663     case ALIAS_TCP_STATE_CONNECTED:
1664         if (link->data.tcp->state.in == ALIAS_TCP_STATE_CONNECTED)
1665             link->expire_time = TCP_EXPIRE_CONNECTED;
1666         break;
1667     default:
1668         abort();
1669     }
1670     link->data.tcp->state.out = state;
1671 }
1672
1673
1674 int
1675 GetStateIn(struct alias_link *link)
1676 {
1677     /* TCP input state */
1678     return link->data.tcp->state.in;
1679 }
1680
1681
1682 int
1683 GetStateOut(struct alias_link *link)
1684 {
1685     /* TCP output state */
1686     return link->data.tcp->state.out;
1687 }
1688
1689
1690 struct in_addr
1691 GetOriginalAddress(struct alias_link *link)
1692 {
1693     if (link->src_addr.s_addr == INADDR_ANY)
1694         return aliasAddress;
1695     else
1696         return(link->src_addr);
1697 }
1698
1699
1700 struct in_addr
1701 GetDestAddress(struct alias_link *link)
1702 {
1703     return(link->dst_addr);
1704 }
1705
1706
1707 struct in_addr
1708 GetAliasAddress(struct alias_link *link)
1709 {
1710     if (link->alias_addr.s_addr == INADDR_ANY)
1711         return aliasAddress;
1712     else
1713         return link->alias_addr;
1714 }
1715
1716
1717 struct in_addr
1718 GetDefaultAliasAddress()
1719 {
1720     return aliasAddress;
1721 }
1722
1723
1724 void
1725 SetDefaultAliasAddress(struct in_addr alias_addr)
1726 {
1727     aliasAddress = alias_addr;
1728 }
1729
1730
1731 u_short
1732 GetOriginalPort(struct alias_link *link)
1733 {
1734     return(link->src_port);
1735 }
1736
1737
1738 u_short
1739 GetAliasPort(struct alias_link *link)
1740 {
1741     return(link->alias_port);
1742 }
1743
1744 #ifndef NO_FW_PUNCH
1745 static u_short
1746 GetDestPort(struct alias_link *link)
1747 {
1748     return(link->dst_port);
1749 }
1750 #endif
1751
1752 void
1753 SetAckModified(struct alias_link *link)
1754 {
1755 /* Indicate that ACK numbers have been modified in a TCP connection */
1756     link->data.tcp->state.ack_modified = 1;
1757 }
1758
1759
1760 struct in_addr
1761 GetProxyAddress(struct alias_link *link)
1762 {
1763     return link->proxy_addr;
1764 }
1765
1766
1767 void
1768 SetProxyAddress(struct alias_link *link, struct in_addr addr)
1769 {
1770     link->proxy_addr = addr;
1771 }
1772
1773
1774 u_short
1775 GetProxyPort(struct alias_link *link)
1776 {
1777     return link->proxy_port;
1778 }
1779
1780
1781 void
1782 SetProxyPort(struct alias_link *link, u_short port)
1783 {
1784     link->proxy_port = port;
1785 }
1786
1787
1788 int
1789 GetAckModified(struct alias_link *link)
1790 {
1791 /* See if ACK numbers have been modified */
1792     return link->data.tcp->state.ack_modified;
1793 }
1794
1795
1796 int
1797 GetDeltaAckIn(struct ip *pip, struct alias_link *link)
1798 {
1799 /*
1800 Find out how much the ACK number has been altered for an incoming
1801 TCP packet.  To do this, a circular list of ACK numbers where the TCP
1802 packet size was altered is searched. 
1803 */
1804
1805     int i;
1806     struct tcphdr *tc;
1807     int delta, ack_diff_min;
1808     u_long ack;
1809
1810     tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
1811     ack      = tc->th_ack;
1812
1813     delta = 0;
1814     ack_diff_min = -1;
1815     for (i=0; i<N_LINK_TCP_DATA; i++)
1816     {
1817         struct ack_data_record x;
1818
1819         x = link->data.tcp->ack[i];
1820         if (x.active == 1)
1821         {
1822             int ack_diff;
1823
1824             ack_diff = SeqDiff(x.ack_new, ack);
1825             if (ack_diff >= 0)
1826             {
1827                 if (ack_diff_min >= 0)
1828                 {
1829                     if (ack_diff < ack_diff_min)
1830                     {
1831                         delta = x.delta;
1832                         ack_diff_min = ack_diff;
1833                     }
1834                 }
1835                 else
1836                 {
1837                     delta = x.delta;
1838                     ack_diff_min = ack_diff;
1839                 }
1840             }
1841         }
1842     }
1843     return (delta);
1844 }
1845
1846
1847 int
1848 GetDeltaSeqOut(struct ip *pip, struct alias_link *link)
1849 {
1850 /*
1851 Find out how much the sequence number has been altered for an outgoing
1852 TCP packet.  To do this, a circular list of ACK numbers where the TCP
1853 packet size was altered is searched. 
1854 */
1855
1856     int i;
1857     struct tcphdr *tc;
1858     int delta, seq_diff_min;
1859     u_long seq;
1860
1861     tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
1862     seq = tc->th_seq;
1863
1864     delta = 0;
1865     seq_diff_min = -1;
1866     for (i=0; i<N_LINK_TCP_DATA; i++)
1867     {
1868         struct ack_data_record x;
1869
1870         x = link->data.tcp->ack[i];
1871         if (x.active == 1)
1872         {
1873             int seq_diff;
1874
1875             seq_diff = SeqDiff(x.ack_old, seq);
1876             if (seq_diff >= 0)
1877             {
1878                 if (seq_diff_min >= 0)
1879                 {
1880                     if (seq_diff < seq_diff_min)
1881                     {
1882                         delta = x.delta;
1883                         seq_diff_min = seq_diff;
1884                     }
1885                 }
1886                 else
1887                 {
1888                     delta = x.delta;
1889                     seq_diff_min = seq_diff;
1890                 }
1891             }
1892         }
1893     }
1894     return (delta);
1895 }
1896
1897
1898 void
1899 AddSeq(struct ip *pip, struct alias_link *link, int delta)
1900 {
1901 /*
1902 When a TCP packet has been altered in length, save this
1903 information in a circular list.  If enough packets have
1904 been altered, then this list will begin to overwrite itself.
1905 */
1906
1907     struct tcphdr *tc;
1908     struct ack_data_record x;
1909     int hlen, tlen, dlen;
1910     int i;
1911
1912     tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
1913
1914     hlen = (pip->ip_hl + tc->th_off) << 2;
1915     tlen = ntohs(pip->ip_len);
1916     dlen = tlen - hlen;
1917
1918     x.ack_old = htonl(ntohl(tc->th_seq) + dlen);
1919     x.ack_new = htonl(ntohl(tc->th_seq) + dlen + delta);
1920     x.delta = delta;
1921     x.active = 1;
1922
1923     i = link->data.tcp->state.index;
1924     link->data.tcp->ack[i] = x;
1925
1926     i++;
1927     if (i == N_LINK_TCP_DATA)
1928         link->data.tcp->state.index = 0;
1929     else
1930         link->data.tcp->state.index = i;
1931 }
1932
1933 void
1934 SetExpire(struct alias_link *link, int expire)
1935 {
1936     if (expire == 0)
1937     {
1938         link->flags &= ~LINK_PERMANENT;
1939         DeleteLink(link);
1940     }
1941     else if (expire == -1)
1942     {
1943         link->flags |= LINK_PERMANENT;
1944     }
1945     else if (expire > 0)
1946     {
1947         link->expire_time = expire;
1948     }
1949     else
1950     {
1951 #ifdef DEBUG
1952         fprintf(stderr, "PacketAlias/SetExpire(): ");
1953         fprintf(stderr, "error in expire parameter\n");
1954 #endif
1955     }
1956 }
1957
1958 void
1959 ClearCheckNewLink(void)
1960 {
1961     newDefaultLink = 0;
1962 }
1963
1964
1965 /* Miscellaneous Functions
1966
1967     HouseKeeping()
1968     InitPacketAliasLog()
1969     UninitPacketAliasLog()
1970 */
1971
1972 /*
1973     Whenever an outgoing or incoming packet is handled, HouseKeeping()
1974     is called to find and remove timed-out aliasing links.  Logic exists
1975     to sweep through the entire table and linked list structure
1976     every 60 seconds.
1977
1978     (prototype in alias_local.h)
1979 */
1980
1981 void
1982 HouseKeeping(void)
1983 {
1984     int i, n, n100;
1985     struct timeval tv;
1986     struct timezone tz;
1987
1988     /*
1989      * Save system time (seconds) in global variable timeStamp for
1990      * use by other functions. This is done so as not to unnecessarily
1991      * waste timeline by making system calls.
1992      */
1993     gettimeofday(&tv, &tz);
1994     timeStamp = tv.tv_sec;
1995
1996     /* Compute number of spokes (output table link chains) to cover */
1997     n100  = LINK_TABLE_OUT_SIZE * 100 + houseKeepingResidual;
1998     n100 *= timeStamp - lastCleanupTime;
1999     n100 /= ALIAS_CLEANUP_INTERVAL_SECS;
2000
2001     n = n100/100;
2002
2003     /* Handle different cases */
2004     if (n > ALIAS_CLEANUP_MAX_SPOKES)
2005     {
2006         n = ALIAS_CLEANUP_MAX_SPOKES;
2007         lastCleanupTime = timeStamp;
2008         houseKeepingResidual = 0;
2009
2010         for (i=0; i<n; i++)
2011             IncrementalCleanup();
2012     }
2013     else if (n > 0)
2014     {
2015         lastCleanupTime = timeStamp;
2016         houseKeepingResidual = n100 - 100*n;
2017
2018         for (i=0; i<n; i++)
2019             IncrementalCleanup();
2020     }
2021     else if (n < 0)
2022     {
2023 #ifdef DEBUG
2024         fprintf(stderr, "PacketAlias/HouseKeeping(): ");
2025         fprintf(stderr, "something unexpected in time values\n");
2026 #endif
2027         lastCleanupTime = timeStamp;
2028         houseKeepingResidual = 0;
2029     }
2030 }
2031
2032
2033 /* Init the log file and enable logging */
2034 static void
2035 InitPacketAliasLog(void)
2036 {
2037    if ((~packetAliasMode & PKT_ALIAS_LOG)
2038     && (monitorFile = fopen("/var/log/alias.log", "w")))
2039    {
2040       packetAliasMode |= PKT_ALIAS_LOG;
2041       fprintf(monitorFile,
2042       "PacketAlias/InitPacketAliasLog: Packet alias logging enabled.\n");
2043    }
2044 }
2045
2046
2047 /* Close the log-file and disable logging. */
2048 static void
2049 UninitPacketAliasLog(void)
2050 {
2051     if (monitorFile) {
2052         fclose(monitorFile);
2053         monitorFile = NULL;
2054     }
2055     packetAliasMode &= ~PKT_ALIAS_LOG;
2056 }
2057
2058
2059
2060
2061
2062
2063 /* Outside world interfaces
2064
2065 -- "outside world" means other than alias*.c routines --
2066
2067     PacketAliasRedirectPort()
2068     PacketAliasAddServer()
2069     PacketAliasRedirectProto()
2070     PacketAliasRedirectAddr()
2071     PacketAliasRedirectDelete()
2072     PacketAliasSetAddress()
2073     PacketAliasInit()
2074     PacketAliasUninit()
2075     PacketAliasSetMode()
2076
2077 (prototypes in alias.h)
2078 */
2079
2080 /* Redirection from a specific public addr:port to a
2081    private addr:port */
2082 struct alias_link *
2083 PacketAliasRedirectPort(struct in_addr src_addr,   u_short src_port,
2084                         struct in_addr dst_addr,   u_short dst_port,
2085                         struct in_addr alias_addr, u_short alias_port,
2086                         u_char proto)
2087 {
2088     int link_type;
2089     struct alias_link *link;
2090
2091     switch(proto)
2092     {
2093     case IPPROTO_UDP:
2094         link_type = LINK_UDP;
2095         break;
2096     case IPPROTO_TCP:
2097         link_type = LINK_TCP;
2098         break;
2099     default:
2100 #ifdef DEBUG
2101         fprintf(stderr, "PacketAliasRedirectPort(): ");
2102         fprintf(stderr, "only TCP and UDP protocols allowed\n");
2103 #endif
2104         return NULL;
2105     }
2106
2107     link = AddLink(src_addr, dst_addr, alias_addr,
2108                    src_port, dst_port, alias_port,
2109                    link_type);
2110
2111     if (link != NULL)
2112     {
2113         link->flags |= LINK_PERMANENT;
2114     }
2115 #ifdef DEBUG
2116     else
2117     {
2118         fprintf(stderr, "PacketAliasRedirectPort(): " 
2119                         "call to AddLink() failed\n");
2120     }
2121 #endif
2122
2123     return link;
2124 }
2125
2126 /* Add server to the pool of servers */
2127 int
2128 PacketAliasAddServer(struct alias_link *link, struct in_addr addr, u_short port)
2129 {
2130     struct server *server;
2131
2132     server = malloc(sizeof(struct server));
2133
2134     if (server != NULL) {
2135         struct server *head;
2136
2137         server->addr = addr;
2138         server->port = port;
2139
2140         head = link->server;
2141         if (head == NULL)
2142             server->next = server;
2143         else {
2144             struct server *s;
2145
2146             for (s = head; s->next != head; s = s->next);
2147             s->next = server;
2148             server->next = head;
2149         }
2150         link->server = server;
2151         return (0);
2152     } else
2153         return (-1);
2154 }
2155
2156 /* Translate PPTP packets to a machine on the inside
2157  * XXX This function is made obsolete by PacketAliasRedirectProto().
2158  */
2159 int
2160 PacketAliasPptp(struct in_addr src_addr)
2161 {
2162
2163     if (src_addr.s_addr != INADDR_NONE)
2164         (void)PacketAliasRedirectProto(src_addr, nullAddress, nullAddress,
2165                                        IPPROTO_GRE);
2166
2167     return 1;
2168 }
2169
2170 /* Redirect packets of a given IP protocol from a specific
2171    public address to a private address */
2172 struct alias_link *
2173 PacketAliasRedirectProto(struct in_addr src_addr,
2174                          struct in_addr dst_addr,
2175                          struct in_addr alias_addr,
2176                          u_char proto)
2177 {
2178     struct alias_link *link;
2179
2180     link = AddLink(src_addr, dst_addr, alias_addr,
2181                    NO_SRC_PORT, NO_DEST_PORT, 0,
2182                    proto);
2183
2184     if (link != NULL)
2185     {
2186         link->flags |= LINK_PERMANENT;
2187     }
2188 #ifdef DEBUG
2189     else
2190     {
2191         fprintf(stderr, "PacketAliasRedirectProto(): " 
2192                         "call to AddLink() failed\n");
2193     }
2194 #endif
2195
2196     return link;
2197 }
2198
2199 /* Static address translation */
2200 struct alias_link *
2201 PacketAliasRedirectAddr(struct in_addr src_addr,
2202                         struct in_addr alias_addr)
2203 {
2204     struct alias_link *link;
2205
2206     link = AddLink(src_addr, nullAddress, alias_addr,
2207                    0, 0, 0,
2208                    LINK_ADDR);
2209
2210     if (link != NULL)
2211     {
2212         link->flags |= LINK_PERMANENT;
2213     }
2214 #ifdef DEBUG
2215     else
2216     {
2217         fprintf(stderr, "PacketAliasRedirectAddr(): " 
2218                         "call to AddLink() failed\n");
2219     }
2220 #endif
2221
2222     return link;
2223 }
2224
2225
2226 void
2227 PacketAliasRedirectDelete(struct alias_link *link)
2228 {
2229 /* This is a dangerous function to put in the API,
2230    because an invalid pointer can crash the program. */
2231
2232     deleteAllLinks = 1;
2233     DeleteLink(link);
2234     deleteAllLinks = 0;
2235 }
2236
2237
2238 void
2239 PacketAliasSetAddress(struct in_addr addr)
2240 {
2241     if (packetAliasMode & PKT_ALIAS_RESET_ON_ADDR_CHANGE
2242      && aliasAddress.s_addr != addr.s_addr)
2243         CleanupAliasData();
2244
2245     aliasAddress = addr;
2246 }
2247
2248
2249 void
2250 PacketAliasSetTarget(struct in_addr target_addr)
2251 {
2252     targetAddress = target_addr;
2253 }
2254
2255
2256 void
2257 PacketAliasInit(void)
2258 {
2259     int i;
2260     struct timeval tv;
2261     struct timezone tz;
2262     static int firstCall = 1;
2263
2264     if (firstCall == 1)
2265     {
2266         gettimeofday(&tv, &tz);
2267         timeStamp = tv.tv_sec;
2268         lastCleanupTime = tv.tv_sec;
2269         houseKeepingResidual = 0;
2270
2271         for (i=0; i<LINK_TABLE_OUT_SIZE; i++)
2272             linkTableOut[i] = NULL;
2273         for (i=0; i<LINK_TABLE_IN_SIZE; i++)
2274             linkTableIn[i] = NULL;
2275
2276         atexit(PacketAliasUninit);
2277         firstCall = 0;
2278     }
2279     else
2280     {
2281         deleteAllLinks = 1;
2282         CleanupAliasData();
2283         deleteAllLinks = 0;
2284     }
2285
2286     aliasAddress.s_addr = INADDR_ANY;
2287     targetAddress.s_addr = INADDR_ANY;
2288
2289     icmpLinkCount = 0;
2290     udpLinkCount = 0;
2291     tcpLinkCount = 0;
2292     protoLinkCount = 0;
2293     fragmentIdLinkCount = 0;
2294     fragmentPtrLinkCount = 0;
2295     sockCount = 0;
2296
2297     cleanupIndex =0;
2298
2299     packetAliasMode = PKT_ALIAS_SAME_PORTS
2300                     | PKT_ALIAS_USE_SOCKETS
2301                     | PKT_ALIAS_RESET_ON_ADDR_CHANGE;
2302 }
2303
2304 void
2305 PacketAliasUninit(void) {
2306     deleteAllLinks = 1;
2307     CleanupAliasData();
2308     deleteAllLinks = 0;
2309     UninitPacketAliasLog();
2310 #ifndef NO_FW_PUNCH
2311     UninitPunchFW();
2312 #endif
2313 }
2314
2315
2316 /* Change mode for some operations */
2317 unsigned int
2318 PacketAliasSetMode(
2319     unsigned int flags, /* Which state to bring flags to */
2320     unsigned int mask   /* Mask of which flags to affect (use 0 to do a
2321                            probe for flag values) */
2322 )
2323 {
2324 /* Enable logging? */
2325     if (flags & mask & PKT_ALIAS_LOG)
2326     {
2327         InitPacketAliasLog();     /* Do the enable */
2328     } else
2329 /* _Disable_ logging? */
2330     if (~flags & mask & PKT_ALIAS_LOG) {
2331         UninitPacketAliasLog();
2332     }
2333
2334 #ifndef NO_FW_PUNCH
2335 /* Start punching holes in the firewall? */
2336     if (flags & mask & PKT_ALIAS_PUNCH_FW) {
2337         InitPunchFW();
2338     } else
2339 /* Stop punching holes in the firewall? */
2340     if (~flags & mask & PKT_ALIAS_PUNCH_FW) {
2341         UninitPunchFW();
2342     }
2343 #endif
2344
2345 /* Other flags can be set/cleared without special action */
2346     packetAliasMode = (flags & mask) | (packetAliasMode & ~mask);
2347     return packetAliasMode;
2348 }
2349
2350
2351 int
2352 PacketAliasCheckNewLink(void)
2353 {
2354     return newDefaultLink;
2355 }
2356
2357
2358 #ifndef NO_FW_PUNCH
2359
2360 /*****************
2361   Code to support firewall punching.  This shouldn't really be in this
2362   file, but making variables global is evil too.
2363   ****************/
2364
2365 /* Firewall include files */
2366 #include <sys/queue.h>
2367 #include <net/if.h>
2368 #include <netinet/ip_fw.h>
2369 #include <string.h>
2370 #include <err.h>
2371
2372 static void ClearAllFWHoles(void);
2373
2374 static int fireWallBaseNum;     /* The first firewall entry free for our use */
2375 static int fireWallNumNums;     /* How many entries can we use? */
2376 static int fireWallActiveNum;   /* Which entry did we last use? */
2377 static char *fireWallField;     /* bool array for entries */
2378
2379 #define fw_setfield(field, num)                         \
2380 do {                                                    \
2381     (field)[num] = 1;                                   \
2382 } /*lint -save -e717 */ while(0) /*lint -restore */
2383 #define fw_clrfield(field, num)                         \
2384 do {                                                    \
2385     (field)[num] = 0;                                   \
2386 } /*lint -save -e717 */ while(0) /*lint -restore */
2387 #define fw_tstfield(field, num) ((field)[num])
2388
2389 void
2390 PacketAliasSetFWBase(unsigned int base, unsigned int num) {
2391     fireWallBaseNum = base;
2392     fireWallNumNums = num;
2393 }
2394
2395 static void
2396 InitPunchFW(void) {
2397     fireWallField = malloc(fireWallNumNums);
2398     if (fireWallField) {
2399         memset(fireWallField, 0, fireWallNumNums);
2400         if (fireWallFD < 0) {
2401             fireWallFD = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
2402         }
2403         ClearAllFWHoles();
2404         fireWallActiveNum = fireWallBaseNum;
2405     }
2406 }
2407
2408 static void
2409 UninitPunchFW(void) {
2410     ClearAllFWHoles();
2411     if (fireWallFD >= 0)
2412         close(fireWallFD);
2413     fireWallFD = -1;
2414     if (fireWallField)
2415         free(fireWallField);
2416     fireWallField = NULL;
2417     packetAliasMode &= ~PKT_ALIAS_PUNCH_FW;
2418 }
2419
2420 /* Make a certain link go through the firewall */
2421 void
2422 PunchFWHole(struct alias_link *link) {
2423     int r;                      /* Result code */
2424     struct ip_fw rule;          /* On-the-fly built rule */
2425     int fwhole;                 /* Where to punch hole */
2426
2427 /* Don't do anything unless we are asked to */
2428     if ( !(packetAliasMode & PKT_ALIAS_PUNCH_FW) ||
2429          fireWallFD < 0 ||
2430          link->link_type != LINK_TCP ||
2431          !link->data.tcp)
2432         return;
2433
2434     memset(&rule, 0, sizeof rule);
2435
2436 /** Build rule **/
2437
2438     /* Find empty slot */
2439     for (fwhole = fireWallActiveNum;
2440          fwhole < fireWallBaseNum + fireWallNumNums &&
2441              fw_tstfield(fireWallField, fwhole);
2442          fwhole++)
2443         ;
2444     if (fwhole >= fireWallBaseNum + fireWallNumNums ||
2445         fw_tstfield(fireWallField, fwhole)) {
2446         for (fwhole = fireWallBaseNum;
2447              fwhole < fireWallActiveNum &&
2448                  fw_tstfield(fireWallField, fwhole);
2449              fwhole++)
2450             ;
2451         if (fwhole == fireWallActiveNum) {
2452             /* No rule point empty - we can't punch more holes. */
2453             fireWallActiveNum = fireWallBaseNum;
2454 #ifdef DEBUG
2455             fprintf(stderr, "libalias: Unable to create firewall hole!\n");
2456 #endif
2457             return;
2458         }
2459     }
2460     /* Start next search at next position */
2461     fireWallActiveNum = fwhole+1;
2462
2463     /* Build generic part of the two rules */
2464     rule.fw_number = fwhole;
2465     rule.fw_nports = 1;         /* Number of source ports; dest ports follow */
2466     rule.fw_flg = IP_FW_F_ACCEPT;
2467     rule.fw_prot = IPPROTO_TCP;
2468     rule.fw_smsk.s_addr = INADDR_BROADCAST;
2469     rule.fw_dmsk.s_addr = INADDR_BROADCAST;
2470
2471     /* Build and apply specific part of the rules */
2472     rule.fw_src = GetOriginalAddress(link);
2473     rule.fw_dst = GetDestAddress(link);
2474     rule.fw_uar.fw_pts[0] = ntohs(GetOriginalPort(link));
2475     rule.fw_uar.fw_pts[1] = ntohs(GetDestPort(link));
2476
2477     /* Skip non-bound links - XXX should not be strictly necessary,
2478        but seems to leave hole if not done.  Leak of non-bound links?
2479        (Code should be left even if the problem is fixed - it is a
2480        clear optimization) */
2481     if (rule.fw_uar.fw_pts[0] != 0 && rule.fw_uar.fw_pts[1] != 0) {
2482         r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, &rule, sizeof rule);
2483 #ifdef DEBUG
2484         if (r)
2485             err(1, "alias punch inbound(1) setsockopt(IP_FW_ADD)");
2486 #endif
2487         rule.fw_src = GetDestAddress(link);
2488         rule.fw_dst = GetOriginalAddress(link);
2489         rule.fw_uar.fw_pts[0] = ntohs(GetDestPort(link));
2490         rule.fw_uar.fw_pts[1] = ntohs(GetOriginalPort(link));
2491         r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, &rule, sizeof rule);
2492 #ifdef DEBUG
2493         if (r)
2494             err(1, "alias punch inbound(2) setsockopt(IP_FW_ADD)");
2495 #endif
2496     }
2497 /* Indicate hole applied */
2498     link->data.tcp->fwhole = fwhole;
2499     fw_setfield(fireWallField, fwhole);
2500 }
2501
2502 /* Remove a hole in a firewall associated with a particular alias
2503    link.  Calling this too often is harmless. */
2504 static void
2505 ClearFWHole(struct alias_link *link) {
2506     if (link->link_type == LINK_TCP && link->data.tcp) {
2507         int fwhole =  link->data.tcp->fwhole; /* Where is the firewall hole? */
2508         struct ip_fw rule;
2509
2510         if (fwhole < 0)
2511             return;
2512
2513         memset(&rule, 0, sizeof rule);
2514         rule.fw_number = fwhole;
2515         while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule))
2516             ;
2517         fw_clrfield(fireWallField, fwhole);
2518         link->data.tcp->fwhole = -1;
2519     }
2520 }
2521
2522 /* Clear out the entire range dedicated to firewall holes. */
2523 static void
2524 ClearAllFWHoles(void) {
2525     struct ip_fw rule;          /* On-the-fly built rule */
2526     int i;
2527     
2528     if (fireWallFD < 0)
2529         return;
2530
2531     memset(&rule, 0, sizeof rule);
2532     for (i = fireWallBaseNum; i < fireWallBaseNum + fireWallNumNums; i++) {
2533         rule.fw_number = i;
2534         while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule))
2535             ;
2536     }
2537     memset(fireWallField, 0, fireWallNumNums);
2538 }
2539 #endif