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