Merge from vendor branch OPENSSH:
[dragonfly.git] / lib / libalias / alias_db.c
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  * $FreeBSD: src/lib/libalias/alias_db.c,v 1.21.2.14 2002/07/24 03:21:24 luigi Exp $
29  * $DragonFly: src/lib/libalias/alias_db.c,v 1.3 2003/08/08 04:18:33 dillon Exp $
30  */
31
32 /*
33     Alias_db.c encapsulates all data structures used for storing
34     packet aliasing data.  Other parts of the aliasing software
35     access data through functions provided in this file.
36
37     Data storage is based on the notion of a "link", which is
38     established for ICMP echo/reply packets, UDP datagrams and
39     TCP stream connections.  A link stores the original source
40     and destination addresses.  For UDP and TCP, it also stores
41     source and destination port numbers, as well as an alias
42     port number.  Links are also used to store information about
43     fragments.
44
45     There is a facility for sweeping through and deleting old
46     links as new packets are sent through.  A simple timeout is
47     used for ICMP and UDP links.  TCP links are left alone unless
48     there is an incomplete connection, in which case the link
49     can be deleted after a certain amount of time.
50
51
52     Initial version: August, 1996  (cjm)
53
54     Version 1.4: September 16, 1996 (cjm)
55         Facility for handling incoming links added.
56
57     Version 1.6: September 18, 1996 (cjm)
58         ICMP data handling simplified.
59
60     Version 1.7: January 9, 1997 (cjm)
61         Fragment handling simplified.
62         Saves pointers for unresolved fragments.
63         Permits links for unspecified remote ports
64           or unspecified remote addresses.
65         Fixed bug which did not properly zero port
66           table entries after a link was deleted.
67         Cleaned up some obsolete comments.
68
69     Version 1.8: January 14, 1997 (cjm)
70         Fixed data type error in StartPoint().
71         (This error did not exist prior to v1.7
72         and was discovered and fixed by Ari Suutari)
73
74     Version 1.9: February 1, 1997
75         Optionally, connections initiated from packet aliasing host
76         machine will will not have their port number aliased unless it
77         conflicts with an aliasing port already being used. (cjm)
78
79         All options earlier being #ifdef'ed are now available through
80         a new interface, SetPacketAliasMode().  This allows run time
81         control (which is now available in PPP+pktAlias through the
82         'alias' keyword). (ee)
83
84         Added ability to create an alias port without
85         either destination address or port specified.
86         port type = ALIAS_PORT_UNKNOWN_DEST_ALL (ee)
87
88         Removed K&R style function headers
89         and general cleanup. (ee)
90
91         Added packetAliasMode to replace compiler #defines's (ee)
92
93         Allocates sockets for partially specified
94         ports if ALIAS_USE_SOCKETS defined. (cjm)
95
96     Version 2.0: March, 1997
97         SetAliasAddress() will now clean up alias links
98         if the aliasing address is changed. (cjm)
99
100         PacketAliasPermanentLink() function added to support permanent
101         links.  (J. Fortes suggested the need for this.)
102         Examples:
103
104         (192.168.0.1, port 23)  <-> alias port 6002, unknown dest addr/port
105
106         (192.168.0.2, port 21)  <-> alias port 3604, known dest addr
107                                                      unknown dest port
108
109         These permanent links allow for incoming connections to
110         machines on the local network.  They can be given with a
111         user-chosen amount of specificity, with increasing specificity
112         meaning more security. (cjm)
113
114         Quite a bit of rework to the basic engine.  The portTable[]
115         array, which kept track of which ports were in use was replaced
116         by a table/linked list structure. (cjm)
117
118         SetExpire() function added. (cjm)
119
120         DeleteLink() no longer frees memory association with a pointer
121         to a fragment (this bug was first recognized by E. Eklund in
122         v1.9).
123
124     Version 2.1: May, 1997 (cjm)
125         Packet aliasing engine reworked so that it can handle
126         multiple external addresses rather than just a single
127         host address.
128
129         PacketAliasRedirectPort() and PacketAliasRedirectAddr()
130         added to the API.  The first function is a more generalized
131         version of PacketAliasPermanentLink().  The second function
132         implements static network address translation.
133
134     Version 3.2: July, 2000 (salander and satoh)
135         Added FindNewPortGroup to get contiguous range of port values.
136
137         Added QueryUdpTcpIn and QueryUdpTcpOut to look for an aliasing
138         link but not actually add one.
139
140         Added FindRtspOut, which is closely derived from FindUdpTcpOut,
141         except that the alias port (from FindNewPortGroup) is provided
142         as input.
143
144     See HISTORY file for additional revisions.
145 */
146
147
148 /* System include files */
149 #include <errno.h>
150 #include <stdlib.h>
151 #include <stdio.h>
152 #include <unistd.h>
153
154 #include <sys/queue.h>
155 #include <sys/socket.h>
156 #include <sys/time.h>
157 #include <sys/types.h>
158
159 /* BSD network include files */
160 #include <netinet/in_systm.h>
161 #include <netinet/in.h>
162 #include <netinet/ip.h>
163 #include <netinet/tcp.h>
164 #include <arpa/inet.h>
165
166 #include "alias.h"
167 #include "alias_local.h"
168
169
170
171 /*
172    Constants (note: constants are also defined
173               near relevant functions or structs)
174 */
175
176 /* Sizes of input and output link tables */
177 #define LINK_TABLE_OUT_SIZE         101
178 #define LINK_TABLE_IN_SIZE         4001
179
180 /* Parameters used for cleanup of expired links */
181 #define ALIAS_CLEANUP_INTERVAL_SECS  60
182 #define ALIAS_CLEANUP_MAX_SPOKES     30
183
184 /* Timeouts (in seconds) for different link types */
185 #define ICMP_EXPIRE_TIME             60
186 #define UDP_EXPIRE_TIME              60
187 #define PROTO_EXPIRE_TIME            60
188 #define FRAGMENT_ID_EXPIRE_TIME      10
189 #define FRAGMENT_PTR_EXPIRE_TIME     30
190
191 /* TCP link expire time for different cases */
192 /* When the link has been used and closed - minimal grace time to
193    allow ACKs and potential re-connect in FTP (XXX - is this allowed?)  */
194 #ifndef TCP_EXPIRE_DEAD
195 #   define TCP_EXPIRE_DEAD           10
196 #endif
197
198 /* When the link has been used and closed on one side - the other side
199    is allowed to still send data */
200 #ifndef TCP_EXPIRE_SINGLEDEAD
201 #   define TCP_EXPIRE_SINGLEDEAD     90
202 #endif
203
204 /* When the link isn't yet up */
205 #ifndef TCP_EXPIRE_INITIAL
206 #   define TCP_EXPIRE_INITIAL       300
207 #endif
208
209 /* When the link is up */
210 #ifndef TCP_EXPIRE_CONNECTED
211 #   define TCP_EXPIRE_CONNECTED   86400
212 #endif
213
214
215 /* Dummy port number codes used for FindLinkIn/Out() and AddLink().
216    These constants can be anything except zero, which indicates an
217    unknown port number. */
218
219 #define NO_DEST_PORT     1
220 #define NO_SRC_PORT      1
221
222
223
224 /* Data Structures
225
226     The fundamental data structure used in this program is
227     "struct alias_link".  Whenever a TCP connection is made,
228     a UDP datagram is sent out, or an ICMP echo request is made,
229     a link record is made (if it has not already been created).
230     The link record is identified by the source address/port
231     and the destination address/port. In the case of an ICMP
232     echo request, the source port is treated as being equivalent
233     with the 16-bit ID number of the ICMP packet.
234
235     The link record also can store some auxiliary data.  For
236     TCP connections that have had sequence and acknowledgment
237     modifications, data space is available to track these changes.
238     A state field is used to keep track in changes to the TCP
239     connection state.  ID numbers of fragments can also be
240     stored in the auxiliary space.  Pointers to unresolved
241     fragments can also be stored.
242
243     The link records support two independent chainings.  Lookup
244     tables for input and out tables hold the initial pointers
245     the link chains.  On input, the lookup table indexes on alias
246     port and link type.  On output, the lookup table indexes on
247     source address, destination address, source port, destination
248     port and link type.
249 */
250
251 struct ack_data_record     /* used to save changes to ACK/sequence numbers */
252 {
253     u_long ack_old;
254     u_long ack_new;
255     int delta;
256     int active;
257 };
258
259 struct tcp_state           /* Information about TCP connection        */
260 {
261     int in;                /* State for outside -> inside             */
262     int out;               /* State for inside  -> outside            */
263     int index;             /* Index to ACK data array                 */
264     int ack_modified;      /* Indicates whether ACK and sequence numbers */
265                            /* been modified                           */
266 };
267
268 #define N_LINK_TCP_DATA   3 /* Number of distinct ACK number changes
269                                saved for a modified TCP stream */
270 struct tcp_dat
271 {
272     struct tcp_state state;
273     struct ack_data_record ack[N_LINK_TCP_DATA];
274     int fwhole;             /* Which firewall record is used for this hole? */
275 };
276
277 struct server              /* LSNAT server pool (circular list) */
278 {
279     struct in_addr addr;
280     u_short port;
281     struct server *next;
282 };
283
284 struct alias_link                /* Main data structure */
285 {
286     struct in_addr src_addr;     /* Address and port information        */
287     struct in_addr dst_addr;
288     struct in_addr alias_addr;
289     struct in_addr proxy_addr;
290     u_short src_port;
291     u_short dst_port;
292     u_short alias_port;
293     u_short proxy_port;
294     struct server *server;
295
296     int link_type;               /* Type of link: TCP, UDP, ICMP, proto, frag */
297
298 /* values for link_type */
299 #define LINK_ICMP                     IPPROTO_ICMP
300 #define LINK_UDP                      IPPROTO_UDP
301 #define LINK_TCP                      IPPROTO_TCP
302 #define LINK_FRAGMENT_ID              (IPPROTO_MAX + 1)
303 #define LINK_FRAGMENT_PTR             (IPPROTO_MAX + 2)
304 #define LINK_ADDR                     (IPPROTO_MAX + 3)
305 #define LINK_PPTP                     (IPPROTO_MAX + 4)
306
307     int flags;                   /* indicates special characteristics   */
308
309 /* flag bits */
310 #define LINK_UNKNOWN_DEST_PORT     0x01
311 #define LINK_UNKNOWN_DEST_ADDR     0x02
312 #define LINK_PERMANENT             0x04
313 #define LINK_PARTIALLY_SPECIFIED   0x03 /* logical-or of first two bits */
314 #define LINK_UNFIREWALLED          0x08
315 #define LINK_LAST_LINE_CRLF_TERMED 0x10
316
317     int timestamp;               /* Time link was last accessed         */
318     int expire_time;             /* Expire time for link                */
319
320     int sockfd;                  /* socket descriptor                   */
321
322     LIST_ENTRY(alias_link) list_out; /* Linked list of pointers for     */
323     LIST_ENTRY(alias_link) list_in;  /* input and output lookup tables  */
324
325     union                        /* Auxiliary data                      */
326     {
327         char *frag_ptr;
328         struct in_addr frag_addr;
329         struct tcp_dat *tcp;
330     } data;
331 };
332
333
334
335
336
337 /* Global Variables
338
339     The global variables listed here are only accessed from
340     within alias_db.c and so are prefixed with the static
341     designation.
342 */
343
344 int packetAliasMode;                 /* Mode flags                      */
345                                      /*        - documented in alias.h  */
346
347 static struct in_addr aliasAddress;  /* Address written onto source     */
348                                      /*   field of IP packet.           */
349
350 static struct in_addr targetAddress; /* IP address incoming packets     */
351                                      /*   are sent to if no aliasing    */
352                                      /*   link already exists           */
353
354 static struct in_addr nullAddress;   /* Used as a dummy parameter for   */
355                                      /*   some function calls           */
356 static LIST_HEAD(, alias_link)
357 linkTableOut[LINK_TABLE_OUT_SIZE];   /* Lookup table of pointers to     */
358                                      /*   chains of link records. Each  */
359 static LIST_HEAD(, alias_link)       /*   link record is doubly indexed */
360 linkTableIn[LINK_TABLE_IN_SIZE];     /*   into input and output lookup  */
361                                      /*   tables.                       */
362
363 static int icmpLinkCount;            /* Link statistics                 */
364 static int udpLinkCount;
365 static int tcpLinkCount;
366 static int pptpLinkCount;
367 static int protoLinkCount;
368 static int fragmentIdLinkCount;
369 static int fragmentPtrLinkCount;
370 static int sockCount;
371
372 static int cleanupIndex;             /* Index to chain of link table    */
373                                      /* being inspected for old links   */
374
375 static int timeStamp;                /* System time in seconds for      */
376                                      /* current packet                  */
377
378 static int lastCleanupTime;          /* Last time IncrementalCleanup()  */
379                                      /* was called                      */
380
381 static int houseKeepingResidual;     /* used by HouseKeeping()          */
382
383 static int deleteAllLinks;           /* If equal to zero, DeleteLink()  */
384                                      /* will not remove permanent links */
385
386 static FILE *monitorFile;            /* File descriptor for link        */
387                                      /* statistics monitoring file      */
388
389 static int newDefaultLink;           /* Indicates if a new aliasing     */
390                                      /* link has been created after a   */
391                                      /* call to PacketAliasIn/Out().    */
392
393 #ifndef NO_FW_PUNCH
394 static int fireWallFD = -1;          /* File descriptor to be able to   */
395                                      /* control firewall.  Opened by    */
396                                      /* PacketAliasSetMode on first     */
397                                      /* setting the PKT_ALIAS_PUNCH_FW  */
398                                      /* flag.                           */
399 #endif
400
401
402
403
404
405
406
407 /* Internal utility routines (used only in alias_db.c)
408
409 Lookup 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
415 Miscellaneous:
416     SeqDiff()                -- difference between two TCP sequences
417     ShowAliasStats()         -- send alias statistics to a monitor file
418 */
419
420
421 /* Local prototypes */
422 static u_int StartPointIn(struct in_addr, u_short, int);
423
424 static u_int StartPointOut(struct in_addr, struct in_addr,
425                            u_short, u_short, int);
426
427 static int SeqDiff(u_long, u_long);
428
429 static void ShowAliasStats(void);
430
431 #ifndef NO_FW_PUNCH
432 /* Firewall control */
433 static void InitPunchFW(void);
434 static void UninitPunchFW(void);
435 static void ClearFWHole(struct alias_link *link);
436 #endif
437
438 /* Log file control */
439 static void InitPacketAliasLog(void);
440 static void UninitPacketAliasLog(void);
441
442 static u_int
443 StartPointIn(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
457 static u_int
458 StartPointOut(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
475 static int
476 SeqDiff(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
489 static void
490 ShowAliasStats(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
524 Port Allocation:
525     GetNewPort()             -- find and reserve new alias port number
526     GetSocket()              -- try to allocate a socket for a given port
527
528 Link 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
535 Link search:
536     FindLinkOut()           - find link for outgoing packets
537     FindLinkIn()            - find link for incoming packets
538
539 Port search:
540     FindNewPortGroup()      - find an available group of ports
541 */
542
543 /* Local prototypes */
544 static int GetNewPort(struct alias_link *, int);
545
546 static u_short GetSocket(u_short, int *, int);
547
548 static void CleanupAliasData(void);
549
550 static void IncrementalCleanup(void);
551
552 static void DeleteLink(struct alias_link *);
553
554 static struct alias_link *
555 AddLink(struct in_addr, struct in_addr, struct in_addr,
556         u_short, u_short, int, int);
557
558 static struct alias_link *
559 ReLink(struct alias_link *,
560        struct in_addr, struct in_addr, struct in_addr,
561         u_short, u_short, int, int);
562
563 static struct alias_link *
564 FindLinkOut(struct in_addr, struct in_addr, u_short, u_short, int, int);
565
566 static struct alias_link *
567 FindLinkIn(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
585 static int
586 GetNewPort(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
698 static u_short
699 GetSocket(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
754 int
755 FindNewPortGroup(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
844 static void
845 CleanupAliasData(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
868 static void
869 IncrementalCleanup(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
914 static void
915 DeleteLink(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
993 static struct alias_link *
994 AddLink(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->timestamp         = timeStamp;
1021
1022     /* Expiration time */
1023         switch (link_type)
1024         {
1025         case LINK_ICMP:
1026             link->expire_time = ICMP_EXPIRE_TIME;
1027             break;
1028         case LINK_UDP:
1029             link->expire_time = UDP_EXPIRE_TIME;
1030             break;
1031         case LINK_TCP:
1032             link->expire_time = TCP_EXPIRE_INITIAL;
1033             break;
1034         case LINK_PPTP:
1035             link->flags |= LINK_PERMANENT;      /* no timeout. */
1036             break;
1037         case LINK_FRAGMENT_ID:
1038             link->expire_time = FRAGMENT_ID_EXPIRE_TIME;
1039             break;
1040         case LINK_FRAGMENT_PTR:
1041             link->expire_time = FRAGMENT_PTR_EXPIRE_TIME;
1042             break;
1043         case LINK_ADDR:
1044             break;
1045         default:
1046             link->expire_time = PROTO_EXPIRE_TIME;
1047             break;
1048         }
1049
1050     /* Determine alias flags */
1051         if (dst_addr.s_addr == INADDR_ANY)
1052             link->flags |= LINK_UNKNOWN_DEST_ADDR;
1053         if (dst_port == 0)
1054             link->flags |= LINK_UNKNOWN_DEST_PORT;
1055
1056     /* Determine alias port */
1057         if (GetNewPort(link, alias_port_param) != 0)
1058         {
1059             free(link);
1060             return(NULL);
1061         }
1062
1063     /* Link-type dependent initialization */
1064         switch(link_type)
1065         {
1066             struct tcp_dat  *aux_tcp;
1067
1068             case LINK_ICMP:
1069                 icmpLinkCount++;
1070                 break;
1071             case LINK_UDP:
1072                 udpLinkCount++;
1073                 break;
1074             case LINK_TCP:
1075                 aux_tcp = malloc(sizeof(struct tcp_dat));
1076                 if (aux_tcp != NULL)
1077                 {
1078                     int i;
1079
1080                     tcpLinkCount++;
1081                     aux_tcp->state.in = ALIAS_TCP_STATE_NOT_CONNECTED;
1082                     aux_tcp->state.out = ALIAS_TCP_STATE_NOT_CONNECTED;
1083                     aux_tcp->state.index = 0;
1084                     aux_tcp->state.ack_modified = 0;
1085                     for (i=0; i<N_LINK_TCP_DATA; i++)
1086                         aux_tcp->ack[i].active = 0;
1087                     aux_tcp->fwhole = -1;
1088                     link->data.tcp = aux_tcp;
1089                 }
1090                 else
1091                 {
1092 #ifdef DEBUG
1093                     fprintf(stderr, "PacketAlias/AddLink: ");
1094                     fprintf(stderr, " cannot allocate auxiliary TCP data\n");
1095 #endif
1096                     free(link);
1097                     return (NULL);
1098                 }
1099                 break;
1100             case LINK_PPTP:
1101                 pptpLinkCount++;
1102                 break;
1103             case LINK_FRAGMENT_ID:
1104                 fragmentIdLinkCount++;
1105                 break;
1106             case LINK_FRAGMENT_PTR:
1107                 fragmentPtrLinkCount++;
1108                 break;
1109             case LINK_ADDR:
1110                 break;
1111             default:
1112                 protoLinkCount++;
1113                 break;
1114         }
1115
1116     /* Set up pointers for output lookup table */
1117         start_point = StartPointOut(src_addr, dst_addr,
1118                                     src_port, dst_port, link_type);
1119         LIST_INSERT_HEAD(&linkTableOut[start_point], link, list_out);
1120
1121     /* Set up pointers for input lookup table */
1122         start_point = StartPointIn(alias_addr, link->alias_port, link_type);
1123         LIST_INSERT_HEAD(&linkTableIn[start_point], link, list_in);
1124     }
1125     else
1126     {
1127 #ifdef DEBUG
1128         fprintf(stderr, "PacketAlias/AddLink(): ");
1129         fprintf(stderr, "malloc() call failed.\n");
1130 #endif
1131     }
1132
1133     if (packetAliasMode & PKT_ALIAS_LOG)
1134     {
1135         ShowAliasStats();
1136     }
1137
1138     return(link);
1139 }
1140
1141 static struct alias_link *
1142 ReLink(struct alias_link *old_link,
1143        struct in_addr  src_addr,
1144        struct in_addr  dst_addr,
1145        struct in_addr  alias_addr,
1146        u_short         src_port,
1147        u_short         dst_port,
1148        int             alias_port_param,   /* if less than zero, alias   */
1149        int             link_type)          /* port will be automatically */
1150 {                                          /* chosen. If greater than    */
1151     struct alias_link *new_link;           /* zero, equal to alias port  */
1152
1153     new_link = AddLink(src_addr, dst_addr, alias_addr,
1154                        src_port, dst_port, alias_port_param,
1155                        link_type);
1156 #ifndef NO_FW_PUNCH
1157     if (new_link != NULL &&
1158         old_link->link_type == LINK_TCP &&
1159         old_link->data.tcp->fwhole > 0) {
1160       PunchFWHole(new_link);
1161     }
1162 #endif
1163     DeleteLink(old_link);
1164     return new_link;
1165 }
1166
1167 static struct alias_link *
1168 _FindLinkOut(struct in_addr src_addr,
1169             struct in_addr dst_addr,
1170             u_short src_port,
1171             u_short dst_port,
1172             int link_type,
1173             int replace_partial_links)
1174 {
1175     u_int i;
1176     struct alias_link *link;
1177
1178     i = StartPointOut(src_addr, dst_addr, src_port, dst_port, link_type);
1179     LIST_FOREACH(link, &linkTableOut[i], list_out)
1180     {
1181         if (link->src_addr.s_addr == src_addr.s_addr
1182          && link->server          == NULL
1183          && link->dst_addr.s_addr == dst_addr.s_addr
1184          && link->dst_port        == dst_port
1185          && link->src_port        == src_port
1186          && link->link_type       == link_type)
1187         {
1188             link->timestamp = timeStamp;
1189             break;
1190         }
1191     }
1192
1193 /* Search for partially specified links. */
1194     if (link == NULL && replace_partial_links)
1195     {
1196         if (dst_port != 0 && dst_addr.s_addr != INADDR_ANY)
1197         {
1198             link = _FindLinkOut(src_addr, dst_addr, src_port, 0,
1199                                 link_type, 0);
1200             if (link == NULL)
1201                 link = _FindLinkOut(src_addr, nullAddress, src_port,
1202                                     dst_port, link_type, 0);
1203         }
1204         if (link == NULL &&
1205            (dst_port != 0 || dst_addr.s_addr != INADDR_ANY))
1206         {
1207             link = _FindLinkOut(src_addr, nullAddress, src_port, 0,
1208                                 link_type, 0);
1209         }
1210         if (link != NULL)
1211         {
1212             link = ReLink(link,
1213                           src_addr, dst_addr, link->alias_addr,
1214                           src_port, dst_port, link->alias_port,
1215                           link_type);
1216         }
1217     }
1218
1219     return(link);
1220 }
1221
1222 static struct alias_link *
1223 FindLinkOut(struct in_addr src_addr,
1224             struct in_addr dst_addr,
1225             u_short src_port,
1226             u_short dst_port,
1227             int link_type,
1228             int replace_partial_links)
1229 {
1230     struct alias_link *link;
1231
1232     link = _FindLinkOut(src_addr, dst_addr, src_port, dst_port,
1233                         link_type, replace_partial_links);
1234
1235     if (link == NULL)
1236     {
1237     /* The following allows permanent links to be
1238        specified as using the default source address
1239        (i.e. device interface address) without knowing
1240        in advance what that address is. */
1241         if (aliasAddress.s_addr != 0 &&
1242             src_addr.s_addr == aliasAddress.s_addr)
1243         {
1244             link = _FindLinkOut(nullAddress, dst_addr, src_port, dst_port,
1245                                link_type, replace_partial_links);
1246         }
1247     }
1248
1249     return(link);
1250 }
1251
1252
1253 static struct alias_link *
1254 _FindLinkIn(struct in_addr dst_addr,
1255            struct in_addr  alias_addr,
1256            u_short         dst_port,
1257            u_short         alias_port,
1258            int             link_type,
1259            int             replace_partial_links)
1260 {
1261     int flags_in;
1262     u_int start_point;
1263     struct alias_link *link;
1264     struct alias_link *link_fully_specified;
1265     struct alias_link *link_unknown_all;
1266     struct alias_link *link_unknown_dst_addr;
1267     struct alias_link *link_unknown_dst_port;
1268
1269 /* Initialize pointers */
1270     link_fully_specified  = NULL;
1271     link_unknown_all      = NULL;
1272     link_unknown_dst_addr = NULL;
1273     link_unknown_dst_port = NULL;
1274
1275 /* If either the dest addr or port is unknown, the search
1276    loop will have to know about this. */
1277
1278     flags_in = 0;
1279     if (dst_addr.s_addr == INADDR_ANY)
1280         flags_in |= LINK_UNKNOWN_DEST_ADDR;
1281     if (dst_port == 0)
1282         flags_in |= LINK_UNKNOWN_DEST_PORT;
1283
1284 /* Search loop */
1285     start_point = StartPointIn(alias_addr, alias_port, link_type);
1286     LIST_FOREACH(link, &linkTableIn[start_point], list_in)
1287     {
1288         int flags;
1289
1290         flags = flags_in | link->flags;
1291         if (!(flags & LINK_PARTIALLY_SPECIFIED))
1292         {
1293             if (link->alias_addr.s_addr == alias_addr.s_addr
1294              && link->alias_port        == alias_port
1295              && link->dst_addr.s_addr   == dst_addr.s_addr
1296              && link->dst_port          == dst_port
1297              && link->link_type         == link_type)
1298             {
1299                 link_fully_specified = link;
1300                 break;
1301             }
1302         }
1303         else if ((flags & LINK_UNKNOWN_DEST_ADDR)
1304               && (flags & LINK_UNKNOWN_DEST_PORT))
1305         {
1306             if (link->alias_addr.s_addr == alias_addr.s_addr
1307              && link->alias_port        == alias_port
1308              && link->link_type         == link_type)
1309             {
1310                 if (link_unknown_all == NULL)
1311                     link_unknown_all = link;
1312             }
1313         }
1314         else if (flags & LINK_UNKNOWN_DEST_ADDR)
1315         {
1316             if (link->alias_addr.s_addr == alias_addr.s_addr
1317              && link->alias_port        == alias_port
1318              && link->link_type         == link_type
1319              && link->dst_port          == dst_port)
1320             {
1321                 if (link_unknown_dst_addr == NULL)
1322                     link_unknown_dst_addr = link;
1323             }
1324         }
1325         else if (flags & LINK_UNKNOWN_DEST_PORT)
1326         {
1327             if (link->alias_addr.s_addr == alias_addr.s_addr
1328              && link->alias_port        == alias_port
1329              && link->link_type         == link_type
1330              && link->dst_addr.s_addr   == dst_addr.s_addr)
1331             {
1332                 if (link_unknown_dst_port == NULL)
1333                     link_unknown_dst_port = link;
1334             }
1335         }
1336     }
1337
1338
1339
1340     if (link_fully_specified != NULL)
1341     {
1342         link_fully_specified->timestamp = timeStamp;
1343         link = link_fully_specified;
1344     }
1345     else if (link_unknown_dst_port != NULL)
1346         link = link_unknown_dst_port;
1347     else if (link_unknown_dst_addr != NULL)
1348         link = link_unknown_dst_addr;
1349     else if (link_unknown_all != NULL)
1350         link = link_unknown_all;
1351     else
1352         return (NULL);
1353
1354     if (replace_partial_links &&
1355         (link->flags & LINK_PARTIALLY_SPECIFIED || link->server != NULL))
1356     {
1357         struct in_addr src_addr;
1358         u_short src_port;
1359
1360         if (link->server != NULL) {             /* LSNAT link */
1361             src_addr = link->server->addr;
1362             src_port = link->server->port;
1363             link->server = link->server->next;
1364         } else {
1365             src_addr = link->src_addr;
1366             src_port = link->src_port;
1367         }
1368
1369         link = ReLink(link,
1370                       src_addr, dst_addr, alias_addr,
1371                       src_port, dst_port, alias_port,
1372                       link_type);
1373     }
1374
1375     return (link);
1376 }
1377
1378 static struct alias_link *
1379 FindLinkIn(struct in_addr dst_addr,
1380            struct in_addr alias_addr,
1381            u_short dst_port,
1382            u_short alias_port,
1383            int link_type,
1384            int replace_partial_links)
1385 {
1386     struct alias_link *link;
1387
1388     link = _FindLinkIn(dst_addr, alias_addr, dst_port, alias_port,
1389                        link_type, replace_partial_links);
1390
1391     if (link == NULL)
1392     {
1393     /* The following allows permanent links to be
1394        specified as using the default aliasing address
1395        (i.e. device interface address) without knowing
1396        in advance what that address is. */
1397         if (aliasAddress.s_addr != 0 &&
1398             alias_addr.s_addr == aliasAddress.s_addr)
1399         {
1400             link = _FindLinkIn(dst_addr, nullAddress, dst_port, alias_port,
1401                                link_type, replace_partial_links);
1402         }
1403     }
1404
1405     return(link);
1406 }
1407
1408
1409
1410
1411 /* External routines for finding/adding links
1412
1413 -- "external" means outside alias_db.c, but within alias*.c --
1414
1415     FindIcmpIn(), FindIcmpOut()
1416     FindFragmentIn1(), FindFragmentIn2()
1417     AddFragmentPtrLink(), FindFragmentPtr()
1418     FindProtoIn(), FindProtoOut()
1419     FindUdpTcpIn(), FindUdpTcpOut()
1420     AddPptp(), FindPptpOutByCallId(), FindPptpInByCallId(),
1421     FindPptpOutByPeerCallId(), FindPptpInByPeerCallId()
1422     FindOriginalAddress(), FindAliasAddress()
1423
1424 (prototypes in alias_local.h)
1425 */
1426
1427
1428 struct alias_link *
1429 FindIcmpIn(struct in_addr dst_addr,
1430            struct in_addr alias_addr,
1431            u_short id_alias,
1432            int create)
1433 {
1434     struct alias_link *link;
1435
1436     link = FindLinkIn(dst_addr, alias_addr,
1437                       NO_DEST_PORT, id_alias,
1438                       LINK_ICMP, 0);
1439     if (link == NULL && create && !(packetAliasMode & PKT_ALIAS_DENY_INCOMING))
1440     {
1441         struct in_addr target_addr;
1442
1443         target_addr = FindOriginalAddress(alias_addr);
1444         link = AddLink(target_addr, dst_addr, alias_addr,
1445                        id_alias, NO_DEST_PORT, id_alias,
1446                        LINK_ICMP);
1447     }
1448
1449     return (link);
1450 }
1451
1452
1453 struct alias_link *
1454 FindIcmpOut(struct in_addr src_addr,
1455             struct in_addr dst_addr,
1456             u_short id,
1457             int create)
1458 {
1459     struct alias_link * link;
1460
1461     link = FindLinkOut(src_addr, dst_addr,
1462                        id, NO_DEST_PORT,
1463                        LINK_ICMP, 0);
1464     if (link == NULL && create)
1465     {
1466         struct in_addr alias_addr;
1467
1468         alias_addr = FindAliasAddress(src_addr);
1469         link = AddLink(src_addr, dst_addr, alias_addr,
1470                        id, NO_DEST_PORT, GET_ALIAS_ID,
1471                        LINK_ICMP);
1472     }
1473
1474     return(link);
1475 }
1476
1477
1478 struct alias_link *
1479 FindFragmentIn1(struct in_addr dst_addr,
1480                 struct in_addr alias_addr,
1481                 u_short ip_id)
1482 {
1483     struct alias_link *link;
1484
1485     link = FindLinkIn(dst_addr, alias_addr,
1486                       NO_DEST_PORT, ip_id,
1487                       LINK_FRAGMENT_ID, 0);
1488
1489     if (link == NULL)
1490     {
1491         link = AddLink(nullAddress, dst_addr, alias_addr,
1492                        NO_SRC_PORT, NO_DEST_PORT, ip_id,
1493                        LINK_FRAGMENT_ID);
1494     }
1495
1496     return(link);
1497 }
1498
1499
1500 struct alias_link *
1501 FindFragmentIn2(struct in_addr dst_addr,   /* Doesn't add a link if one */
1502                 struct in_addr alias_addr, /*   is not found.           */
1503                 u_short ip_id)
1504 {
1505     return FindLinkIn(dst_addr, alias_addr,
1506                       NO_DEST_PORT, ip_id,
1507                       LINK_FRAGMENT_ID, 0);
1508 }
1509
1510
1511 struct alias_link *
1512 AddFragmentPtrLink(struct in_addr dst_addr,
1513                    u_short ip_id)
1514 {
1515     return AddLink(nullAddress, dst_addr, nullAddress,
1516                    NO_SRC_PORT, NO_DEST_PORT, ip_id,
1517                    LINK_FRAGMENT_PTR);
1518 }
1519
1520
1521 struct alias_link *
1522 FindFragmentPtr(struct in_addr dst_addr,
1523                 u_short ip_id)
1524 {
1525     return FindLinkIn(dst_addr, nullAddress,
1526                       NO_DEST_PORT, ip_id,
1527                       LINK_FRAGMENT_PTR, 0);
1528 }
1529
1530
1531 struct alias_link *
1532 FindProtoIn(struct in_addr dst_addr,
1533             struct in_addr alias_addr,
1534             u_char proto)
1535 {
1536     struct alias_link *link;
1537
1538     link = FindLinkIn(dst_addr, alias_addr,
1539                       NO_DEST_PORT, 0,
1540                       proto, 1);
1541
1542     if (link == NULL && !(packetAliasMode & PKT_ALIAS_DENY_INCOMING))
1543     {
1544         struct in_addr target_addr;
1545
1546         target_addr = FindOriginalAddress(alias_addr);
1547         link = AddLink(target_addr, dst_addr, alias_addr,
1548                        NO_SRC_PORT, NO_DEST_PORT, 0,
1549                        proto);
1550     }
1551
1552     return (link);
1553 }
1554
1555
1556 struct alias_link *
1557 FindProtoOut(struct in_addr src_addr,
1558              struct in_addr dst_addr,
1559              u_char proto)
1560 {
1561     struct alias_link *link;
1562
1563     link = FindLinkOut(src_addr, dst_addr,
1564                        NO_SRC_PORT, NO_DEST_PORT,
1565                        proto, 1);
1566
1567     if (link == NULL)
1568     {
1569         struct in_addr alias_addr;
1570
1571         alias_addr = FindAliasAddress(src_addr);
1572         link = AddLink(src_addr, dst_addr, alias_addr,
1573                        NO_SRC_PORT, NO_DEST_PORT, 0,
1574                        proto);
1575     }
1576
1577     return (link);
1578 }
1579
1580
1581 struct alias_link *
1582 FindUdpTcpIn(struct in_addr dst_addr,
1583              struct in_addr alias_addr,
1584              u_short        dst_port,
1585              u_short        alias_port,
1586              u_char         proto,
1587              int            create)
1588 {
1589     int link_type;
1590     struct alias_link *link;
1591
1592     switch (proto)
1593     {
1594     case IPPROTO_UDP:
1595         link_type = LINK_UDP;
1596         break;
1597     case IPPROTO_TCP:
1598         link_type = LINK_TCP;
1599         break;
1600     default:
1601         return NULL;
1602         break;
1603     }
1604
1605     link = FindLinkIn(dst_addr, alias_addr,
1606                       dst_port, alias_port,
1607                       link_type, create);
1608
1609     if (link == NULL && create && !(packetAliasMode & PKT_ALIAS_DENY_INCOMING))
1610     {
1611         struct in_addr target_addr;
1612
1613         target_addr = FindOriginalAddress(alias_addr);
1614         link = AddLink(target_addr, dst_addr, alias_addr,
1615                        alias_port, dst_port, alias_port,
1616                        link_type);
1617     }
1618
1619     return(link);
1620 }
1621
1622
1623 struct alias_link *
1624 FindUdpTcpOut(struct in_addr  src_addr,
1625               struct in_addr  dst_addr,
1626               u_short         src_port,
1627               u_short         dst_port,
1628               u_char          proto,
1629               int             create)
1630 {
1631     int link_type;
1632     struct alias_link *link;
1633
1634     switch (proto)
1635     {
1636     case IPPROTO_UDP:
1637         link_type = LINK_UDP;
1638         break;
1639     case IPPROTO_TCP:
1640         link_type = LINK_TCP;
1641         break;
1642     default:
1643         return NULL;
1644         break;
1645     }
1646
1647     link = FindLinkOut(src_addr, dst_addr, src_port, dst_port, link_type, create);
1648
1649     if (link == NULL && create)
1650     {
1651         struct in_addr alias_addr;
1652
1653         alias_addr = FindAliasAddress(src_addr);
1654         link = AddLink(src_addr, dst_addr, alias_addr,
1655                        src_port, dst_port, GET_ALIAS_PORT,
1656                        link_type);
1657     }
1658
1659     return(link);
1660 }
1661
1662
1663 struct alias_link *
1664 AddPptp(struct in_addr  src_addr,
1665         struct in_addr  dst_addr,
1666         struct in_addr  alias_addr,
1667         u_int16_t       src_call_id)
1668 {
1669     struct alias_link *link;
1670
1671     link = AddLink(src_addr, dst_addr, alias_addr,
1672                    src_call_id, 0, GET_ALIAS_PORT,
1673                    LINK_PPTP);
1674
1675     return (link);
1676 }
1677
1678
1679 struct alias_link *
1680 FindPptpOutByCallId(struct in_addr src_addr,
1681                     struct in_addr dst_addr,
1682                     u_int16_t      src_call_id)
1683 {
1684     u_int i;
1685     struct alias_link *link;
1686
1687     i = StartPointOut(src_addr, dst_addr, 0, 0, LINK_PPTP);
1688     LIST_FOREACH(link, &linkTableOut[i], list_out)
1689         if (link->link_type == LINK_PPTP &&
1690             link->src_addr.s_addr == src_addr.s_addr &&
1691             link->dst_addr.s_addr == dst_addr.s_addr &&
1692             link->src_port == src_call_id)
1693                 break;
1694
1695     return (link);
1696 }
1697
1698
1699 struct alias_link *
1700 FindPptpOutByPeerCallId(struct in_addr src_addr,
1701                         struct in_addr dst_addr,
1702                         u_int16_t      dst_call_id)
1703 {
1704     u_int i;
1705     struct alias_link *link;
1706
1707     i = StartPointOut(src_addr, dst_addr, 0, 0, LINK_PPTP);
1708     LIST_FOREACH(link, &linkTableOut[i], list_out)
1709         if (link->link_type == LINK_PPTP &&
1710             link->src_addr.s_addr == src_addr.s_addr &&
1711             link->dst_addr.s_addr == dst_addr.s_addr &&
1712             link->dst_port == dst_call_id)
1713                 break;
1714
1715     return (link);
1716 }
1717
1718
1719 struct alias_link *
1720 FindPptpInByCallId(struct in_addr dst_addr,
1721                    struct in_addr alias_addr,
1722                    u_int16_t      dst_call_id)
1723 {
1724     u_int i;
1725     struct alias_link *link;
1726
1727     i = StartPointIn(alias_addr, 0, LINK_PPTP);
1728     LIST_FOREACH(link, &linkTableIn[i], list_in)
1729         if (link->link_type == LINK_PPTP &&
1730             link->dst_addr.s_addr == dst_addr.s_addr &&
1731             link->alias_addr.s_addr == alias_addr.s_addr &&
1732             link->dst_port == dst_call_id)
1733                 break;
1734
1735     return (link);
1736 }
1737
1738
1739 struct alias_link *
1740 FindPptpInByPeerCallId(struct in_addr dst_addr,
1741                        struct in_addr alias_addr,
1742                        u_int16_t      alias_call_id)
1743 {
1744     struct alias_link *link;
1745
1746     link = FindLinkIn(dst_addr, alias_addr,
1747                       0/* any */, alias_call_id,
1748                       LINK_PPTP, 0);
1749
1750
1751     return (link);
1752 }
1753
1754
1755 struct alias_link *
1756 FindRtspOut(struct in_addr  src_addr,
1757             struct in_addr  dst_addr,
1758             u_short         src_port,
1759             u_short         alias_port,
1760             u_char          proto)
1761 {
1762     int link_type;
1763     struct alias_link *link;
1764
1765     switch (proto)
1766     {
1767     case IPPROTO_UDP:
1768         link_type = LINK_UDP;
1769         break;
1770     case IPPROTO_TCP:
1771         link_type = LINK_TCP;
1772         break;
1773     default:
1774         return NULL;
1775         break;
1776     }
1777
1778     link = FindLinkOut(src_addr, dst_addr, src_port, 0, link_type, 1);
1779
1780     if (link == NULL)
1781     {
1782         struct in_addr alias_addr;
1783
1784         alias_addr = FindAliasAddress(src_addr);
1785         link = AddLink(src_addr, dst_addr, alias_addr,
1786                        src_port, 0, alias_port,
1787                        link_type);
1788     }
1789
1790     return(link);
1791 }
1792
1793
1794 struct in_addr
1795 FindOriginalAddress(struct in_addr alias_addr)
1796 {
1797     struct alias_link *link;
1798
1799     link = FindLinkIn(nullAddress, alias_addr,
1800                       0, 0, LINK_ADDR, 0);
1801     if (link == NULL)
1802     {
1803         newDefaultLink = 1;
1804         if (targetAddress.s_addr == INADDR_ANY)
1805             return alias_addr;
1806         else if (targetAddress.s_addr == INADDR_NONE)
1807             return aliasAddress;
1808         else
1809             return targetAddress;
1810     }
1811     else
1812     {
1813         if (link->server != NULL) {             /* LSNAT link */
1814             struct in_addr src_addr;
1815
1816             src_addr = link->server->addr;
1817             link->server = link->server->next;
1818             return (src_addr);
1819         } else if (link->src_addr.s_addr == INADDR_ANY)
1820             return aliasAddress;
1821         else
1822             return link->src_addr;
1823     }
1824 }
1825
1826
1827 struct in_addr
1828 FindAliasAddress(struct in_addr original_addr)
1829 {
1830     struct alias_link *link;
1831
1832     link = FindLinkOut(original_addr, nullAddress,
1833                        0, 0, LINK_ADDR, 0);
1834     if (link == NULL)
1835     {
1836         return aliasAddress;
1837     }
1838     else
1839     {
1840         if (link->alias_addr.s_addr == INADDR_ANY)
1841             return aliasAddress;
1842         else
1843             return link->alias_addr;
1844     }
1845 }
1846
1847
1848 /* External routines for getting or changing link data
1849    (external to alias_db.c, but internal to alias*.c)
1850
1851     SetFragmentData(), GetFragmentData()
1852     SetFragmentPtr(), GetFragmentPtr()
1853     SetStateIn(), SetStateOut(), GetStateIn(), GetStateOut()
1854     GetOriginalAddress(), GetDestAddress(), GetAliasAddress()
1855     GetOriginalPort(), GetAliasPort()
1856     SetAckModified(), GetAckModified()
1857     GetDeltaAckIn(), GetDeltaSeqOut(), AddSeq()
1858     SetLastLineCrlfTermed(), GetLastLineCrlfTermed()
1859     SetDestCallId()
1860 */
1861
1862
1863 void
1864 SetFragmentAddr(struct alias_link *link, struct in_addr src_addr)
1865 {
1866     link->data.frag_addr = src_addr;
1867 }
1868
1869
1870 void
1871 GetFragmentAddr(struct alias_link *link, struct in_addr *src_addr)
1872 {
1873     *src_addr = link->data.frag_addr;
1874 }
1875
1876
1877 void
1878 SetFragmentPtr(struct alias_link *link, char *fptr)
1879 {
1880     link->data.frag_ptr = fptr;
1881 }
1882
1883
1884 void
1885 GetFragmentPtr(struct alias_link *link, char **fptr)
1886 {
1887    *fptr = link->data.frag_ptr;
1888 }
1889
1890
1891 void
1892 SetStateIn(struct alias_link *link, int state)
1893 {
1894     /* TCP input state */
1895     switch (state) {
1896     case ALIAS_TCP_STATE_DISCONNECTED:
1897         if (link->data.tcp->state.out != ALIAS_TCP_STATE_CONNECTED)
1898             link->expire_time = TCP_EXPIRE_DEAD;
1899         else
1900             link->expire_time = TCP_EXPIRE_SINGLEDEAD;
1901         break;
1902     case ALIAS_TCP_STATE_CONNECTED:
1903         if (link->data.tcp->state.out == ALIAS_TCP_STATE_CONNECTED)
1904             link->expire_time = TCP_EXPIRE_CONNECTED;
1905         break;
1906     default:
1907         abort();
1908     }
1909     link->data.tcp->state.in = state;
1910 }
1911
1912
1913 void
1914 SetStateOut(struct alias_link *link, int state)
1915 {
1916     /* TCP output state */
1917     switch (state) {
1918     case ALIAS_TCP_STATE_DISCONNECTED:
1919         if (link->data.tcp->state.in != ALIAS_TCP_STATE_CONNECTED)
1920             link->expire_time = TCP_EXPIRE_DEAD;
1921         else
1922             link->expire_time = TCP_EXPIRE_SINGLEDEAD;
1923         break;
1924     case ALIAS_TCP_STATE_CONNECTED:
1925         if (link->data.tcp->state.in == ALIAS_TCP_STATE_CONNECTED)
1926             link->expire_time = TCP_EXPIRE_CONNECTED;
1927         break;
1928     default:
1929         abort();
1930     }
1931     link->data.tcp->state.out = state;
1932 }
1933
1934
1935 int
1936 GetStateIn(struct alias_link *link)
1937 {
1938     /* TCP input state */
1939     return link->data.tcp->state.in;
1940 }
1941
1942
1943 int
1944 GetStateOut(struct alias_link *link)
1945 {
1946     /* TCP output state */
1947     return link->data.tcp->state.out;
1948 }
1949
1950
1951 struct in_addr
1952 GetOriginalAddress(struct alias_link *link)
1953 {
1954     if (link->src_addr.s_addr == INADDR_ANY)
1955         return aliasAddress;
1956     else
1957         return(link->src_addr);
1958 }
1959
1960
1961 struct in_addr
1962 GetDestAddress(struct alias_link *link)
1963 {
1964     return(link->dst_addr);
1965 }
1966
1967
1968 struct in_addr
1969 GetAliasAddress(struct alias_link *link)
1970 {
1971     if (link->alias_addr.s_addr == INADDR_ANY)
1972         return aliasAddress;
1973     else
1974         return link->alias_addr;
1975 }
1976
1977
1978 struct in_addr
1979 GetDefaultAliasAddress()
1980 {
1981     return aliasAddress;
1982 }
1983
1984
1985 void
1986 SetDefaultAliasAddress(struct in_addr alias_addr)
1987 {
1988     aliasAddress = alias_addr;
1989 }
1990
1991
1992 u_short
1993 GetOriginalPort(struct alias_link *link)
1994 {
1995     return(link->src_port);
1996 }
1997
1998
1999 u_short
2000 GetAliasPort(struct alias_link *link)
2001 {
2002     return(link->alias_port);
2003 }
2004
2005 #ifndef NO_FW_PUNCH
2006 static u_short
2007 GetDestPort(struct alias_link *link)
2008 {
2009     return(link->dst_port);
2010 }
2011 #endif
2012
2013 void
2014 SetAckModified(struct alias_link *link)
2015 {
2016 /* Indicate that ACK numbers have been modified in a TCP connection */
2017     link->data.tcp->state.ack_modified = 1;
2018 }
2019
2020
2021 struct in_addr
2022 GetProxyAddress(struct alias_link *link)
2023 {
2024     return link->proxy_addr;
2025 }
2026
2027
2028 void
2029 SetProxyAddress(struct alias_link *link, struct in_addr addr)
2030 {
2031     link->proxy_addr = addr;
2032 }
2033
2034
2035 u_short
2036 GetProxyPort(struct alias_link *link)
2037 {
2038     return link->proxy_port;
2039 }
2040
2041
2042 void
2043 SetProxyPort(struct alias_link *link, u_short port)
2044 {
2045     link->proxy_port = port;
2046 }
2047
2048
2049 int
2050 GetAckModified(struct alias_link *link)
2051 {
2052 /* See if ACK numbers have been modified */
2053     return link->data.tcp->state.ack_modified;
2054 }
2055
2056
2057 int
2058 GetDeltaAckIn(struct ip *pip, struct alias_link *link)
2059 {
2060 /*
2061 Find out how much the ACK number has been altered for an incoming
2062 TCP packet.  To do this, a circular list of ACK numbers where the TCP
2063 packet size was altered is searched.
2064 */
2065
2066     int i;
2067     struct tcphdr *tc;
2068     int delta, ack_diff_min;
2069     u_long ack;
2070
2071     tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
2072     ack      = tc->th_ack;
2073
2074     delta = 0;
2075     ack_diff_min = -1;
2076     for (i=0; i<N_LINK_TCP_DATA; i++)
2077     {
2078         struct ack_data_record x;
2079
2080         x = link->data.tcp->ack[i];
2081         if (x.active == 1)
2082         {
2083             int ack_diff;
2084
2085             ack_diff = SeqDiff(x.ack_new, ack);
2086             if (ack_diff >= 0)
2087             {
2088                 if (ack_diff_min >= 0)
2089                 {
2090                     if (ack_diff < ack_diff_min)
2091                     {
2092                         delta = x.delta;
2093                         ack_diff_min = ack_diff;
2094                     }
2095                 }
2096                 else
2097                 {
2098                     delta = x.delta;
2099                     ack_diff_min = ack_diff;
2100                 }
2101             }
2102         }
2103     }
2104     return (delta);
2105 }
2106
2107
2108 int
2109 GetDeltaSeqOut(struct ip *pip, struct alias_link *link)
2110 {
2111 /*
2112 Find out how much the sequence number has been altered for an outgoing
2113 TCP packet.  To do this, a circular list of ACK numbers where the TCP
2114 packet size was altered is searched.
2115 */
2116
2117     int i;
2118     struct tcphdr *tc;
2119     int delta, seq_diff_min;
2120     u_long seq;
2121
2122     tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
2123     seq = tc->th_seq;
2124
2125     delta = 0;
2126     seq_diff_min = -1;
2127     for (i=0; i<N_LINK_TCP_DATA; i++)
2128     {
2129         struct ack_data_record x;
2130
2131         x = link->data.tcp->ack[i];
2132         if (x.active == 1)
2133         {
2134             int seq_diff;
2135
2136             seq_diff = SeqDiff(x.ack_old, seq);
2137             if (seq_diff >= 0)
2138             {
2139                 if (seq_diff_min >= 0)
2140                 {
2141                     if (seq_diff < seq_diff_min)
2142                     {
2143                         delta = x.delta;
2144                         seq_diff_min = seq_diff;
2145                     }
2146                 }
2147                 else
2148                 {
2149                     delta = x.delta;
2150                     seq_diff_min = seq_diff;
2151                 }
2152             }
2153         }
2154     }
2155     return (delta);
2156 }
2157
2158
2159 void
2160 AddSeq(struct ip *pip, struct alias_link *link, int delta)
2161 {
2162 /*
2163 When a TCP packet has been altered in length, save this
2164 information in a circular list.  If enough packets have
2165 been altered, then this list will begin to overwrite itself.
2166 */
2167
2168     struct tcphdr *tc;
2169     struct ack_data_record x;
2170     int hlen, tlen, dlen;
2171     int i;
2172
2173     tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
2174
2175     hlen = (pip->ip_hl + tc->th_off) << 2;
2176     tlen = ntohs(pip->ip_len);
2177     dlen = tlen - hlen;
2178
2179     x.ack_old = htonl(ntohl(tc->th_seq) + dlen);
2180     x.ack_new = htonl(ntohl(tc->th_seq) + dlen + delta);
2181     x.delta = delta;
2182     x.active = 1;
2183
2184     i = link->data.tcp->state.index;
2185     link->data.tcp->ack[i] = x;
2186
2187     i++;
2188     if (i == N_LINK_TCP_DATA)
2189         link->data.tcp->state.index = 0;
2190     else
2191         link->data.tcp->state.index = i;
2192 }
2193
2194 void
2195 SetExpire(struct alias_link *link, int expire)
2196 {
2197     if (expire == 0)
2198     {
2199         link->flags &= ~LINK_PERMANENT;
2200         DeleteLink(link);
2201     }
2202     else if (expire == -1)
2203     {
2204         link->flags |= LINK_PERMANENT;
2205     }
2206     else if (expire > 0)
2207     {
2208         link->expire_time = expire;
2209     }
2210     else
2211     {
2212 #ifdef DEBUG
2213         fprintf(stderr, "PacketAlias/SetExpire(): ");
2214         fprintf(stderr, "error in expire parameter\n");
2215 #endif
2216     }
2217 }
2218
2219 void
2220 ClearCheckNewLink(void)
2221 {
2222     newDefaultLink = 0;
2223 }
2224
2225 void
2226 SetLastLineCrlfTermed(struct alias_link *link, int yes)
2227 {
2228
2229     if (yes)
2230         link->flags |= LINK_LAST_LINE_CRLF_TERMED;
2231     else
2232         link->flags &= ~LINK_LAST_LINE_CRLF_TERMED;
2233 }
2234
2235 int
2236 GetLastLineCrlfTermed(struct alias_link *link)
2237 {
2238
2239     return (link->flags & LINK_LAST_LINE_CRLF_TERMED);
2240 }
2241
2242 void
2243 SetDestCallId(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
2269 void
2270 HouseKeeping(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 */
2322 static void
2323 InitPacketAliasLog(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. */
2336 static void
2337 UninitPacketAliasLog(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 */
2370 struct alias_link *
2371 PacketAliasRedirectPort(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 */
2415 int
2416 PacketAliasAddServer(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 */
2446 struct alias_link *
2447 PacketAliasRedirectProto(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 */
2474 struct alias_link *
2475 PacketAliasRedirectAddr(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
2500 void
2501 PacketAliasRedirectDelete(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
2512 void
2513 PacketAliasSetAddress(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
2523 void
2524 PacketAliasSetTarget(struct in_addr target_addr)
2525 {
2526     targetAddress = target_addr;
2527 }
2528
2529
2530 void
2531 PacketAliasInit(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
2579 void
2580 PacketAliasUninit(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 */
2592 unsigned int
2593 PacketAliasSetMode(
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
2626 int
2627 PacketAliasCheckNewLink(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 <net/ipfw/ip_fw.h>
2643 #include <string.h>
2644 #include <err.h>
2645
2646 #if IPFW2               /* support for new firewall code */
2647 /*
2648  * helper function, updates the pointer to cmd with the length
2649  * of the current command, and also cleans up the first word of
2650  * the new command in case it has been clobbered before.
2651  */
2652 static ipfw_insn *
2653 next_cmd(ipfw_insn *cmd)
2654 {
2655     cmd += F_LEN(cmd);
2656     bzero(cmd, sizeof(*cmd));
2657     return cmd;
2658 }
2659
2660 /*
2661  * A function to fill simple commands of size 1.
2662  * Existing flags are preserved.
2663  */
2664 static ipfw_insn *
2665 fill_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode, int size,
2666          int flags, u_int16_t arg)
2667 {
2668     cmd->opcode = opcode;
2669     cmd->len =  ((cmd->len | flags) & (F_NOT | F_OR)) | (size & F_LEN_MASK);
2670     cmd->arg1 = arg;
2671     return next_cmd(cmd);
2672 }
2673
2674 static ipfw_insn *
2675 fill_ip(ipfw_insn *cmd1, enum ipfw_opcodes opcode, u_int32_t addr)
2676 {
2677     ipfw_insn_ip *cmd = (ipfw_insn_ip *)cmd1;
2678
2679     cmd->addr.s_addr = addr;
2680     return fill_cmd(cmd1, opcode, F_INSN_SIZE(ipfw_insn_u32), 0, 0);
2681 }
2682
2683 static ipfw_insn *
2684 fill_one_port(ipfw_insn *cmd1, enum ipfw_opcodes opcode, u_int16_t port)
2685 {
2686     ipfw_insn_u16 *cmd = (ipfw_insn_u16 *)cmd1;
2687
2688     cmd->ports[0] = cmd->ports[1] = port;
2689     return fill_cmd(cmd1, opcode, F_INSN_SIZE(ipfw_insn_u16), 0, 0);
2690 }
2691
2692 static int
2693 fill_rule(void *buf, int bufsize, int rulenum,
2694         enum ipfw_opcodes action, int proto,
2695         struct in_addr sa, u_int16_t sp, struct in_addr da, u_int16_t dp)
2696 {
2697     struct ip_fw *rule = (struct ip_fw *)buf;
2698     ipfw_insn *cmd = (ipfw_insn *)rule->cmd;
2699
2700     bzero(buf, bufsize);
2701     rule->rulenum = rulenum;
2702
2703     cmd = fill_cmd(cmd, O_PROTO, F_INSN_SIZE(ipfw_insn), 0, proto);
2704     cmd = fill_ip(cmd, O_IP_SRC, sa.s_addr);
2705     cmd = fill_one_port(cmd, O_IP_SRCPORT, sp);
2706     cmd = fill_ip(cmd, O_IP_DST, da.s_addr);
2707     cmd = fill_one_port(cmd, O_IP_DSTPORT, dp);
2708
2709     rule->act_ofs = (u_int32_t *)cmd - (u_int32_t *)rule->cmd;
2710     cmd = fill_cmd(cmd, action, F_INSN_SIZE(ipfw_insn), 0, 0);
2711
2712     rule->cmd_len = (u_int32_t *)cmd - (u_int32_t *)rule->cmd;
2713
2714     return ((void *)cmd - buf);
2715 }
2716 #endif /* IPFW2 */
2717
2718 static void ClearAllFWHoles(void);
2719
2720 static int fireWallBaseNum;     /* The first firewall entry free for our use */
2721 static int fireWallNumNums;     /* How many entries can we use? */
2722 static int fireWallActiveNum;   /* Which entry did we last use? */
2723 static char *fireWallField;     /* bool array for entries */
2724
2725 #define fw_setfield(field, num)                         \
2726 do {                                                    \
2727     (field)[(num) - fireWallBaseNum] = 1;               \
2728 } /*lint -save -e717 */ while(0) /*lint -restore */
2729 #define fw_clrfield(field, num)                         \
2730 do {                                                    \
2731     (field)[(num) - fireWallBaseNum] = 0;               \
2732 } /*lint -save -e717 */ while(0) /*lint -restore */
2733 #define fw_tstfield(field, num) ((field)[(num) - fireWallBaseNum])
2734
2735 static void
2736 InitPunchFW(void) {
2737     fireWallField = malloc(fireWallNumNums);
2738     if (fireWallField) {
2739         memset(fireWallField, 0, fireWallNumNums);
2740         if (fireWallFD < 0) {
2741             fireWallFD = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
2742         }
2743         ClearAllFWHoles();
2744         fireWallActiveNum = fireWallBaseNum;
2745     }
2746 }
2747
2748 static void
2749 UninitPunchFW(void) {
2750     ClearAllFWHoles();
2751     if (fireWallFD >= 0)
2752         close(fireWallFD);
2753     fireWallFD = -1;
2754     if (fireWallField)
2755         free(fireWallField);
2756     fireWallField = NULL;
2757     packetAliasMode &= ~PKT_ALIAS_PUNCH_FW;
2758 }
2759
2760 /* Make a certain link go through the firewall */
2761 void
2762 PunchFWHole(struct alias_link *link) {
2763     int r;                      /* Result code */
2764     struct ip_fw rule;          /* On-the-fly built rule */
2765     int fwhole;                 /* Where to punch hole */
2766
2767 /* Don't do anything unless we are asked to */
2768     if ( !(packetAliasMode & PKT_ALIAS_PUNCH_FW) ||
2769          fireWallFD < 0 ||
2770          link->link_type != LINK_TCP)
2771         return;
2772
2773     memset(&rule, 0, sizeof rule);
2774
2775 /** Build rule **/
2776
2777     /* Find empty slot */
2778     for (fwhole = fireWallActiveNum;
2779          fwhole < fireWallBaseNum + fireWallNumNums &&
2780              fw_tstfield(fireWallField, fwhole);
2781          fwhole++)
2782         ;
2783     if (fwhole == fireWallBaseNum + fireWallNumNums) {
2784         for (fwhole = fireWallBaseNum;
2785              fwhole < fireWallActiveNum &&
2786                  fw_tstfield(fireWallField, fwhole);
2787              fwhole++)
2788             ;
2789         if (fwhole == fireWallActiveNum) {
2790             /* No rule point empty - we can't punch more holes. */
2791             fireWallActiveNum = fireWallBaseNum;
2792 #ifdef DEBUG
2793             fprintf(stderr, "libalias: Unable to create firewall hole!\n");
2794 #endif
2795             return;
2796         }
2797     }
2798     /* Start next search at next position */
2799     fireWallActiveNum = fwhole+1;
2800
2801     /*
2802      * generate two rules of the form
2803      *
2804      *  add fwhole accept tcp from OAddr OPort to DAddr DPort
2805      *  add fwhole accept tcp from DAddr DPort to OAddr OPort
2806      */
2807 #if IPFW2
2808     if (GetOriginalPort(link) != 0 && GetDestPort(link) != 0) {
2809         u_int32_t rulebuf[255];
2810         int i;
2811
2812         i = fill_rule(rulebuf, sizeof(rulebuf), fwhole,
2813                 O_ACCEPT, IPPROTO_TCP,
2814                 GetOriginalAddress(link), ntohs(GetOriginalPort(link)),
2815                 GetDestAddress(link), ntohs(GetDestPort(link)) );
2816         r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, rulebuf, i);
2817         if (r)
2818                 err(1, "alias punch inbound(1) setsockopt(IP_FW_ADD)");
2819
2820         i = fill_rule(rulebuf, sizeof(rulebuf), fwhole,
2821                 O_ACCEPT, IPPROTO_TCP,
2822                 GetDestAddress(link), ntohs(GetDestPort(link)),
2823                 GetOriginalAddress(link), ntohs(GetOriginalPort(link)) );
2824         r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, rulebuf, i);
2825         if (r)
2826                 err(1, "alias punch inbound(2) setsockopt(IP_FW_ADD)");
2827     }
2828 #else   /* !IPFW2, old code to generate ipfw rule */
2829
2830     /* Build generic part of the two rules */
2831     rule.fw_number = fwhole;
2832     IP_FW_SETNSRCP(&rule, 1);   /* Number of source ports. */
2833     IP_FW_SETNDSTP(&rule, 1);   /* Number of destination ports. */
2834     rule.fw_flg = IP_FW_F_ACCEPT | IP_FW_F_IN | IP_FW_F_OUT;
2835     rule.fw_prot = IPPROTO_TCP;
2836     rule.fw_smsk.s_addr = INADDR_BROADCAST;
2837     rule.fw_dmsk.s_addr = INADDR_BROADCAST;
2838
2839     /* Build and apply specific part of the rules */
2840     rule.fw_src = GetOriginalAddress(link);
2841     rule.fw_dst = GetDestAddress(link);
2842     rule.fw_uar.fw_pts[0] = ntohs(GetOriginalPort(link));
2843     rule.fw_uar.fw_pts[1] = ntohs(GetDestPort(link));
2844
2845     /* Skip non-bound links - XXX should not be strictly necessary,
2846        but seems to leave hole if not done.  Leak of non-bound links?
2847        (Code should be left even if the problem is fixed - it is a
2848        clear optimization) */
2849     if (rule.fw_uar.fw_pts[0] != 0 && rule.fw_uar.fw_pts[1] != 0) {
2850         r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, &rule, sizeof rule);
2851 #ifdef DEBUG
2852         if (r)
2853             err(1, "alias punch inbound(1) setsockopt(IP_FW_ADD)");
2854 #endif
2855         rule.fw_src = GetDestAddress(link);
2856         rule.fw_dst = GetOriginalAddress(link);
2857         rule.fw_uar.fw_pts[0] = ntohs(GetDestPort(link));
2858         rule.fw_uar.fw_pts[1] = ntohs(GetOriginalPort(link));
2859         r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, &rule, sizeof rule);
2860 #ifdef DEBUG
2861         if (r)
2862             err(1, "alias punch inbound(2) setsockopt(IP_FW_ADD)");
2863 #endif
2864     }
2865 #endif /* !IPFW2 */
2866 /* Indicate hole applied */
2867     link->data.tcp->fwhole = fwhole;
2868     fw_setfield(fireWallField, fwhole);
2869 }
2870
2871 /* Remove a hole in a firewall associated with a particular alias
2872    link.  Calling this too often is harmless. */
2873 static void
2874 ClearFWHole(struct alias_link *link) {
2875     if (link->link_type == LINK_TCP) {
2876         int fwhole =  link->data.tcp->fwhole; /* Where is the firewall hole? */
2877         struct ip_fw rule;
2878
2879         if (fwhole < 0)
2880             return;
2881
2882         memset(&rule, 0, sizeof rule); /* useless for ipfw2 */
2883 #if IPFW2
2884         while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL,
2885                     &fwhole, sizeof fwhole))
2886             ;
2887 #else /* !IPFW2 */
2888         rule.fw_number = fwhole;
2889         while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL,
2890                     &rule, sizeof rule))
2891             ;
2892 #endif /* !IPFW2 */
2893         fw_clrfield(fireWallField, fwhole);
2894         link->data.tcp->fwhole = -1;
2895     }
2896 }
2897
2898 /* Clear out the entire range dedicated to firewall holes. */
2899 static void
2900 ClearAllFWHoles(void) {
2901     struct ip_fw rule;          /* On-the-fly built rule */
2902     int i;
2903
2904     if (fireWallFD < 0)
2905         return;
2906
2907     memset(&rule, 0, sizeof rule);
2908     for (i = fireWallBaseNum; i < fireWallBaseNum + fireWallNumNums; i++) {
2909 #if IPFW2
2910         int r = i;
2911         while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL, &r, sizeof r))
2912             ;
2913 #else /* !IPFW2 */
2914         rule.fw_number = i;
2915         while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule))
2916             ;
2917 #endif /* !IPFW2 */
2918     }
2919     memset(fireWallField, 0, fireWallNumNums);
2920 }
2921 #endif
2922
2923 void
2924 PacketAliasSetFWBase(unsigned int base, unsigned int num) {
2925 #ifndef NO_FW_PUNCH
2926     fireWallBaseNum = base;
2927     fireWallNumNums = num;
2928 #endif
2929 }