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