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