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