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