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