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