xref: /freebsd/sys/netinet/libalias/alias_db.c (revision 77a0943ded95b9e6438f7db70c4a28e4d93946d4)
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 
283 /* flag bits */
284 #define LINK_UNKNOWN_DEST_PORT     0x01
285 #define LINK_UNKNOWN_DEST_ADDR     0x02
286 #define LINK_PERMANENT             0x04
287 #define LINK_PARTIALLY_SPECIFIED   0x03 /* logical-or of first two bits */
288 #define LINK_UNFIREWALLED          0x08
289 #define LINK_LAST_LINE_CRLF_TERMED 0x10
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->timestamp         = timeStamp;
995 
996     /* Expiration time */
997         switch (link_type)
998         {
999         case LINK_ICMP:
1000             link->expire_time = ICMP_EXPIRE_TIME;
1001             break;
1002         case LINK_UDP:
1003             link->expire_time = UDP_EXPIRE_TIME;
1004             break;
1005         case LINK_TCP:
1006             link->expire_time = TCP_EXPIRE_INITIAL;
1007             break;
1008         case LINK_PPTP:
1009             link->flags |= LINK_PERMANENT;	/* no timeout. */
1010             break;
1011         case LINK_FRAGMENT_ID:
1012             link->expire_time = FRAGMENT_ID_EXPIRE_TIME;
1013             break;
1014         case LINK_FRAGMENT_PTR:
1015             link->expire_time = FRAGMENT_PTR_EXPIRE_TIME;
1016             break;
1017 	case LINK_ADDR:
1018 	    break;
1019         default:
1020             link->expire_time = PROTO_EXPIRE_TIME;
1021             break;
1022         }
1023 
1024     /* Determine alias flags */
1025         if (dst_addr.s_addr == INADDR_ANY)
1026             link->flags |= LINK_UNKNOWN_DEST_ADDR;
1027         if (dst_port == 0)
1028             link->flags |= LINK_UNKNOWN_DEST_PORT;
1029 
1030     /* Determine alias port */
1031         if (GetNewPort(link, alias_port_param) != 0)
1032         {
1033             free(link);
1034             return(NULL);
1035         }
1036 
1037     /* Link-type dependent initialization */
1038         switch(link_type)
1039         {
1040             struct tcp_dat  *aux_tcp;
1041 
1042             case LINK_ICMP:
1043                 icmpLinkCount++;
1044                 break;
1045             case LINK_UDP:
1046                 udpLinkCount++;
1047                 break;
1048             case LINK_TCP:
1049                 aux_tcp = malloc(sizeof(struct tcp_dat));
1050                 if (aux_tcp != NULL)
1051                 {
1052                     int i;
1053 
1054                     tcpLinkCount++;
1055                     aux_tcp->state.in = ALIAS_TCP_STATE_NOT_CONNECTED;
1056                     aux_tcp->state.out = ALIAS_TCP_STATE_NOT_CONNECTED;
1057                     aux_tcp->state.index = 0;
1058                     aux_tcp->state.ack_modified = 0;
1059                     for (i=0; i<N_LINK_TCP_DATA; i++)
1060                         aux_tcp->ack[i].active = 0;
1061                     aux_tcp->fwhole = -1;
1062                     link->data.tcp = aux_tcp;
1063                 }
1064                 else
1065                 {
1066 #ifdef DEBUG
1067                     fprintf(stderr, "PacketAlias/AddLink: ");
1068                     fprintf(stderr, " cannot allocate auxiliary TCP data\n");
1069 #endif
1070 		    free(link);
1071 		    return (NULL);
1072                 }
1073                 break;
1074             case LINK_PPTP:
1075                 pptpLinkCount++;
1076                 break;
1077             case LINK_FRAGMENT_ID:
1078                 fragmentIdLinkCount++;
1079                 break;
1080             case LINK_FRAGMENT_PTR:
1081                 fragmentPtrLinkCount++;
1082                 break;
1083 	    case LINK_ADDR:
1084 		break;
1085             default:
1086                 protoLinkCount++;
1087                 break;
1088         }
1089 
1090     /* Set up pointers for output lookup table */
1091         start_point = StartPointOut(src_addr, dst_addr,
1092                                     src_port, dst_port, link_type);
1093         LIST_INSERT_HEAD(&linkTableOut[start_point], link, list_out);
1094 
1095     /* Set up pointers for input lookup table */
1096         start_point = StartPointIn(alias_addr, link->alias_port, link_type);
1097         LIST_INSERT_HEAD(&linkTableIn[start_point], link, list_in);
1098     }
1099     else
1100     {
1101 #ifdef DEBUG
1102         fprintf(stderr, "PacketAlias/AddLink(): ");
1103         fprintf(stderr, "malloc() call failed.\n");
1104 #endif
1105     }
1106 
1107     if (packetAliasMode & PKT_ALIAS_LOG)
1108     {
1109         ShowAliasStats();
1110     }
1111 
1112     return(link);
1113 }
1114 
1115 static struct alias_link *
1116 ReLink(struct alias_link *old_link,
1117        struct in_addr  src_addr,
1118        struct in_addr  dst_addr,
1119        struct in_addr  alias_addr,
1120        u_short         src_port,
1121        u_short         dst_port,
1122        int             alias_port_param,   /* if less than zero, alias   */
1123        int             link_type)          /* port will be automatically */
1124 {                                          /* chosen. If greater than    */
1125     struct alias_link *new_link;           /* zero, equal to alias port  */
1126 
1127     new_link = AddLink(src_addr, dst_addr, alias_addr,
1128                        src_port, dst_port, alias_port_param,
1129                        link_type);
1130 #ifndef NO_FW_PUNCH
1131     if (new_link != NULL &&
1132         old_link->link_type == LINK_TCP &&
1133         old_link->data.tcp->fwhole > 0) {
1134       PunchFWHole(new_link);
1135     }
1136 #endif
1137     DeleteLink(old_link);
1138     return new_link;
1139 }
1140 
1141 static struct alias_link *
1142 _FindLinkOut(struct in_addr src_addr,
1143             struct in_addr dst_addr,
1144             u_short src_port,
1145             u_short dst_port,
1146             int link_type,
1147             int replace_partial_links)
1148 {
1149     u_int i;
1150     struct alias_link *link;
1151 
1152     i = StartPointOut(src_addr, dst_addr, src_port, dst_port, link_type);
1153     LIST_FOREACH(link, &linkTableOut[i], list_out)
1154     {
1155         if (link->src_addr.s_addr == src_addr.s_addr
1156          && link->server          == NULL
1157          && link->dst_addr.s_addr == dst_addr.s_addr
1158          && link->dst_port        == dst_port
1159          && link->src_port        == src_port
1160          && link->link_type       == link_type)
1161         {
1162             link->timestamp = timeStamp;
1163             break;
1164         }
1165     }
1166 
1167 /* Search for partially specified links. */
1168     if (link == NULL && replace_partial_links)
1169     {
1170         if (dst_port != 0 && dst_addr.s_addr != INADDR_ANY)
1171         {
1172             link = _FindLinkOut(src_addr, dst_addr, src_port, 0,
1173                                 link_type, 0);
1174             if (link == NULL)
1175                 link = _FindLinkOut(src_addr, nullAddress, src_port,
1176                                     dst_port, link_type, 0);
1177         }
1178         if (link == NULL &&
1179            (dst_port != 0 || dst_addr.s_addr != INADDR_ANY))
1180         {
1181             link = _FindLinkOut(src_addr, nullAddress, src_port, 0,
1182                                 link_type, 0);
1183         }
1184         if (link != NULL)
1185         {
1186             link = ReLink(link,
1187                           src_addr, dst_addr, link->alias_addr,
1188                           src_port, dst_port, link->alias_port,
1189                           link_type);
1190         }
1191     }
1192 
1193     return(link);
1194 }
1195 
1196 static struct alias_link *
1197 FindLinkOut(struct in_addr src_addr,
1198             struct in_addr dst_addr,
1199             u_short src_port,
1200             u_short dst_port,
1201             int link_type,
1202             int replace_partial_links)
1203 {
1204     struct alias_link *link;
1205 
1206     link = _FindLinkOut(src_addr, dst_addr, src_port, dst_port,
1207                         link_type, replace_partial_links);
1208 
1209     if (link == NULL)
1210     {
1211     /* The following allows permanent links to be
1212        specified as using the default source address
1213        (i.e. device interface address) without knowing
1214        in advance what that address is. */
1215         if (aliasAddress.s_addr != 0 &&
1216             src_addr.s_addr == aliasAddress.s_addr)
1217         {
1218             link = _FindLinkOut(nullAddress, dst_addr, src_port, dst_port,
1219                                link_type, replace_partial_links);
1220         }
1221     }
1222 
1223     return(link);
1224 }
1225 
1226 
1227 static struct alias_link *
1228 _FindLinkIn(struct in_addr dst_addr,
1229            struct in_addr  alias_addr,
1230            u_short         dst_port,
1231            u_short         alias_port,
1232            int             link_type,
1233            int             replace_partial_links)
1234 {
1235     int flags_in;
1236     u_int start_point;
1237     struct alias_link *link;
1238     struct alias_link *link_fully_specified;
1239     struct alias_link *link_unknown_all;
1240     struct alias_link *link_unknown_dst_addr;
1241     struct alias_link *link_unknown_dst_port;
1242 
1243 /* Initialize pointers */
1244     link_fully_specified  = NULL;
1245     link_unknown_all      = NULL;
1246     link_unknown_dst_addr = NULL;
1247     link_unknown_dst_port = NULL;
1248 
1249 /* If either the dest addr or port is unknown, the search
1250    loop will have to know about this. */
1251 
1252     flags_in = 0;
1253     if (dst_addr.s_addr == INADDR_ANY)
1254         flags_in |= LINK_UNKNOWN_DEST_ADDR;
1255     if (dst_port == 0)
1256         flags_in |= LINK_UNKNOWN_DEST_PORT;
1257 
1258 /* Search loop */
1259     start_point = StartPointIn(alias_addr, alias_port, link_type);
1260     LIST_FOREACH(link, &linkTableIn[start_point], list_in)
1261     {
1262         int flags;
1263 
1264         flags = flags_in | link->flags;
1265         if (!(flags & LINK_PARTIALLY_SPECIFIED))
1266         {
1267             if (link->alias_addr.s_addr == alias_addr.s_addr
1268              && link->alias_port        == alias_port
1269              && link->dst_addr.s_addr   == dst_addr.s_addr
1270              && link->dst_port          == dst_port
1271              && link->link_type         == link_type)
1272             {
1273                 link_fully_specified = link;
1274                 break;
1275             }
1276         }
1277         else if ((flags & LINK_UNKNOWN_DEST_ADDR)
1278               && (flags & LINK_UNKNOWN_DEST_PORT))
1279         {
1280             if (link->alias_addr.s_addr == alias_addr.s_addr
1281              && link->alias_port        == alias_port
1282              && link->link_type         == link_type)
1283             {
1284                 if (link_unknown_all == NULL)
1285                     link_unknown_all = link;
1286             }
1287         }
1288         else if (flags & LINK_UNKNOWN_DEST_ADDR)
1289         {
1290             if (link->alias_addr.s_addr == alias_addr.s_addr
1291              && link->alias_port        == alias_port
1292              && link->link_type         == link_type
1293              && link->dst_port          == dst_port)
1294             {
1295                 if (link_unknown_dst_addr == NULL)
1296                     link_unknown_dst_addr = link;
1297             }
1298         }
1299         else if (flags & LINK_UNKNOWN_DEST_PORT)
1300         {
1301             if (link->alias_addr.s_addr == alias_addr.s_addr
1302              && link->alias_port        == alias_port
1303              && link->link_type         == link_type
1304              && link->dst_addr.s_addr   == dst_addr.s_addr)
1305             {
1306                 if (link_unknown_dst_port == NULL)
1307                     link_unknown_dst_port = link;
1308             }
1309         }
1310     }
1311 
1312 
1313 
1314     if (link_fully_specified != NULL)
1315     {
1316         link_fully_specified->timestamp = timeStamp;
1317         link = link_fully_specified;
1318     }
1319     else if (link_unknown_dst_port != NULL)
1320 	link = link_unknown_dst_port;
1321     else if (link_unknown_dst_addr != NULL)
1322 	link = link_unknown_dst_addr;
1323     else if (link_unknown_all != NULL)
1324 	link = link_unknown_all;
1325     else
1326         return (NULL);
1327 
1328     if (replace_partial_links &&
1329 	(link->flags & LINK_PARTIALLY_SPECIFIED || link->server != NULL))
1330     {
1331 	struct in_addr src_addr;
1332 	u_short src_port;
1333 
1334 	if (link->server != NULL) {		/* LSNAT link */
1335 	    src_addr = link->server->addr;
1336 	    src_port = link->server->port;
1337 	    link->server = link->server->next;
1338 	} else {
1339 	    src_addr = link->src_addr;
1340 	    src_port = link->src_port;
1341 	}
1342 
1343 	link = ReLink(link,
1344 		      src_addr, dst_addr, alias_addr,
1345 		      src_port, dst_port, alias_port,
1346 		      link_type);
1347     }
1348 
1349     return (link);
1350 }
1351 
1352 static struct alias_link *
1353 FindLinkIn(struct in_addr dst_addr,
1354            struct in_addr alias_addr,
1355            u_short dst_port,
1356            u_short alias_port,
1357            int link_type,
1358            int replace_partial_links)
1359 {
1360     struct alias_link *link;
1361 
1362     link = _FindLinkIn(dst_addr, alias_addr, dst_port, alias_port,
1363                        link_type, replace_partial_links);
1364 
1365     if (link == NULL)
1366     {
1367     /* The following allows permanent links to be
1368        specified as using the default aliasing address
1369        (i.e. device interface address) without knowing
1370        in advance what that address is. */
1371         if (aliasAddress.s_addr != 0 &&
1372             alias_addr.s_addr == aliasAddress.s_addr)
1373         {
1374             link = _FindLinkIn(dst_addr, nullAddress, dst_port, alias_port,
1375                                link_type, replace_partial_links);
1376         }
1377     }
1378 
1379     return(link);
1380 }
1381 
1382 
1383 
1384 
1385 /* External routines for finding/adding links
1386 
1387 -- "external" means outside alias_db.c, but within alias*.c --
1388 
1389     FindIcmpIn(), FindIcmpOut()
1390     FindFragmentIn1(), FindFragmentIn2()
1391     AddFragmentPtrLink(), FindFragmentPtr()
1392     FindProtoIn(), FindProtoOut()
1393     FindUdpTcpIn(), FindUdpTcpOut()
1394     AddPptp(), FindPptpOutByCallId(), FindPptpInByCallId(),
1395     FindPptpOutByPeerCallId(), FindPptpInByPeerCallId()
1396     FindOriginalAddress(), FindAliasAddress()
1397 
1398 (prototypes in alias_local.h)
1399 */
1400 
1401 
1402 struct alias_link *
1403 FindIcmpIn(struct in_addr dst_addr,
1404            struct in_addr alias_addr,
1405            u_short id_alias,
1406            int create)
1407 {
1408     struct alias_link *link;
1409 
1410     link = FindLinkIn(dst_addr, alias_addr,
1411                       NO_DEST_PORT, id_alias,
1412                       LINK_ICMP, 0);
1413     if (link == NULL && create && !(packetAliasMode & PKT_ALIAS_DENY_INCOMING))
1414     {
1415         struct in_addr target_addr;
1416 
1417         target_addr = FindOriginalAddress(alias_addr);
1418         link = AddLink(target_addr, dst_addr, alias_addr,
1419                        id_alias, NO_DEST_PORT, id_alias,
1420                        LINK_ICMP);
1421     }
1422 
1423     return (link);
1424 }
1425 
1426 
1427 struct alias_link *
1428 FindIcmpOut(struct in_addr src_addr,
1429             struct in_addr dst_addr,
1430             u_short id,
1431             int create)
1432 {
1433     struct alias_link * link;
1434 
1435     link = FindLinkOut(src_addr, dst_addr,
1436                        id, NO_DEST_PORT,
1437                        LINK_ICMP, 0);
1438     if (link == NULL && create)
1439     {
1440         struct in_addr alias_addr;
1441 
1442         alias_addr = FindAliasAddress(src_addr);
1443         link = AddLink(src_addr, dst_addr, alias_addr,
1444                        id, NO_DEST_PORT, GET_ALIAS_ID,
1445                        LINK_ICMP);
1446     }
1447 
1448     return(link);
1449 }
1450 
1451 
1452 struct alias_link *
1453 FindFragmentIn1(struct in_addr dst_addr,
1454                 struct in_addr alias_addr,
1455                 u_short ip_id)
1456 {
1457     struct alias_link *link;
1458 
1459     link = FindLinkIn(dst_addr, alias_addr,
1460                       NO_DEST_PORT, ip_id,
1461                       LINK_FRAGMENT_ID, 0);
1462 
1463     if (link == NULL)
1464     {
1465         link = AddLink(nullAddress, dst_addr, alias_addr,
1466                        NO_SRC_PORT, NO_DEST_PORT, ip_id,
1467                        LINK_FRAGMENT_ID);
1468     }
1469 
1470     return(link);
1471 }
1472 
1473 
1474 struct alias_link *
1475 FindFragmentIn2(struct in_addr dst_addr,   /* Doesn't add a link if one */
1476                 struct in_addr alias_addr, /*   is not found.           */
1477                 u_short ip_id)
1478 {
1479     return FindLinkIn(dst_addr, alias_addr,
1480                       NO_DEST_PORT, ip_id,
1481                       LINK_FRAGMENT_ID, 0);
1482 }
1483 
1484 
1485 struct alias_link *
1486 AddFragmentPtrLink(struct in_addr dst_addr,
1487                    u_short ip_id)
1488 {
1489     return AddLink(nullAddress, dst_addr, nullAddress,
1490                    NO_SRC_PORT, NO_DEST_PORT, ip_id,
1491                    LINK_FRAGMENT_PTR);
1492 }
1493 
1494 
1495 struct alias_link *
1496 FindFragmentPtr(struct in_addr dst_addr,
1497                 u_short ip_id)
1498 {
1499     return FindLinkIn(dst_addr, nullAddress,
1500                       NO_DEST_PORT, ip_id,
1501                       LINK_FRAGMENT_PTR, 0);
1502 }
1503 
1504 
1505 struct alias_link *
1506 FindProtoIn(struct in_addr dst_addr,
1507             struct in_addr alias_addr,
1508 	    u_char proto)
1509 {
1510     struct alias_link *link;
1511 
1512     link = FindLinkIn(dst_addr, alias_addr,
1513                       NO_DEST_PORT, 0,
1514                       proto, 1);
1515 
1516     if (link == NULL && !(packetAliasMode & PKT_ALIAS_DENY_INCOMING))
1517     {
1518         struct in_addr target_addr;
1519 
1520         target_addr = FindOriginalAddress(alias_addr);
1521         link = AddLink(target_addr, dst_addr, alias_addr,
1522                        NO_SRC_PORT, NO_DEST_PORT, 0,
1523                        proto);
1524     }
1525 
1526     return (link);
1527 }
1528 
1529 
1530 struct alias_link *
1531 FindProtoOut(struct in_addr src_addr,
1532              struct in_addr dst_addr,
1533              u_char proto)
1534 {
1535     struct alias_link *link;
1536 
1537     link = FindLinkOut(src_addr, dst_addr,
1538                        NO_SRC_PORT, NO_DEST_PORT,
1539                        proto, 1);
1540 
1541     if (link == NULL)
1542     {
1543         struct in_addr alias_addr;
1544 
1545         alias_addr = FindAliasAddress(src_addr);
1546         link = AddLink(src_addr, dst_addr, alias_addr,
1547                        NO_SRC_PORT, NO_DEST_PORT, 0,
1548                        proto);
1549     }
1550 
1551     return (link);
1552 }
1553 
1554 
1555 struct alias_link *
1556 FindUdpTcpIn(struct in_addr dst_addr,
1557              struct in_addr alias_addr,
1558              u_short        dst_port,
1559              u_short        alias_port,
1560              u_char         proto,
1561              int            create)
1562 {
1563     int link_type;
1564     struct alias_link *link;
1565 
1566     switch (proto)
1567     {
1568     case IPPROTO_UDP:
1569         link_type = LINK_UDP;
1570         break;
1571     case IPPROTO_TCP:
1572         link_type = LINK_TCP;
1573         break;
1574     default:
1575         return NULL;
1576         break;
1577     }
1578 
1579     link = FindLinkIn(dst_addr, alias_addr,
1580                       dst_port, alias_port,
1581                       link_type, create);
1582 
1583     if (link == NULL && create && !(packetAliasMode & PKT_ALIAS_DENY_INCOMING))
1584     {
1585         struct in_addr target_addr;
1586 
1587         target_addr = FindOriginalAddress(alias_addr);
1588         link = AddLink(target_addr, dst_addr, alias_addr,
1589                        alias_port, dst_port, alias_port,
1590                        link_type);
1591     }
1592 
1593     return(link);
1594 }
1595 
1596 
1597 struct alias_link *
1598 FindUdpTcpOut(struct in_addr  src_addr,
1599               struct in_addr  dst_addr,
1600               u_short         src_port,
1601               u_short         dst_port,
1602               u_char          proto,
1603               int             create)
1604 {
1605     int link_type;
1606     struct alias_link *link;
1607 
1608     switch (proto)
1609     {
1610     case IPPROTO_UDP:
1611         link_type = LINK_UDP;
1612         break;
1613     case IPPROTO_TCP:
1614         link_type = LINK_TCP;
1615         break;
1616     default:
1617         return NULL;
1618         break;
1619     }
1620 
1621     link = FindLinkOut(src_addr, dst_addr, src_port, dst_port, link_type, create);
1622 
1623     if (link == NULL && create)
1624     {
1625         struct in_addr alias_addr;
1626 
1627         alias_addr = FindAliasAddress(src_addr);
1628         link = AddLink(src_addr, dst_addr, alias_addr,
1629                        src_port, dst_port, GET_ALIAS_PORT,
1630                        link_type);
1631     }
1632 
1633     return(link);
1634 }
1635 
1636 
1637 struct alias_link *
1638 AddPptp(struct in_addr  src_addr,
1639 	struct in_addr  dst_addr,
1640 	struct in_addr  alias_addr,
1641 	u_int16_t       src_call_id)
1642 {
1643     struct alias_link *link;
1644 
1645     link = AddLink(src_addr, dst_addr, alias_addr,
1646 		   src_call_id, 0, GET_ALIAS_PORT,
1647 		   LINK_PPTP);
1648 
1649     return (link);
1650 }
1651 
1652 
1653 struct alias_link *
1654 FindPptpOutByCallId(struct in_addr src_addr,
1655 		    struct in_addr dst_addr,
1656 		    u_int16_t      src_call_id)
1657 {
1658     u_int i;
1659     struct alias_link *link;
1660 
1661     i = StartPointOut(src_addr, dst_addr, 0, 0, LINK_PPTP);
1662     LIST_FOREACH(link, &linkTableOut[i], list_out)
1663 	if (link->link_type == LINK_PPTP &&
1664 	    link->src_addr.s_addr == src_addr.s_addr &&
1665 	    link->dst_addr.s_addr == dst_addr.s_addr &&
1666 	    link->src_port == src_call_id)
1667 		break;
1668 
1669     return (link);
1670 }
1671 
1672 
1673 struct alias_link *
1674 FindPptpOutByPeerCallId(struct in_addr src_addr,
1675 			struct in_addr dst_addr,
1676 			u_int16_t      dst_call_id)
1677 {
1678     u_int i;
1679     struct alias_link *link;
1680 
1681     i = StartPointOut(src_addr, dst_addr, 0, 0, LINK_PPTP);
1682     LIST_FOREACH(link, &linkTableOut[i], list_out)
1683 	if (link->link_type == LINK_PPTP &&
1684 	    link->src_addr.s_addr == src_addr.s_addr &&
1685 	    link->dst_addr.s_addr == dst_addr.s_addr &&
1686 	    link->dst_port == dst_call_id)
1687 		break;
1688 
1689     return (link);
1690 }
1691 
1692 
1693 struct alias_link *
1694 FindPptpInByCallId(struct in_addr dst_addr,
1695 		   struct in_addr alias_addr,
1696 		   u_int16_t      dst_call_id)
1697 {
1698     u_int i;
1699     struct alias_link *link;
1700 
1701     i = StartPointIn(alias_addr, 0, LINK_PPTP);
1702     LIST_FOREACH(link, &linkTableIn[i], list_in)
1703 	if (link->link_type == LINK_PPTP &&
1704 	    link->dst_addr.s_addr == dst_addr.s_addr &&
1705 	    link->alias_addr.s_addr == alias_addr.s_addr &&
1706 	    link->dst_port == dst_call_id)
1707 		break;
1708 
1709     return (link);
1710 }
1711 
1712 
1713 struct alias_link *
1714 FindPptpInByPeerCallId(struct in_addr dst_addr,
1715 		       struct in_addr alias_addr,
1716 		       u_int16_t      alias_call_id)
1717 {
1718     struct alias_link *link;
1719 
1720     link = FindLinkIn(dst_addr, alias_addr,
1721 		      0/* any */, alias_call_id,
1722 		      LINK_PPTP, 0);
1723 
1724 
1725     return (link);
1726 }
1727 
1728 
1729 struct alias_link *
1730 FindRtspOut(struct in_addr  src_addr,
1731             struct in_addr  dst_addr,
1732             u_short         src_port,
1733             u_short         alias_port,
1734             u_char          proto)
1735 {
1736     int link_type;
1737     struct alias_link *link;
1738 
1739     switch (proto)
1740     {
1741     case IPPROTO_UDP:
1742         link_type = LINK_UDP;
1743         break;
1744     case IPPROTO_TCP:
1745         link_type = LINK_TCP;
1746         break;
1747     default:
1748         return NULL;
1749         break;
1750     }
1751 
1752     link = FindLinkOut(src_addr, dst_addr, src_port, 0, link_type, 1);
1753 
1754     if (link == NULL)
1755     {
1756         struct in_addr alias_addr;
1757 
1758         alias_addr = FindAliasAddress(src_addr);
1759         link = AddLink(src_addr, dst_addr, alias_addr,
1760                        src_port, 0, alias_port,
1761                        link_type);
1762     }
1763 
1764     return(link);
1765 }
1766 
1767 
1768 struct in_addr
1769 FindOriginalAddress(struct in_addr alias_addr)
1770 {
1771     struct alias_link *link;
1772 
1773     link = FindLinkIn(nullAddress, alias_addr,
1774                       0, 0, LINK_ADDR, 0);
1775     if (link == NULL)
1776     {
1777         newDefaultLink = 1;
1778         if (targetAddress.s_addr == INADDR_ANY)
1779             return alias_addr;
1780         else if (targetAddress.s_addr == INADDR_NONE)
1781             return aliasAddress;
1782         else
1783             return targetAddress;
1784     }
1785     else
1786     {
1787 	if (link->server != NULL) {		/* LSNAT link */
1788 	    struct in_addr src_addr;
1789 
1790 	    src_addr = link->server->addr;
1791 	    link->server = link->server->next;
1792 	    return (src_addr);
1793         } else if (link->src_addr.s_addr == INADDR_ANY)
1794             return aliasAddress;
1795         else
1796             return link->src_addr;
1797     }
1798 }
1799 
1800 
1801 struct in_addr
1802 FindAliasAddress(struct in_addr original_addr)
1803 {
1804     struct alias_link *link;
1805 
1806     link = FindLinkOut(original_addr, nullAddress,
1807                        0, 0, LINK_ADDR, 0);
1808     if (link == NULL)
1809     {
1810         return aliasAddress;
1811     }
1812     else
1813     {
1814         if (link->alias_addr.s_addr == INADDR_ANY)
1815             return aliasAddress;
1816         else
1817             return link->alias_addr;
1818     }
1819 }
1820 
1821 
1822 /* External routines for getting or changing link data
1823    (external to alias_db.c, but internal to alias*.c)
1824 
1825     SetFragmentData(), GetFragmentData()
1826     SetFragmentPtr(), GetFragmentPtr()
1827     SetStateIn(), SetStateOut(), GetStateIn(), GetStateOut()
1828     GetOriginalAddress(), GetDestAddress(), GetAliasAddress()
1829     GetOriginalPort(), GetAliasPort()
1830     SetAckModified(), GetAckModified()
1831     GetDeltaAckIn(), GetDeltaSeqOut(), AddSeq()
1832     SetLastLineCrlfTermed(), GetLastLineCrlfTermed()
1833     SetDestCallId()
1834 */
1835 
1836 
1837 void
1838 SetFragmentAddr(struct alias_link *link, struct in_addr src_addr)
1839 {
1840     link->data.frag_addr = src_addr;
1841 }
1842 
1843 
1844 void
1845 GetFragmentAddr(struct alias_link *link, struct in_addr *src_addr)
1846 {
1847     *src_addr = link->data.frag_addr;
1848 }
1849 
1850 
1851 void
1852 SetFragmentPtr(struct alias_link *link, char *fptr)
1853 {
1854     link->data.frag_ptr = fptr;
1855 }
1856 
1857 
1858 void
1859 GetFragmentPtr(struct alias_link *link, char **fptr)
1860 {
1861    *fptr = link->data.frag_ptr;
1862 }
1863 
1864 
1865 void
1866 SetStateIn(struct alias_link *link, int state)
1867 {
1868     /* TCP input state */
1869     switch (state) {
1870     case ALIAS_TCP_STATE_DISCONNECTED:
1871         if (link->data.tcp->state.out != ALIAS_TCP_STATE_CONNECTED)
1872             link->expire_time = TCP_EXPIRE_DEAD;
1873         else
1874             link->expire_time = TCP_EXPIRE_SINGLEDEAD;
1875         break;
1876     case ALIAS_TCP_STATE_CONNECTED:
1877         if (link->data.tcp->state.out == ALIAS_TCP_STATE_CONNECTED)
1878             link->expire_time = TCP_EXPIRE_CONNECTED;
1879         break;
1880     default:
1881         abort();
1882     }
1883     link->data.tcp->state.in = state;
1884 }
1885 
1886 
1887 void
1888 SetStateOut(struct alias_link *link, int state)
1889 {
1890     /* TCP output state */
1891     switch (state) {
1892     case ALIAS_TCP_STATE_DISCONNECTED:
1893         if (link->data.tcp->state.in != ALIAS_TCP_STATE_CONNECTED)
1894             link->expire_time = TCP_EXPIRE_DEAD;
1895         else
1896             link->expire_time = TCP_EXPIRE_SINGLEDEAD;
1897         break;
1898     case ALIAS_TCP_STATE_CONNECTED:
1899         if (link->data.tcp->state.in == ALIAS_TCP_STATE_CONNECTED)
1900             link->expire_time = TCP_EXPIRE_CONNECTED;
1901         break;
1902     default:
1903         abort();
1904     }
1905     link->data.tcp->state.out = state;
1906 }
1907 
1908 
1909 int
1910 GetStateIn(struct alias_link *link)
1911 {
1912     /* TCP input state */
1913     return link->data.tcp->state.in;
1914 }
1915 
1916 
1917 int
1918 GetStateOut(struct alias_link *link)
1919 {
1920     /* TCP output state */
1921     return link->data.tcp->state.out;
1922 }
1923 
1924 
1925 struct in_addr
1926 GetOriginalAddress(struct alias_link *link)
1927 {
1928     if (link->src_addr.s_addr == INADDR_ANY)
1929         return aliasAddress;
1930     else
1931         return(link->src_addr);
1932 }
1933 
1934 
1935 struct in_addr
1936 GetDestAddress(struct alias_link *link)
1937 {
1938     return(link->dst_addr);
1939 }
1940 
1941 
1942 struct in_addr
1943 GetAliasAddress(struct alias_link *link)
1944 {
1945     if (link->alias_addr.s_addr == INADDR_ANY)
1946         return aliasAddress;
1947     else
1948         return link->alias_addr;
1949 }
1950 
1951 
1952 struct in_addr
1953 GetDefaultAliasAddress()
1954 {
1955     return aliasAddress;
1956 }
1957 
1958 
1959 void
1960 SetDefaultAliasAddress(struct in_addr alias_addr)
1961 {
1962     aliasAddress = alias_addr;
1963 }
1964 
1965 
1966 u_short
1967 GetOriginalPort(struct alias_link *link)
1968 {
1969     return(link->src_port);
1970 }
1971 
1972 
1973 u_short
1974 GetAliasPort(struct alias_link *link)
1975 {
1976     return(link->alias_port);
1977 }
1978 
1979 #ifndef NO_FW_PUNCH
1980 static u_short
1981 GetDestPort(struct alias_link *link)
1982 {
1983     return(link->dst_port);
1984 }
1985 #endif
1986 
1987 void
1988 SetAckModified(struct alias_link *link)
1989 {
1990 /* Indicate that ACK numbers have been modified in a TCP connection */
1991     link->data.tcp->state.ack_modified = 1;
1992 }
1993 
1994 
1995 struct in_addr
1996 GetProxyAddress(struct alias_link *link)
1997 {
1998     return link->proxy_addr;
1999 }
2000 
2001 
2002 void
2003 SetProxyAddress(struct alias_link *link, struct in_addr addr)
2004 {
2005     link->proxy_addr = addr;
2006 }
2007 
2008 
2009 u_short
2010 GetProxyPort(struct alias_link *link)
2011 {
2012     return link->proxy_port;
2013 }
2014 
2015 
2016 void
2017 SetProxyPort(struct alias_link *link, u_short port)
2018 {
2019     link->proxy_port = port;
2020 }
2021 
2022 
2023 int
2024 GetAckModified(struct alias_link *link)
2025 {
2026 /* See if ACK numbers have been modified */
2027     return link->data.tcp->state.ack_modified;
2028 }
2029 
2030 
2031 int
2032 GetDeltaAckIn(struct ip *pip, struct alias_link *link)
2033 {
2034 /*
2035 Find out how much the ACK number has been altered for an incoming
2036 TCP packet.  To do this, a circular list of ACK numbers where the TCP
2037 packet size was altered is searched.
2038 */
2039 
2040     int i;
2041     struct tcphdr *tc;
2042     int delta, ack_diff_min;
2043     u_long ack;
2044 
2045     tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
2046     ack      = tc->th_ack;
2047 
2048     delta = 0;
2049     ack_diff_min = -1;
2050     for (i=0; i<N_LINK_TCP_DATA; i++)
2051     {
2052         struct ack_data_record x;
2053 
2054         x = link->data.tcp->ack[i];
2055         if (x.active == 1)
2056         {
2057             int ack_diff;
2058 
2059             ack_diff = SeqDiff(x.ack_new, ack);
2060             if (ack_diff >= 0)
2061             {
2062                 if (ack_diff_min >= 0)
2063                 {
2064                     if (ack_diff < ack_diff_min)
2065                     {
2066                         delta = x.delta;
2067                         ack_diff_min = ack_diff;
2068                     }
2069                 }
2070                 else
2071                 {
2072                     delta = x.delta;
2073                     ack_diff_min = ack_diff;
2074                 }
2075             }
2076         }
2077     }
2078     return (delta);
2079 }
2080 
2081 
2082 int
2083 GetDeltaSeqOut(struct ip *pip, struct alias_link *link)
2084 {
2085 /*
2086 Find out how much the sequence number has been altered for an outgoing
2087 TCP packet.  To do this, a circular list of ACK numbers where the TCP
2088 packet size was altered is searched.
2089 */
2090 
2091     int i;
2092     struct tcphdr *tc;
2093     int delta, seq_diff_min;
2094     u_long seq;
2095 
2096     tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
2097     seq = tc->th_seq;
2098 
2099     delta = 0;
2100     seq_diff_min = -1;
2101     for (i=0; i<N_LINK_TCP_DATA; i++)
2102     {
2103         struct ack_data_record x;
2104 
2105         x = link->data.tcp->ack[i];
2106         if (x.active == 1)
2107         {
2108             int seq_diff;
2109 
2110             seq_diff = SeqDiff(x.ack_old, seq);
2111             if (seq_diff >= 0)
2112             {
2113                 if (seq_diff_min >= 0)
2114                 {
2115                     if (seq_diff < seq_diff_min)
2116                     {
2117                         delta = x.delta;
2118                         seq_diff_min = seq_diff;
2119                     }
2120                 }
2121                 else
2122                 {
2123                     delta = x.delta;
2124                     seq_diff_min = seq_diff;
2125                 }
2126             }
2127         }
2128     }
2129     return (delta);
2130 }
2131 
2132 
2133 void
2134 AddSeq(struct ip *pip, struct alias_link *link, int delta)
2135 {
2136 /*
2137 When a TCP packet has been altered in length, save this
2138 information in a circular list.  If enough packets have
2139 been altered, then this list will begin to overwrite itself.
2140 */
2141 
2142     struct tcphdr *tc;
2143     struct ack_data_record x;
2144     int hlen, tlen, dlen;
2145     int i;
2146 
2147     tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
2148 
2149     hlen = (pip->ip_hl + tc->th_off) << 2;
2150     tlen = ntohs(pip->ip_len);
2151     dlen = tlen - hlen;
2152 
2153     x.ack_old = htonl(ntohl(tc->th_seq) + dlen);
2154     x.ack_new = htonl(ntohl(tc->th_seq) + dlen + delta);
2155     x.delta = delta;
2156     x.active = 1;
2157 
2158     i = link->data.tcp->state.index;
2159     link->data.tcp->ack[i] = x;
2160 
2161     i++;
2162     if (i == N_LINK_TCP_DATA)
2163         link->data.tcp->state.index = 0;
2164     else
2165         link->data.tcp->state.index = i;
2166 }
2167 
2168 void
2169 SetExpire(struct alias_link *link, int expire)
2170 {
2171     if (expire == 0)
2172     {
2173         link->flags &= ~LINK_PERMANENT;
2174         DeleteLink(link);
2175     }
2176     else if (expire == -1)
2177     {
2178         link->flags |= LINK_PERMANENT;
2179     }
2180     else if (expire > 0)
2181     {
2182         link->expire_time = expire;
2183     }
2184     else
2185     {
2186 #ifdef DEBUG
2187         fprintf(stderr, "PacketAlias/SetExpire(): ");
2188         fprintf(stderr, "error in expire parameter\n");
2189 #endif
2190     }
2191 }
2192 
2193 void
2194 ClearCheckNewLink(void)
2195 {
2196     newDefaultLink = 0;
2197 }
2198 
2199 void
2200 SetLastLineCrlfTermed(struct alias_link *link, int yes)
2201 {
2202 
2203     if (yes)
2204 	link->flags |= LINK_LAST_LINE_CRLF_TERMED;
2205     else
2206 	link->flags &= ~LINK_LAST_LINE_CRLF_TERMED;
2207 }
2208 
2209 int
2210 GetLastLineCrlfTermed(struct alias_link *link)
2211 {
2212 
2213     return (link->flags & LINK_LAST_LINE_CRLF_TERMED);
2214 }
2215 
2216 void
2217 SetDestCallId(struct alias_link *link, u_int16_t cid)
2218 {
2219 
2220     deleteAllLinks = 1;
2221     link = ReLink(link, link->src_addr, link->dst_addr, link->alias_addr,
2222 		  link->src_port, cid, link->alias_port, link->link_type);
2223     deleteAllLinks = 0;
2224 }
2225 
2226 
2227 /* Miscellaneous Functions
2228 
2229     HouseKeeping()
2230     InitPacketAliasLog()
2231     UninitPacketAliasLog()
2232 */
2233 
2234 /*
2235     Whenever an outgoing or incoming packet is handled, HouseKeeping()
2236     is called to find and remove timed-out aliasing links.  Logic exists
2237     to sweep through the entire table and linked list structure
2238     every 60 seconds.
2239 
2240     (prototype in alias_local.h)
2241 */
2242 
2243 void
2244 HouseKeeping(void)
2245 {
2246     int i, n, n100;
2247     struct timeval tv;
2248     struct timezone tz;
2249 
2250     /*
2251      * Save system time (seconds) in global variable timeStamp for
2252      * use by other functions. This is done so as not to unnecessarily
2253      * waste timeline by making system calls.
2254      */
2255     gettimeofday(&tv, &tz);
2256     timeStamp = tv.tv_sec;
2257 
2258     /* Compute number of spokes (output table link chains) to cover */
2259     n100  = LINK_TABLE_OUT_SIZE * 100 + houseKeepingResidual;
2260     n100 *= timeStamp - lastCleanupTime;
2261     n100 /= ALIAS_CLEANUP_INTERVAL_SECS;
2262 
2263     n = n100/100;
2264 
2265     /* Handle different cases */
2266     if (n > ALIAS_CLEANUP_MAX_SPOKES)
2267     {
2268         n = ALIAS_CLEANUP_MAX_SPOKES;
2269         lastCleanupTime = timeStamp;
2270         houseKeepingResidual = 0;
2271 
2272         for (i=0; i<n; i++)
2273             IncrementalCleanup();
2274     }
2275     else if (n > 0)
2276     {
2277         lastCleanupTime = timeStamp;
2278         houseKeepingResidual = n100 - 100*n;
2279 
2280         for (i=0; i<n; i++)
2281             IncrementalCleanup();
2282     }
2283     else if (n < 0)
2284     {
2285 #ifdef DEBUG
2286         fprintf(stderr, "PacketAlias/HouseKeeping(): ");
2287         fprintf(stderr, "something unexpected in time values\n");
2288 #endif
2289         lastCleanupTime = timeStamp;
2290         houseKeepingResidual = 0;
2291     }
2292 }
2293 
2294 
2295 /* Init the log file and enable logging */
2296 static void
2297 InitPacketAliasLog(void)
2298 {
2299    if ((~packetAliasMode & PKT_ALIAS_LOG)
2300     && (monitorFile = fopen("/var/log/alias.log", "w")))
2301    {
2302       packetAliasMode |= PKT_ALIAS_LOG;
2303       fprintf(monitorFile,
2304       "PacketAlias/InitPacketAliasLog: Packet alias logging enabled.\n");
2305    }
2306 }
2307 
2308 
2309 /* Close the log-file and disable logging. */
2310 static void
2311 UninitPacketAliasLog(void)
2312 {
2313     if (monitorFile) {
2314         fclose(monitorFile);
2315         monitorFile = NULL;
2316     }
2317     packetAliasMode &= ~PKT_ALIAS_LOG;
2318 }
2319 
2320 
2321 
2322 
2323 
2324 
2325 /* Outside world interfaces
2326 
2327 -- "outside world" means other than alias*.c routines --
2328 
2329     PacketAliasRedirectPort()
2330     PacketAliasAddServer()
2331     PacketAliasRedirectProto()
2332     PacketAliasRedirectAddr()
2333     PacketAliasRedirectDelete()
2334     PacketAliasSetAddress()
2335     PacketAliasInit()
2336     PacketAliasUninit()
2337     PacketAliasSetMode()
2338 
2339 (prototypes in alias.h)
2340 */
2341 
2342 /* Redirection from a specific public addr:port to a
2343    private addr:port */
2344 struct alias_link *
2345 PacketAliasRedirectPort(struct in_addr src_addr,   u_short src_port,
2346                         struct in_addr dst_addr,   u_short dst_port,
2347                         struct in_addr alias_addr, u_short alias_port,
2348                         u_char proto)
2349 {
2350     int link_type;
2351     struct alias_link *link;
2352 
2353     switch(proto)
2354     {
2355     case IPPROTO_UDP:
2356         link_type = LINK_UDP;
2357         break;
2358     case IPPROTO_TCP:
2359         link_type = LINK_TCP;
2360         break;
2361     default:
2362 #ifdef DEBUG
2363         fprintf(stderr, "PacketAliasRedirectPort(): ");
2364         fprintf(stderr, "only TCP and UDP protocols allowed\n");
2365 #endif
2366         return NULL;
2367     }
2368 
2369     link = AddLink(src_addr, dst_addr, alias_addr,
2370                    src_port, dst_port, alias_port,
2371                    link_type);
2372 
2373     if (link != NULL)
2374     {
2375         link->flags |= LINK_PERMANENT;
2376     }
2377 #ifdef DEBUG
2378     else
2379     {
2380         fprintf(stderr, "PacketAliasRedirectPort(): "
2381                         "call to AddLink() failed\n");
2382     }
2383 #endif
2384 
2385     return link;
2386 }
2387 
2388 /* Add server to the pool of servers */
2389 int
2390 PacketAliasAddServer(struct alias_link *link, struct in_addr addr, u_short port)
2391 {
2392     struct server *server;
2393 
2394     server = malloc(sizeof(struct server));
2395 
2396     if (server != NULL) {
2397 	struct server *head;
2398 
2399 	server->addr = addr;
2400 	server->port = port;
2401 
2402 	head = link->server;
2403 	if (head == NULL)
2404 	    server->next = server;
2405 	else {
2406 	    struct server *s;
2407 
2408 	    for (s = head; s->next != head; s = s->next);
2409 	    s->next = server;
2410 	    server->next = head;
2411 	}
2412 	link->server = server;
2413 	return (0);
2414     } else
2415 	return (-1);
2416 }
2417 
2418 /* Redirect packets of a given IP protocol from a specific
2419    public address to a private address */
2420 struct alias_link *
2421 PacketAliasRedirectProto(struct in_addr src_addr,
2422                          struct in_addr dst_addr,
2423                          struct in_addr alias_addr,
2424                          u_char proto)
2425 {
2426     struct alias_link *link;
2427 
2428     link = AddLink(src_addr, dst_addr, alias_addr,
2429                    NO_SRC_PORT, NO_DEST_PORT, 0,
2430                    proto);
2431 
2432     if (link != NULL)
2433     {
2434         link->flags |= LINK_PERMANENT;
2435     }
2436 #ifdef DEBUG
2437     else
2438     {
2439         fprintf(stderr, "PacketAliasRedirectProto(): "
2440                         "call to AddLink() failed\n");
2441     }
2442 #endif
2443 
2444     return link;
2445 }
2446 
2447 /* Static address translation */
2448 struct alias_link *
2449 PacketAliasRedirectAddr(struct in_addr src_addr,
2450                         struct in_addr alias_addr)
2451 {
2452     struct alias_link *link;
2453 
2454     link = AddLink(src_addr, nullAddress, alias_addr,
2455                    0, 0, 0,
2456                    LINK_ADDR);
2457 
2458     if (link != NULL)
2459     {
2460         link->flags |= LINK_PERMANENT;
2461     }
2462 #ifdef DEBUG
2463     else
2464     {
2465         fprintf(stderr, "PacketAliasRedirectAddr(): "
2466                         "call to AddLink() failed\n");
2467     }
2468 #endif
2469 
2470     return link;
2471 }
2472 
2473 
2474 void
2475 PacketAliasRedirectDelete(struct alias_link *link)
2476 {
2477 /* This is a dangerous function to put in the API,
2478    because an invalid pointer can crash the program. */
2479 
2480     deleteAllLinks = 1;
2481     DeleteLink(link);
2482     deleteAllLinks = 0;
2483 }
2484 
2485 
2486 void
2487 PacketAliasSetAddress(struct in_addr addr)
2488 {
2489     if (packetAliasMode & PKT_ALIAS_RESET_ON_ADDR_CHANGE
2490      && aliasAddress.s_addr != addr.s_addr)
2491         CleanupAliasData();
2492 
2493     aliasAddress = addr;
2494 }
2495 
2496 
2497 void
2498 PacketAliasSetTarget(struct in_addr target_addr)
2499 {
2500     targetAddress = target_addr;
2501 }
2502 
2503 
2504 void
2505 PacketAliasInit(void)
2506 {
2507     int i;
2508     struct timeval tv;
2509     struct timezone tz;
2510     static int firstCall = 1;
2511 
2512     if (firstCall == 1)
2513     {
2514         gettimeofday(&tv, &tz);
2515         timeStamp = tv.tv_sec;
2516         lastCleanupTime = tv.tv_sec;
2517         houseKeepingResidual = 0;
2518 
2519         for (i=0; i<LINK_TABLE_OUT_SIZE; i++)
2520             LIST_INIT(&linkTableOut[i]);
2521         for (i=0; i<LINK_TABLE_IN_SIZE; i++)
2522             LIST_INIT(&linkTableIn[i]);
2523 
2524         atexit(PacketAliasUninit);
2525         firstCall = 0;
2526     }
2527     else
2528     {
2529         deleteAllLinks = 1;
2530         CleanupAliasData();
2531         deleteAllLinks = 0;
2532     }
2533 
2534     aliasAddress.s_addr = INADDR_ANY;
2535     targetAddress.s_addr = INADDR_ANY;
2536 
2537     icmpLinkCount = 0;
2538     udpLinkCount = 0;
2539     tcpLinkCount = 0;
2540     pptpLinkCount = 0;
2541     protoLinkCount = 0;
2542     fragmentIdLinkCount = 0;
2543     fragmentPtrLinkCount = 0;
2544     sockCount = 0;
2545 
2546     cleanupIndex =0;
2547 
2548     packetAliasMode = PKT_ALIAS_SAME_PORTS
2549                     | PKT_ALIAS_USE_SOCKETS
2550                     | PKT_ALIAS_RESET_ON_ADDR_CHANGE;
2551 }
2552 
2553 void
2554 PacketAliasUninit(void) {
2555     deleteAllLinks = 1;
2556     CleanupAliasData();
2557     deleteAllLinks = 0;
2558     UninitPacketAliasLog();
2559 #ifndef NO_FW_PUNCH
2560     UninitPunchFW();
2561 #endif
2562 }
2563 
2564 
2565 /* Change mode for some operations */
2566 unsigned int
2567 PacketAliasSetMode(
2568     unsigned int flags, /* Which state to bring flags to */
2569     unsigned int mask   /* Mask of which flags to affect (use 0 to do a
2570                            probe for flag values) */
2571 )
2572 {
2573 /* Enable logging? */
2574     if (flags & mask & PKT_ALIAS_LOG)
2575     {
2576         InitPacketAliasLog();     /* Do the enable */
2577     } else
2578 /* _Disable_ logging? */
2579     if (~flags & mask & PKT_ALIAS_LOG) {
2580         UninitPacketAliasLog();
2581     }
2582 
2583 #ifndef NO_FW_PUNCH
2584 /* Start punching holes in the firewall? */
2585     if (flags & mask & PKT_ALIAS_PUNCH_FW) {
2586         InitPunchFW();
2587     } else
2588 /* Stop punching holes in the firewall? */
2589     if (~flags & mask & PKT_ALIAS_PUNCH_FW) {
2590         UninitPunchFW();
2591     }
2592 #endif
2593 
2594 /* Other flags can be set/cleared without special action */
2595     packetAliasMode = (flags & mask) | (packetAliasMode & ~mask);
2596     return packetAliasMode;
2597 }
2598 
2599 
2600 int
2601 PacketAliasCheckNewLink(void)
2602 {
2603     return newDefaultLink;
2604 }
2605 
2606 
2607 #ifndef NO_FW_PUNCH
2608 
2609 /*****************
2610   Code to support firewall punching.  This shouldn't really be in this
2611   file, but making variables global is evil too.
2612   ****************/
2613 
2614 /* Firewall include files */
2615 #include <net/if.h>
2616 #include <netinet/ip_fw.h>
2617 #include <string.h>
2618 #include <err.h>
2619 
2620 static void ClearAllFWHoles(void);
2621 
2622 static int fireWallBaseNum;     /* The first firewall entry free for our use */
2623 static int fireWallNumNums;     /* How many entries can we use? */
2624 static int fireWallActiveNum;   /* Which entry did we last use? */
2625 static char *fireWallField;     /* bool array for entries */
2626 
2627 #define fw_setfield(field, num)                         \
2628 do {                                                    \
2629     (field)[(num) - fireWallBaseNum] = 1;               \
2630 } /*lint -save -e717 */ while(0) /*lint -restore */
2631 #define fw_clrfield(field, num)                         \
2632 do {                                                    \
2633     (field)[(num) - fireWallBaseNum] = 0;               \
2634 } /*lint -save -e717 */ while(0) /*lint -restore */
2635 #define fw_tstfield(field, num) ((field)[(num) - fireWallBaseNum])
2636 
2637 void
2638 PacketAliasSetFWBase(unsigned int base, unsigned int num) {
2639     fireWallBaseNum = base;
2640     fireWallNumNums = num;
2641 }
2642 
2643 static void
2644 InitPunchFW(void) {
2645     fireWallField = malloc(fireWallNumNums);
2646     if (fireWallField) {
2647         memset(fireWallField, 0, fireWallNumNums);
2648         if (fireWallFD < 0) {
2649             fireWallFD = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
2650         }
2651         ClearAllFWHoles();
2652         fireWallActiveNum = fireWallBaseNum;
2653     }
2654 }
2655 
2656 static void
2657 UninitPunchFW(void) {
2658     ClearAllFWHoles();
2659     if (fireWallFD >= 0)
2660         close(fireWallFD);
2661     fireWallFD = -1;
2662     if (fireWallField)
2663         free(fireWallField);
2664     fireWallField = NULL;
2665     packetAliasMode &= ~PKT_ALIAS_PUNCH_FW;
2666 }
2667 
2668 /* Make a certain link go through the firewall */
2669 void
2670 PunchFWHole(struct alias_link *link) {
2671     int r;                      /* Result code */
2672     struct ip_fw rule;          /* On-the-fly built rule */
2673     int fwhole;                 /* Where to punch hole */
2674 
2675 /* Don't do anything unless we are asked to */
2676     if ( !(packetAliasMode & PKT_ALIAS_PUNCH_FW) ||
2677          fireWallFD < 0 ||
2678          link->link_type != LINK_TCP)
2679         return;
2680 
2681     memset(&rule, 0, sizeof rule);
2682 
2683 /** Build rule **/
2684 
2685     /* Find empty slot */
2686     for (fwhole = fireWallActiveNum;
2687          fwhole < fireWallBaseNum + fireWallNumNums &&
2688              fw_tstfield(fireWallField, fwhole);
2689          fwhole++)
2690         ;
2691     if (fwhole == fireWallBaseNum + fireWallNumNums) {
2692         for (fwhole = fireWallBaseNum;
2693              fwhole < fireWallActiveNum &&
2694                  fw_tstfield(fireWallField, fwhole);
2695              fwhole++)
2696             ;
2697         if (fwhole == fireWallActiveNum) {
2698             /* No rule point empty - we can't punch more holes. */
2699             fireWallActiveNum = fireWallBaseNum;
2700 #ifdef DEBUG
2701             fprintf(stderr, "libalias: Unable to create firewall hole!\n");
2702 #endif
2703             return;
2704         }
2705     }
2706     /* Start next search at next position */
2707     fireWallActiveNum = fwhole+1;
2708 
2709     /* Build generic part of the two rules */
2710     rule.fw_number = fwhole;
2711     IP_FW_SETNSRCP(&rule, 1);	/* Number of source ports. */
2712     IP_FW_SETNDSTP(&rule, 1);	/* Number of destination ports. */
2713     rule.fw_flg = IP_FW_F_ACCEPT | IP_FW_F_IN | IP_FW_F_OUT;
2714     rule.fw_prot = IPPROTO_TCP;
2715     rule.fw_smsk.s_addr = INADDR_BROADCAST;
2716     rule.fw_dmsk.s_addr = INADDR_BROADCAST;
2717 
2718     /* Build and apply specific part of the rules */
2719     rule.fw_src = GetOriginalAddress(link);
2720     rule.fw_dst = GetDestAddress(link);
2721     rule.fw_uar.fw_pts[0] = ntohs(GetOriginalPort(link));
2722     rule.fw_uar.fw_pts[1] = ntohs(GetDestPort(link));
2723 
2724     /* Skip non-bound links - XXX should not be strictly necessary,
2725        but seems to leave hole if not done.  Leak of non-bound links?
2726        (Code should be left even if the problem is fixed - it is a
2727        clear optimization) */
2728     if (rule.fw_uar.fw_pts[0] != 0 && rule.fw_uar.fw_pts[1] != 0) {
2729         r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, &rule, sizeof rule);
2730 #ifdef DEBUG
2731         if (r)
2732             err(1, "alias punch inbound(1) setsockopt(IP_FW_ADD)");
2733 #endif
2734         rule.fw_src = GetDestAddress(link);
2735         rule.fw_dst = GetOriginalAddress(link);
2736         rule.fw_uar.fw_pts[0] = ntohs(GetDestPort(link));
2737         rule.fw_uar.fw_pts[1] = ntohs(GetOriginalPort(link));
2738         r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, &rule, sizeof rule);
2739 #ifdef DEBUG
2740         if (r)
2741             err(1, "alias punch inbound(2) setsockopt(IP_FW_ADD)");
2742 #endif
2743     }
2744 /* Indicate hole applied */
2745     link->data.tcp->fwhole = fwhole;
2746     fw_setfield(fireWallField, fwhole);
2747 }
2748 
2749 /* Remove a hole in a firewall associated with a particular alias
2750    link.  Calling this too often is harmless. */
2751 static void
2752 ClearFWHole(struct alias_link *link) {
2753     if (link->link_type == LINK_TCP) {
2754         int fwhole =  link->data.tcp->fwhole; /* Where is the firewall hole? */
2755         struct ip_fw rule;
2756 
2757         if (fwhole < 0)
2758             return;
2759 
2760         memset(&rule, 0, sizeof rule);
2761         rule.fw_number = fwhole;
2762         while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule))
2763             ;
2764         fw_clrfield(fireWallField, fwhole);
2765         link->data.tcp->fwhole = -1;
2766     }
2767 }
2768 
2769 /* Clear out the entire range dedicated to firewall holes. */
2770 static void
2771 ClearAllFWHoles(void) {
2772     struct ip_fw rule;          /* On-the-fly built rule */
2773     int i;
2774 
2775     if (fireWallFD < 0)
2776         return;
2777 
2778     memset(&rule, 0, sizeof rule);
2779     for (i = fireWallBaseNum; i < fireWallBaseNum + fireWallNumNums; i++) {
2780         rule.fw_number = i;
2781         while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule))
2782             ;
2783     }
2784     memset(fireWallField, 0, fireWallNumNums);
2785 }
2786 #endif
2787