]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netinet/libalias/alias_db.c
This commit was generated by cvs2svn to compensate for changes in r44852,
[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 #ifdef DEBUG
583         fprintf(stderr, "PacketAlias/GetNewPort(): ");
584         fprintf(stderr, "input parameter error\n");
585 #endif
586         return(-1);
587     }
588
589
590 /* Port number search */
591     for (i=0; i<max_trials; i++)
592     {
593         int go_ahead;
594         struct alias_link *search_result;
595
596         search_result = FindLinkIn(link->dst_addr, link->alias_addr,
597                                    link->dst_port, port_net,
598                                    link->link_type, 0);
599
600         if (search_result == NULL)
601             go_ahead = 1;
602         else if (!(link->flags          & LINK_PARTIALLY_SPECIFIED)
603                && (search_result->flags & LINK_PARTIALLY_SPECIFIED))
604             go_ahead = 1;
605         else
606             go_ahead = 0;
607
608         if (go_ahead)
609         {
610             if ((packetAliasMode && PKT_ALIAS_USE_SOCKETS)
611              && (link->flags & LINK_PARTIALLY_SPECIFIED))
612             {
613                 if (GetSocket(port_net, &link->sockfd, link->link_type))
614                 {
615                     link->alias_port = port_net;
616                     return(0);
617                 }
618             }
619             else
620             {
621                 link->alias_port = port_net;
622                 return(0);
623             }
624         }
625
626         port_sys = random() & ALIAS_PORT_MASK;
627         port_sys += ALIAS_PORT_BASE;
628         port_net = htons(port_sys);
629     }
630
631 #ifdef DEBUG
632     fprintf(stderr, "PacketAlias/GetnewPort(): ");
633     fprintf(stderr, "could not find free port\n");
634 #endif
635
636     return(-1);
637 }
638
639
640 static u_short 
641 GetSocket(u_short port_net, int *sockfd, int link_type)
642 {
643     int err;
644     int sock;
645     struct sockaddr_in sock_addr;
646
647     if (link_type == LINK_TCP)
648         sock = socket(AF_INET, SOCK_STREAM, 0);
649     else if (link_type == LINK_UDP)
650         sock = socket(AF_INET, SOCK_DGRAM, 0);
651     else
652     {
653 #ifdef DEBUG
654         fprintf(stderr, "PacketAlias/GetSocket(): ");
655         fprintf(stderr, "incorrect link type\n");
656 #endif
657         return(0);
658     }
659
660     if (sock < 0)
661     {
662 #ifdef DEBUG
663         fprintf(stderr, "PacketAlias/GetSocket(): ");
664         fprintf(stderr, "socket() error %d\n", *sockfd);
665 #endif
666         return(0);
667     }
668
669     sock_addr.sin_family = AF_INET;
670     sock_addr.sin_addr.s_addr = htonl(INADDR_ANY);
671     sock_addr.sin_port = port_net;
672
673     err = bind(sock,
674                (struct sockaddr *) &sock_addr,
675                sizeof(sock_addr));
676     if (err == 0)
677     {
678         sockCount++;
679         *sockfd = sock;
680         return(1);
681     }
682     else
683     {
684         close(sock);
685         return(0);
686     }
687 }
688
689
690 static void
691 CleanupAliasData(void)
692 {
693     struct alias_link *link;
694     int i, icount;
695
696     icount = 0;
697     for (i=0; i<LINK_TABLE_OUT_SIZE; i++)
698     {
699         link = linkTableOut[i];
700         while (link != NULL)
701         {
702             struct alias_link *link_next;
703             link_next = link->next_out;
704             icount++;
705             DeleteLink(link);
706             link = link_next;
707         }
708     }
709
710     cleanupIndex =0;
711 }
712
713
714 static void
715 IncrementalCleanup(void)
716 {
717     int icount;
718     struct alias_link *link;
719
720     icount = 0;
721     link = linkTableOut[cleanupIndex++];
722     while (link != NULL)
723     {
724         int idelta;
725         struct alias_link *link_next;
726
727         link_next = link->next_out; 
728         idelta = timeStamp - link->timestamp;
729         switch (link->link_type)
730         {
731             case LINK_ICMP:
732             case LINK_UDP:
733             case LINK_FRAGMENT_ID:
734             case LINK_FRAGMENT_PTR:
735                 if (idelta > link->expire_time)
736                 {
737                     DeleteLink(link);
738                     icount++;
739                 }
740                 break;
741             case LINK_TCP:
742                 if (idelta > link->expire_time)
743                 {
744                     struct tcp_dat *tcp_aux;
745
746                     tcp_aux = link->data.tcp; 
747                     if (tcp_aux->state.in  != ALIAS_TCP_STATE_CONNECTED
748                      || tcp_aux->state.out != ALIAS_TCP_STATE_CONNECTED)
749                     {
750                         DeleteLink(link);
751                         icount++;
752                     }
753                 }
754                 break;
755         }
756         link = link_next;
757     }
758
759     if (cleanupIndex == LINK_TABLE_OUT_SIZE)
760         cleanupIndex = 0;
761 }
762
763 void
764 DeleteLink(struct alias_link *link)
765 {
766     struct alias_link *link_last;
767     struct alias_link *link_next;
768
769 /* Don't do anything if the link is marked permanent */
770     if (deleteAllLinks == 0 && link->flags & LINK_PERMANENT)
771         return;
772
773 #ifndef NO_FW_PUNCH
774 /* Delete associatied firewall hole, if any */
775     ClearFWHole(link);
776 #endif
777
778 /* Adjust output table pointers */
779     link_last = link->last_out;
780     link_next = link->next_out;
781
782     if (link_last != NULL)
783         link_last->next_out = link_next;
784     else
785         linkTableOut[link->start_point_out] = link_next;
786
787     if (link_next != NULL)
788         link_next->last_out = link_last;
789
790 /* Adjust input table pointers */
791     link_last = link->last_in;
792     link_next = link->next_in;
793
794     if (link_last != NULL)
795         link_last->next_in = link_next;
796     else
797         linkTableIn[link->start_point_in] = link_next;
798
799     if (link_next != NULL)
800         link_next->last_in = link_last;
801
802 /* Close socket, if one has been allocated */
803     if (link->sockfd != -1)
804     {
805         sockCount--;
806         close(link->sockfd);
807     }
808
809 /* Link-type dependent cleanup */
810     switch(link->link_type)
811     {
812         case LINK_ICMP:
813             icmpLinkCount--;
814             break;
815         case LINK_UDP:
816             udpLinkCount--;
817             break;
818         case LINK_TCP:
819             tcpLinkCount--;
820             if (link->data.tcp != NULL)
821                 free(link->data.tcp);
822             break;
823         case LINK_FRAGMENT_ID:
824             fragmentIdLinkCount--;
825             break;
826         case LINK_FRAGMENT_PTR:
827             fragmentPtrLinkCount--;
828             if (link->data.frag_ptr != NULL)
829                 free(link->data.frag_ptr);
830             break;
831     }
832
833 /* Free memory */
834     free(link);
835
836 /* Write statistics, if logging enabled */
837     if (packetAliasMode & PKT_ALIAS_LOG)
838     {
839         ShowAliasStats();
840     }
841 }
842
843
844 static struct alias_link *
845 AddLink(struct in_addr  src_addr,
846         struct in_addr  dst_addr,
847         struct in_addr  alias_addr,
848         u_short         src_port,
849         u_short         dst_port,
850         int             alias_port_param,  /* if less than zero, alias   */
851         int             link_type)         /* port will be automatically */
852 {                                          /* chosen. If greater than    */
853     u_int start_point;                     /* zero, equal to alias port  */
854     struct alias_link *link;
855     struct alias_link *first_link;
856
857     link = malloc(sizeof(struct alias_link));
858     if (link != NULL)
859     {
860     /* If either the aliasing address or source address are
861        equal to the default device address (equal to the
862        global variable aliasAddress), then set the alias
863        address field of the link record to zero */
864
865         if (src_addr.s_addr == aliasAddress.s_addr)
866             src_addr.s_addr = 0;
867
868         if (alias_addr.s_addr == aliasAddress.s_addr)
869             alias_addr.s_addr = 0;
870
871     /* Basic initialization */
872         link->src_addr          = src_addr;
873         link->dst_addr          = dst_addr;
874         link->alias_addr        = alias_addr;
875         link->proxy_addr.s_addr = 0;
876         link->src_port          = src_port;
877         link->dst_port          = dst_port;
878         link->proxy_port        = 0;
879         link->link_type         = link_type;
880         link->sockfd            = -1;
881         link->flags             = 0;
882         link->timestamp         = timeStamp;
883
884     /* Expiration time */
885         switch (link_type)
886         {
887         case LINK_ICMP:
888             link->expire_time = ICMP_EXPIRE_TIME;
889             break;
890         case LINK_UDP:
891             link->expire_time = UDP_EXPIRE_TIME;
892             break;
893         case LINK_TCP:
894             link->expire_time = TCP_EXPIRE_INITIAL;
895             break;
896         case LINK_FRAGMENT_ID:
897             link->expire_time = FRAGMENT_ID_EXPIRE_TIME;
898             break;
899         case LINK_FRAGMENT_PTR:
900             link->expire_time = FRAGMENT_PTR_EXPIRE_TIME;
901             break;
902         }
903
904     /* Determine alias flags */
905         if (dst_addr.s_addr == 0)
906             link->flags |= LINK_UNKNOWN_DEST_ADDR;
907         if (dst_port == 0)
908             link->flags |= LINK_UNKNOWN_DEST_PORT;
909
910     /* Determine alias port */
911         if (GetNewPort(link, alias_port_param) != 0)
912         {
913             free(link);
914             return(NULL);
915         }
916
917     /* Set up pointers for output lookup table */
918         start_point = StartPointOut(src_addr, dst_addr, 
919                                     src_port, dst_port, link_type);
920         first_link = linkTableOut[start_point];
921
922         link->last_out        = NULL;
923         link->next_out        = first_link;
924         link->start_point_out = start_point;
925
926         if (first_link != NULL)
927             first_link->last_out = link;
928
929         linkTableOut[start_point] = link;
930
931     /* Set up pointers for input lookup table */
932         start_point = StartPointIn(alias_addr, link->alias_port, link_type); 
933         first_link = linkTableIn[start_point];
934
935         link->last_in        = NULL;
936         link->next_in        = first_link;
937         link->start_point_in = start_point;
938
939         if (first_link != NULL)
940             first_link->last_in = link;
941
942         linkTableIn[start_point] = link;
943
944     /* Link-type dependent initialization */
945         switch(link_type)
946         {
947             struct tcp_dat  *aux_tcp;
948
949             case LINK_ICMP:
950                 icmpLinkCount++;
951                 break;
952             case LINK_UDP:
953                 udpLinkCount++;
954                 break;
955             case LINK_TCP:
956                 aux_tcp = malloc(sizeof(struct tcp_dat));
957                 link->data.tcp = aux_tcp;
958                 if (aux_tcp != NULL)
959                 {
960                     int i;
961
962                     tcpLinkCount++;
963                     aux_tcp->state.in = ALIAS_TCP_STATE_NOT_CONNECTED;
964                     aux_tcp->state.out = ALIAS_TCP_STATE_NOT_CONNECTED;
965                     aux_tcp->state.index = 0;
966                     aux_tcp->state.ack_modified = 0;
967                     for (i=0; i<N_LINK_TCP_DATA; i++)
968                         aux_tcp->ack[i].active = 0;
969                     aux_tcp->fwhole = -1;
970                 }
971                 else
972                 {
973 #ifdef DEBUG
974                     fprintf(stderr, "PacketAlias/AddLink: ");
975                     fprintf(stderr, " cannot allocate auxiliary TCP data\n");
976 #endif
977                 }
978                 break;
979             case LINK_FRAGMENT_ID:
980                 fragmentIdLinkCount++;
981                 break;
982             case LINK_FRAGMENT_PTR:
983                 fragmentPtrLinkCount++;
984                 break;
985         }
986     }
987     else
988     {
989 #ifdef DEBUG
990         fprintf(stderr, "PacketAlias/AddLink(): ");
991         fprintf(stderr, "malloc() call failed.\n");
992 #endif
993     }
994
995     if (packetAliasMode & PKT_ALIAS_LOG)
996     {
997         ShowAliasStats();
998     }
999
1000     return(link);
1001 }
1002
1003 static struct alias_link *
1004 ReLink(struct alias_link *old_link,
1005        struct in_addr  src_addr,
1006        struct in_addr  dst_addr,
1007        struct in_addr  alias_addr,
1008        u_short         src_port,
1009        u_short         dst_port,
1010        int             alias_port_param,   /* if less than zero, alias   */
1011        int             link_type)          /* port will be automatically */
1012 {                                          /* chosen. If greater than    */
1013     struct alias_link *new_link;           /* zero, equal to alias port  */
1014
1015     new_link = AddLink(src_addr, dst_addr, alias_addr,
1016                        src_port, dst_port, alias_port_param,
1017                        link_type);
1018 #ifndef NO_FW_PUNCH
1019     if (new_link != NULL &&
1020         old_link->link_type == LINK_TCP &&
1021         old_link->data.tcp &&
1022         old_link->data.tcp->fwhole > 0) {
1023       PunchFWHole(new_link);
1024     }
1025 #endif
1026     DeleteLink(old_link);
1027     return new_link;
1028 }
1029
1030 static struct alias_link *
1031 FindLinkOut(struct in_addr src_addr,
1032             struct in_addr dst_addr,
1033             u_short src_port,
1034             u_short dst_port,
1035             int link_type)
1036 {
1037     u_int i;
1038     struct alias_link *link;
1039
1040     if (src_addr.s_addr == aliasAddress.s_addr)
1041         src_addr.s_addr = 0;
1042
1043     i = StartPointOut(src_addr, dst_addr, src_port, dst_port, link_type);
1044     link = linkTableOut[i];
1045     while (link != NULL)
1046     {
1047         if (link->src_addr.s_addr == src_addr.s_addr
1048          && link->dst_addr.s_addr == dst_addr.s_addr
1049          && link->dst_port        == dst_port
1050          && link->src_port        == src_port
1051          && link->link_type       == link_type)
1052         {
1053             link->timestamp = timeStamp;
1054             break;
1055         }
1056         link = link->next_out;
1057     }
1058
1059     return(link);
1060 }
1061
1062
1063 struct alias_link *
1064 FindLinkIn(struct in_addr  dst_addr,
1065            struct in_addr  alias_addr,
1066            u_short         dst_port,
1067            u_short         alias_port,
1068            int             link_type,
1069            int             replace_partial_links)
1070 {
1071     int flags_in;
1072     u_int start_point;
1073     struct alias_link *link;
1074     struct alias_link *link_fully_specified;
1075     struct alias_link *link_unknown_all;
1076     struct alias_link *link_unknown_dst_addr;
1077     struct alias_link *link_unknown_dst_port;
1078
1079 /* Initialize pointers */
1080     link_fully_specified  = NULL;
1081     link_unknown_all      = NULL;
1082     link_unknown_dst_addr = NULL;
1083     link_unknown_dst_port = NULL;
1084
1085 /* If either the dest addr or port is unknown, the search
1086    loop will have to know about this. */
1087
1088     flags_in = 0;
1089     if (dst_addr.s_addr == 0)
1090         flags_in |= LINK_UNKNOWN_DEST_ADDR;
1091     if (dst_port == 0)
1092         flags_in |= LINK_UNKNOWN_DEST_PORT;
1093
1094 /* The following allows permanent links to be
1095    be specified as using the default aliasing address
1096    (i.e. device interface address) without knowing
1097    in advance what that address is. */
1098
1099     if (alias_addr.s_addr == aliasAddress.s_addr)
1100         alias_addr.s_addr = 0;
1101
1102 /* Search loop */
1103     start_point = StartPointIn(alias_addr, alias_port, link_type);
1104     link = linkTableIn[start_point];
1105     while (link != NULL)
1106     {
1107         int flags;
1108
1109         flags = flags_in | link->flags;
1110         if (!(flags & LINK_PARTIALLY_SPECIFIED))
1111         {
1112             if (link->alias_addr.s_addr == alias_addr.s_addr
1113              && link->alias_port        == alias_port 
1114              && link->dst_addr.s_addr   == dst_addr.s_addr
1115              && link->dst_port          == dst_port
1116              && link->link_type         == link_type)
1117             {
1118                 link_fully_specified = link;
1119                 break;
1120             }
1121         }
1122         else if ((flags & LINK_UNKNOWN_DEST_ADDR)
1123               && (flags & LINK_UNKNOWN_DEST_PORT))
1124         {
1125             if (link->alias_addr.s_addr == alias_addr.s_addr
1126              && link->alias_port        == alias_port
1127              && link->link_type         == link_type)
1128             {
1129                 if (link_unknown_all == NULL)
1130                     link_unknown_all = link;
1131             }
1132         }
1133         else if (flags & LINK_UNKNOWN_DEST_ADDR)
1134         {
1135             if (link->alias_addr.s_addr == alias_addr.s_addr
1136              && link->alias_port        == alias_port
1137              && link->link_type         == link_type
1138              && link->dst_port          == dst_port)
1139             {
1140                 if (link_unknown_dst_addr == NULL)
1141                     link_unknown_dst_addr = link;
1142             }
1143         }
1144         else if (flags & LINK_UNKNOWN_DEST_PORT)
1145         {
1146             if (link->alias_addr.s_addr == alias_addr.s_addr
1147              && link->alias_port        == alias_port
1148              && link->link_type         == link_type
1149              && link->dst_addr.s_addr   == dst_addr.s_addr)
1150             {
1151                 if (link_unknown_dst_port == NULL)
1152                     link_unknown_dst_port = link;
1153             }
1154         }
1155         link = link->next_in;
1156     }
1157
1158
1159
1160     if (link_fully_specified != NULL)
1161     {
1162         return link_fully_specified;
1163     }
1164     else if (link_unknown_dst_port != NULL)
1165     {
1166         return replace_partial_links
1167           ? ReLink(link_unknown_dst_port,
1168                    link_unknown_dst_port->src_addr, dst_addr, alias_addr,
1169                    link_unknown_dst_port->src_port, dst_port, alias_port,
1170                    link_type)
1171           : link_unknown_dst_port;
1172     }
1173     else if (link_unknown_dst_addr != NULL)
1174     {
1175         return replace_partial_links
1176           ? ReLink(link_unknown_dst_addr,
1177                    link_unknown_dst_addr->src_addr, dst_addr, alias_addr,
1178                    link_unknown_dst_addr->src_port, dst_port, alias_port,
1179                    link_type)
1180           : link_unknown_dst_addr;
1181     }
1182     else if (link_unknown_all != NULL)
1183     {
1184         return replace_partial_links
1185           ? ReLink(link_unknown_all,
1186                    link_unknown_all->src_addr, dst_addr, alias_addr,
1187                    link_unknown_all->src_port, dst_port, alias_port,
1188                    link_type)
1189           : link_unknown_all;
1190     }
1191     else
1192     {
1193         return(NULL);
1194     }
1195 }
1196
1197
1198
1199
1200 /* External routines for finding/adding links
1201
1202 -- "external" means outside alias_db.c, but within alias*.c --
1203
1204     FindIcmpIn(), FindIcmpOut()
1205     FindFragmentIn1(), FindFragmentIn2()
1206     AddFragmentPtrLink(), FindFragmentPtr()
1207     FindUdpTcpIn(), FindUdpTcpOut()
1208     FindOriginalAddress(), FindAliasAddress()
1209
1210 (prototypes in alias_local.h)
1211 */
1212
1213
1214 struct alias_link *
1215 FindIcmpIn(struct in_addr dst_addr,
1216            struct in_addr alias_addr,
1217            u_short id_alias)
1218 {
1219     return FindLinkIn(dst_addr, alias_addr,
1220                       NO_DEST_PORT, id_alias,
1221                       LINK_ICMP, 0);
1222 }
1223
1224
1225 struct alias_link *
1226 FindIcmpOut(struct in_addr src_addr,
1227             struct in_addr dst_addr,
1228             u_short id)
1229 {
1230     struct alias_link * link;
1231
1232     link = FindLinkOut(src_addr, dst_addr,
1233                        id, NO_DEST_PORT,
1234                        LINK_ICMP);
1235     if (link == NULL)
1236     {
1237         struct in_addr alias_addr;
1238
1239         alias_addr = FindAliasAddress(src_addr);
1240         link = AddLink(src_addr, dst_addr, alias_addr,
1241                        id, NO_DEST_PORT, GET_ALIAS_ID,
1242                        LINK_ICMP);
1243     }
1244
1245     return(link);
1246 }
1247
1248
1249 struct alias_link *
1250 FindFragmentIn1(struct in_addr dst_addr,
1251                 struct in_addr alias_addr,
1252                 u_short ip_id)
1253 {
1254     struct alias_link *link;
1255
1256     link = FindLinkIn(dst_addr, alias_addr,
1257                       NO_DEST_PORT, ip_id,
1258                       LINK_FRAGMENT_ID, 0);
1259
1260     if (link == NULL)
1261     {
1262         link = AddLink(nullAddress, dst_addr, alias_addr,
1263                        NO_SRC_PORT, NO_DEST_PORT, ip_id,
1264                        LINK_FRAGMENT_ID);
1265     }
1266
1267     return(link);
1268 }
1269
1270
1271 struct alias_link *
1272 FindFragmentIn2(struct in_addr dst_addr,   /* Doesn't add a link if one */
1273                 struct in_addr alias_addr, /*   is not found.           */
1274                 u_short ip_id)
1275 {
1276     return FindLinkIn(dst_addr, alias_addr,
1277                       NO_DEST_PORT, ip_id,
1278                       LINK_FRAGMENT_ID, 0);
1279 }
1280
1281
1282 struct alias_link *
1283 AddFragmentPtrLink(struct in_addr dst_addr,
1284                    u_short ip_id)
1285 {
1286     return AddLink(nullAddress, dst_addr, nullAddress,
1287                    NO_SRC_PORT, NO_DEST_PORT, ip_id,
1288                    LINK_FRAGMENT_PTR);
1289 }
1290
1291
1292 struct alias_link *
1293 FindFragmentPtr(struct in_addr dst_addr,
1294                 u_short ip_id)
1295 {
1296     return FindLinkIn(dst_addr, nullAddress,
1297                       NO_DEST_PORT, ip_id,
1298                       LINK_FRAGMENT_PTR, 0);
1299 }
1300
1301
1302 struct alias_link *
1303 FindUdpTcpIn(struct in_addr dst_addr,
1304              struct in_addr alias_addr,
1305              u_short        dst_port,
1306              u_short        alias_port,
1307              u_char         proto)
1308 {
1309     int link_type;
1310     struct alias_link *link;
1311
1312     switch (proto)
1313     {
1314     case IPPROTO_UDP:
1315         link_type = LINK_UDP;
1316         break;
1317     case IPPROTO_TCP:
1318         link_type = LINK_TCP;
1319         break;
1320     default:
1321         return NULL;
1322         break;
1323     }
1324
1325     link = FindLinkIn(dst_addr, alias_addr,
1326                       dst_port, alias_port,
1327                       link_type, 1);
1328
1329     if (!(packetAliasMode & PKT_ALIAS_DENY_INCOMING)
1330      && !(packetAliasMode & PKT_ALIAS_PROXY_ONLY)
1331      && link == NULL)
1332     {
1333         struct in_addr target_addr;
1334
1335         target_addr = FindOriginalAddress(alias_addr);
1336         link = AddLink(target_addr, dst_addr, alias_addr,
1337                        alias_port, dst_port, alias_port,
1338                        link_type);
1339     }
1340
1341     return(link);
1342 }
1343
1344
1345 struct alias_link * 
1346 FindUdpTcpOut(struct in_addr  src_addr,
1347               struct in_addr  dst_addr,
1348               u_short         src_port,
1349               u_short         dst_port,
1350               u_char          proto)
1351 {
1352     int link_type;
1353     struct alias_link *link;
1354
1355     switch (proto)
1356     {
1357     case IPPROTO_UDP:
1358         link_type = LINK_UDP;
1359         break;
1360     case IPPROTO_TCP:
1361         link_type = LINK_TCP;
1362         break;
1363     default:
1364         return NULL;
1365         break;
1366     }
1367
1368     link = FindLinkOut(src_addr, dst_addr, src_port, dst_port, link_type);
1369
1370     if (link == NULL)
1371     {
1372         struct in_addr alias_addr;
1373
1374         alias_addr = FindAliasAddress(src_addr);
1375         link = AddLink(src_addr, dst_addr, alias_addr,
1376                        src_port, dst_port, GET_ALIAS_PORT,
1377                        link_type);
1378     }
1379
1380     return(link);
1381 }
1382
1383
1384 struct in_addr
1385 FindOriginalAddress(struct in_addr alias_addr)
1386 {
1387     struct alias_link *link;
1388     
1389     link = FindLinkIn(nullAddress, alias_addr,
1390                       0, 0, LINK_ADDR, 0);
1391     if (link == NULL)
1392     {
1393         newDefaultLink = 1;
1394         if (targetAddress.s_addr != 0)
1395             return targetAddress;
1396         else
1397             return alias_addr;
1398     }
1399     else
1400     {
1401         if (link->src_addr.s_addr == 0)
1402             return aliasAddress;
1403         else
1404             return link->src_addr;
1405     }
1406 }
1407
1408
1409 struct in_addr
1410 FindAliasAddress(struct in_addr original_addr)
1411 {
1412     struct alias_link *link;
1413     
1414     link = FindLinkOut(original_addr, nullAddress,
1415                        0, 0, LINK_ADDR);
1416     if (link == NULL)
1417     {
1418         return aliasAddress;
1419     }
1420     else
1421     {
1422         if (link->alias_addr.s_addr == 0)
1423             return aliasAddress;
1424         else
1425             return link->alias_addr;
1426     }
1427 }
1428
1429
1430 /* External routines for getting or changing link data
1431    (external to alias_db.c, but internal to alias*.c)
1432
1433     SetFragmentData(), GetFragmentData()
1434     SetFragmentPtr(), GetFragmentPtr()
1435     SetStateIn(), SetStateOut(), GetStateIn(), GetStateOut()
1436     GetOriginalAddress(), GetDestAddress(), GetAliasAddress()
1437     GetOriginalPort(), GetAliasPort()
1438     SetAckModified(), GetAckModified()
1439     GetDeltaAckIn(), GetDeltaSeqOut(), AddSeq()
1440 */
1441
1442
1443 void
1444 SetFragmentAddr(struct alias_link *link, struct in_addr src_addr)
1445 {
1446     link->data.frag_addr = src_addr;
1447 }
1448
1449
1450 void
1451 GetFragmentAddr(struct alias_link *link, struct in_addr *src_addr)
1452 {
1453     *src_addr = link->data.frag_addr;
1454 }
1455
1456
1457 void
1458 SetFragmentPtr(struct alias_link *link, char *fptr)
1459 {
1460     link->data.frag_ptr = fptr;
1461 }
1462
1463
1464 void
1465 GetFragmentPtr(struct alias_link *link, char **fptr)
1466 {
1467    *fptr = link->data.frag_ptr;
1468 }
1469
1470
1471 void
1472 SetStateIn(struct alias_link *link, int state)
1473 {
1474     /* TCP input state */
1475     switch (state) {
1476     case ALIAS_TCP_STATE_DISCONNECTED:
1477         if (link->data.tcp->state.out != ALIAS_TCP_STATE_CONNECTED) {
1478             link->expire_time = TCP_EXPIRE_DEAD;
1479         } else {
1480             link->expire_time = TCP_EXPIRE_SINGLEDEAD;
1481         }
1482         link->data.tcp->state.in = state;
1483         break;
1484     case ALIAS_TCP_STATE_CONNECTED:
1485         link->expire_time = TCP_EXPIRE_CONNECTED;
1486         /*FALLTHROUGH*/
1487     case ALIAS_TCP_STATE_NOT_CONNECTED:
1488         link->data.tcp->state.in = state;
1489         break;
1490     default:
1491         abort();
1492     }
1493 }
1494
1495
1496 void
1497 SetStateOut(struct alias_link *link, int state)
1498 {
1499     /* TCP output state */
1500     switch (state) {
1501     case ALIAS_TCP_STATE_DISCONNECTED:
1502         if (link->data.tcp->state.in != ALIAS_TCP_STATE_CONNECTED) {
1503             link->expire_time = TCP_EXPIRE_DEAD;
1504         } else {
1505             link->expire_time = TCP_EXPIRE_SINGLEDEAD;
1506         }
1507         link->data.tcp->state.out = state;
1508         break;
1509     case ALIAS_TCP_STATE_CONNECTED:
1510         link->expire_time = TCP_EXPIRE_CONNECTED;
1511         /*FALLTHROUGH*/
1512     case ALIAS_TCP_STATE_NOT_CONNECTED:
1513         link->data.tcp->state.out = state;
1514         break;
1515     default:
1516         abort();
1517     }
1518 }
1519
1520
1521 int
1522 GetStateIn(struct alias_link *link)
1523 {
1524     /* TCP input state */
1525     return link->data.tcp->state.in;
1526 }
1527
1528
1529 int
1530 GetStateOut(struct alias_link *link)
1531 {
1532     /* TCP output state */
1533     return link->data.tcp->state.out;
1534 }
1535
1536
1537 struct in_addr
1538 GetOriginalAddress(struct alias_link *link)
1539 {
1540     if (link->src_addr.s_addr == 0)
1541         return aliasAddress;
1542     else
1543         return(link->src_addr);
1544 }
1545
1546
1547 struct in_addr
1548 GetDestAddress(struct alias_link *link)
1549 {
1550     return(link->dst_addr);
1551 }
1552
1553
1554 struct in_addr
1555 GetAliasAddress(struct alias_link *link)
1556 {
1557     if (link->alias_addr.s_addr == 0)
1558         return aliasAddress;
1559     else
1560         return link->alias_addr;
1561 }
1562
1563
1564 struct in_addr
1565 GetDefaultAliasAddress()
1566 {
1567     return aliasAddress;
1568 }
1569
1570
1571 void
1572 SetDefaultAliasAddress(struct in_addr alias_addr)
1573 {
1574     aliasAddress = alias_addr;
1575 }
1576
1577
1578 u_short
1579 GetOriginalPort(struct alias_link *link)
1580 {
1581     return(link->src_port);
1582 }
1583
1584
1585 u_short
1586 GetAliasPort(struct alias_link *link)
1587 {
1588     return(link->alias_port);
1589 }
1590
1591 u_short
1592 GetDestPort(struct alias_link *link)
1593 {
1594     return(link->dst_port);
1595 }
1596
1597 void
1598 SetAckModified(struct alias_link *link)
1599 {
1600 /* Indicate that ack numbers have been modified in a TCP connection */
1601     link->data.tcp->state.ack_modified = 1;
1602 }
1603
1604
1605 struct in_addr
1606 GetProxyAddress(struct alias_link *link)
1607 {
1608     return link->proxy_addr;
1609 }
1610
1611
1612 void
1613 SetProxyAddress(struct alias_link *link, struct in_addr addr)
1614 {
1615     link->proxy_addr = addr;
1616 }
1617
1618
1619 u_short
1620 GetProxyPort(struct alias_link *link)
1621 {
1622     return link->proxy_port;
1623 }
1624
1625
1626 void
1627 SetProxyPort(struct alias_link *link, u_short port)
1628 {
1629     link->proxy_port = port;
1630 }
1631
1632
1633 int
1634 GetAckModified(struct alias_link *link)
1635 {
1636 /* See if ack numbers have been modified */
1637     return link->data.tcp->state.ack_modified;
1638 }
1639
1640
1641 int
1642 GetDeltaAckIn(struct ip *pip, struct alias_link *link)
1643 {
1644 /*
1645 Find out how much the ack number has been altered for an incoming
1646 TCP packet.  To do this, a circular list is ack numbers where the TCP
1647 packet size was altered is searched. 
1648 */
1649
1650     int i;
1651     struct tcphdr *tc;
1652     int delta, ack_diff_min;
1653     u_long ack;
1654
1655     tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
1656     ack      = tc->th_ack;
1657
1658     delta = 0;
1659     ack_diff_min = -1;
1660     for (i=0; i<N_LINK_TCP_DATA; i++)
1661     {
1662         struct ack_data_record x;
1663
1664         x = link->data.tcp->ack[i];
1665         if (x.active == 1)
1666         {
1667             int ack_diff;
1668
1669             ack_diff = SeqDiff(x.ack_new, ack);
1670             if (ack_diff >= 0)
1671             {
1672                 if (ack_diff_min >= 0)
1673                 {
1674                     if (ack_diff < ack_diff_min)
1675                     {
1676                         delta = x.delta;
1677                         ack_diff_min = ack_diff;
1678                     }
1679                 }
1680                 else
1681                 {
1682                     delta = x.delta;
1683                     ack_diff_min = ack_diff;
1684                 }
1685             }
1686         }
1687     }
1688     return (delta);
1689 }
1690
1691
1692 int
1693 GetDeltaSeqOut(struct ip *pip, struct alias_link *link)
1694 {
1695 /*
1696 Find out how much the seq number has been altered for an outgoing
1697 TCP packet.  To do this, a circular list is ack numbers where the TCP
1698 packet size was altered is searched. 
1699 */
1700
1701     int i;
1702     struct tcphdr *tc;
1703     int delta, seq_diff_min;
1704     u_long seq;
1705
1706     tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
1707     seq = tc->th_seq;
1708
1709     delta = 0;
1710     seq_diff_min = -1;
1711     for (i=0; i<N_LINK_TCP_DATA; i++)
1712     {
1713         struct ack_data_record x;
1714
1715         x = link->data.tcp->ack[i];
1716         if (x.active == 1)
1717         {
1718             int seq_diff;
1719
1720             seq_diff = SeqDiff(x.ack_old, seq);
1721             if (seq_diff >= 0)
1722             {
1723                 if (seq_diff_min >= 0)
1724                 {
1725                     if (seq_diff < seq_diff_min)
1726                     {
1727                         delta = x.delta;
1728                         seq_diff_min = seq_diff;
1729                     }
1730                 }
1731                 else
1732                 {
1733                     delta = x.delta;
1734                     seq_diff_min = seq_diff;
1735                 }
1736             }
1737         }
1738     }
1739     return (delta);
1740 }
1741
1742
1743 void
1744 AddSeq(struct ip *pip, struct alias_link *link, int delta)
1745 {
1746 /*
1747 When a TCP packet has been altered in length, save this
1748 information in a circular list.  If enough packets have
1749 been altered, then this list will begin to overwrite itself.
1750 */
1751
1752     struct tcphdr *tc;
1753     struct ack_data_record x;
1754     int hlen, tlen, dlen;
1755     int i;
1756
1757     tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
1758
1759     hlen = (pip->ip_hl + tc->th_off) << 2;
1760     tlen = ntohs(pip->ip_len);
1761     dlen = tlen - hlen;
1762
1763     x.ack_old = htonl(ntohl(tc->th_seq) + dlen);
1764     x.ack_new = htonl(ntohl(tc->th_seq) + dlen + delta);
1765     x.delta = delta;
1766     x.active = 1;
1767
1768     i = link->data.tcp->state.index;
1769     link->data.tcp->ack[i] = x;
1770
1771     i++;
1772     if (i == N_LINK_TCP_DATA)
1773         link->data.tcp->state.index = 0;
1774     else
1775         link->data.tcp->state.index = i;
1776 }
1777
1778 void
1779 SetExpire(struct alias_link *link, int expire)
1780 {
1781     if (expire == 0)
1782     {
1783         link->flags &= ~LINK_PERMANENT;
1784         DeleteLink(link);
1785     }
1786     else if (expire == -1)
1787     {
1788         link->flags |= LINK_PERMANENT;
1789     }
1790     else if (expire > 0)
1791     {
1792         link->expire_time = expire;
1793     }
1794     else
1795     {
1796 #ifdef DEBUG
1797         fprintf(stderr, "PacketAlias/SetExpire(): ");
1798         fprintf(stderr, "error in expire parameter\n");
1799 #endif
1800     }
1801 }
1802
1803 void
1804 ClearCheckNewLink(void)
1805 {
1806     newDefaultLink = 0;
1807 }
1808
1809
1810 /* Miscellaneous Functions
1811
1812     HouseKeeping()
1813     InitPacketAliasLog()
1814     UninitPacketAliasLog()
1815 */
1816
1817 /*
1818     Whenever an outgoing or incoming packet is handled, HouseKeeping()
1819     is called to find and remove timed-out aliasing links.  Logic exists
1820     to sweep through the entire table and linked list structure
1821     every 60 seconds.
1822
1823     (prototype in alias_local.h)
1824 */
1825
1826 void
1827 HouseKeeping(void)
1828 {
1829     int i, n, n100;
1830     struct timeval tv;
1831     struct timezone tz;
1832
1833     /*
1834      * Save system time (seconds) in global variable timeStamp for
1835      * use by other functions. This is done so as not to unnecessarily
1836      * waste timeline by making system calls.
1837      */
1838     gettimeofday(&tv, &tz);
1839     timeStamp = tv.tv_sec;
1840
1841     /* Compute number of spokes (output table link chains) to cover */
1842     n100  = LINK_TABLE_OUT_SIZE * 100 + houseKeepingResidual;
1843     n100 *= timeStamp - lastCleanupTime;
1844     n100 /= ALIAS_CLEANUP_INTERVAL_SECS;
1845
1846     n = n100/100;
1847
1848     /* Handle different cases */
1849     if (n > ALIAS_CLEANUP_MAX_SPOKES)
1850     {
1851         n = ALIAS_CLEANUP_MAX_SPOKES;
1852         lastCleanupTime = timeStamp;
1853         houseKeepingResidual = 0;
1854
1855         for (i=0; i<n; i++)
1856             IncrementalCleanup();
1857     }
1858     else if (n > 0)
1859     {
1860         lastCleanupTime = timeStamp;
1861         houseKeepingResidual = n100 - 100*n;
1862
1863         for (i=0; i<n; i++)
1864             IncrementalCleanup();
1865     }
1866     else if (n < 0)
1867     {
1868 #ifdef DEBUG
1869         fprintf(stderr, "PacketAlias/HouseKeeping(): ");
1870         fprintf(stderr, "something unexpected in time values\n");
1871 #endif
1872         lastCleanupTime = timeStamp;
1873         houseKeepingResidual = 0;
1874     }
1875 }
1876
1877
1878 /* Init the log file and enable logging */
1879 static void
1880 InitPacketAliasLog(void)
1881 {
1882    if ((~packetAliasMode & PKT_ALIAS_LOG)
1883     && (monitorFile = fopen("/var/log/alias.log", "w")))
1884    {
1885       packetAliasMode |= PKT_ALIAS_LOG;
1886       fprintf(monitorFile,
1887       "PacketAlias/InitPacketAliasLog: Packet alias logging enabled.\n");
1888    }
1889 }
1890
1891
1892 /* Close the log-file and disable logging. */
1893 static void
1894 UninitPacketAliasLog(void)
1895 {
1896     if (monitorFile) {
1897         fclose(monitorFile);
1898         monitorFile = NULL;
1899     }
1900     packetAliasMode &= ~PKT_ALIAS_LOG;
1901 }
1902
1903
1904
1905
1906
1907
1908 /* Outside world interfaces
1909
1910 -- "outside world" means other than alias*.c routines --
1911
1912     PacketAliasRedirectPort()
1913     PacketAliasRedirectAddr()
1914     PacketAliasRedirectDelete()
1915     PacketAliasSetAddress()
1916     PacketAliasInit()
1917     PacketAliasUninit()
1918     PacketAliasSetMode()
1919
1920 (prototypes in alias.h)
1921 */
1922
1923 /* Redirection from a specific public addr:port to a
1924    a private addr:port */
1925 struct alias_link *
1926 PacketAliasRedirectPort(struct in_addr src_addr,   u_short src_port,
1927                         struct in_addr dst_addr,   u_short dst_port,
1928                         struct in_addr alias_addr, u_short alias_port,
1929                         u_char proto)
1930 {
1931     int link_type;
1932     struct alias_link *link;
1933
1934     switch(proto)
1935     {
1936     case IPPROTO_UDP:
1937         link_type = LINK_UDP;
1938         break;
1939     case IPPROTO_TCP:
1940         link_type = LINK_TCP;
1941         break;
1942     default:
1943 #ifdef DEBUG
1944         fprintf(stderr, "PacketAliasRedirectPort(): ");
1945         fprintf(stderr, "only TCP and UDP protocols allowed\n");
1946 #endif
1947         return NULL;
1948     }
1949
1950     link = AddLink(src_addr, dst_addr, alias_addr,
1951                    src_port, dst_port, alias_port,
1952                    link_type);
1953
1954     if (link != NULL)
1955     {
1956         link->flags |= LINK_PERMANENT;
1957     }
1958 #ifdef DEBUG
1959     else
1960     {
1961         fprintf(stderr, "PacketAliasRedirectPort(): " 
1962                         "call to AddLink() failed\n");
1963     }
1964 #endif
1965
1966     return link;
1967 }
1968
1969 /* Translate PPTP packets to a machine on the inside
1970  */
1971 int
1972 PacketAliasPptp(struct in_addr src_addr)
1973 {
1974
1975     pptpAliasAddr = src_addr;           /* Address of the inside PPTP machine */
1976     pptpAliasFlag = src_addr.s_addr != INADDR_NONE;
1977
1978     return 1;
1979 }
1980
1981 int GetPptpAlias (struct in_addr* alias_addr)
1982 {
1983     if (pptpAliasFlag)
1984         *alias_addr = pptpAliasAddr;
1985
1986     return pptpAliasFlag;
1987 }
1988
1989 /* Static address translation */
1990 struct alias_link *
1991 PacketAliasRedirectAddr(struct in_addr src_addr,
1992                         struct in_addr alias_addr)
1993 {
1994     struct alias_link *link;
1995
1996     link = AddLink(src_addr, nullAddress, alias_addr,
1997                    0, 0, 0,
1998                    LINK_ADDR);
1999
2000     if (link != NULL)
2001     {
2002         link->flags |= LINK_PERMANENT;
2003     }
2004 #ifdef DEBUG
2005     else
2006     {
2007         fprintf(stderr, "PacketAliasRedirectAddr(): " 
2008                         "call to AddLink() failed\n");
2009     }
2010 #endif
2011
2012     return link;
2013 }
2014
2015
2016 void
2017 PacketAliasRedirectDelete(struct alias_link *link)
2018 {
2019 /* This is a dangerous function to put in the API,
2020    because an invalid pointer can crash the program. */
2021
2022     deleteAllLinks = 1;
2023     DeleteLink(link);
2024     deleteAllLinks = 0;
2025 }
2026
2027
2028 void
2029 PacketAliasSetAddress(struct in_addr addr)
2030 {
2031     if (packetAliasMode & PKT_ALIAS_RESET_ON_ADDR_CHANGE
2032      && aliasAddress.s_addr != addr.s_addr)
2033         CleanupAliasData();
2034
2035     aliasAddress = addr;
2036 }
2037
2038
2039 void
2040 PacketAliasSetTarget(struct in_addr target_addr)
2041 {
2042     targetAddress = target_addr;
2043 }
2044
2045
2046 void
2047 PacketAliasInit(void)
2048 {
2049     int i;
2050     struct timeval tv;
2051     struct timezone tz;
2052     static int firstCall = 1;
2053
2054     if (firstCall == 1)
2055     {
2056         gettimeofday(&tv, &tz);
2057         timeStamp = tv.tv_sec;
2058         lastCleanupTime = tv.tv_sec;
2059         houseKeepingResidual = 0;
2060
2061         for (i=0; i<LINK_TABLE_OUT_SIZE; i++)
2062             linkTableOut[i] = NULL;
2063         for (i=0; i<LINK_TABLE_IN_SIZE; i++)
2064             linkTableIn[i] = NULL;
2065
2066         atexit(PacketAliasUninit);
2067         firstCall = 0;
2068     }
2069     else
2070     {
2071         deleteAllLinks = 1;
2072         CleanupAliasData();
2073         deleteAllLinks = 0;
2074     }
2075
2076     aliasAddress.s_addr = 0;
2077     targetAddress.s_addr = 0;
2078
2079     icmpLinkCount = 0;
2080     udpLinkCount = 0;
2081     tcpLinkCount = 0;
2082     fragmentIdLinkCount = 0;
2083     fragmentPtrLinkCount = 0;
2084     sockCount = 0;
2085
2086     cleanupIndex =0;
2087
2088     packetAliasMode = PKT_ALIAS_SAME_PORTS
2089                     | PKT_ALIAS_USE_SOCKETS
2090                     | PKT_ALIAS_RESET_ON_ADDR_CHANGE;
2091
2092     pptpAliasFlag = 0;
2093 }
2094
2095 void
2096 PacketAliasUninit(void) {
2097     deleteAllLinks = 1;
2098     CleanupAliasData();
2099     deleteAllLinks = 0;
2100     UninitPacketAliasLog();
2101 #ifndef NO_FW_PUNCH
2102     UninitPunchFW();
2103 #endif
2104 }
2105
2106
2107 /* Change mode for some operations */
2108 unsigned int
2109 PacketAliasSetMode(
2110     unsigned int flags, /* Which state to bring flags to */
2111     unsigned int mask   /* Mask of which flags to affect (use 0 to do a
2112                            probe for flag values) */
2113 )
2114 {
2115 /* Enable logging? */
2116     if (flags & mask & PKT_ALIAS_LOG)
2117     {
2118         InitPacketAliasLog();     /* Do the enable */
2119     } else
2120 /* _Disable_ logging? */
2121     if (~flags & mask & PKT_ALIAS_LOG) {
2122         UninitPacketAliasLog();
2123     }
2124
2125 #ifndef NO_FW_PUNCH
2126 /* Start punching holes in the firewall? */
2127     if (flags & mask & PKT_ALIAS_PUNCH_FW) {
2128         InitPunchFW();
2129     } else
2130 /* Stop punching holes in the firewall? */
2131     if (~flags & mask & PKT_ALIAS_PUNCH_FW) {
2132         UninitPunchFW();
2133     }
2134 #endif
2135
2136 /* Other flags can be set/cleared without special action */
2137     packetAliasMode = (flags & mask) | (packetAliasMode & ~mask);
2138     return packetAliasMode;
2139 }
2140
2141
2142 int
2143 PacketAliasCheckNewLink(void)
2144 {
2145     return newDefaultLink;
2146 }
2147
2148
2149 #ifndef NO_FW_PUNCH
2150
2151 /*****************
2152   Code to support firewall punching.  This shouldn't really be in this
2153   file, but making variables global is evil too.
2154   ****************/
2155
2156 /* Firewall include files */
2157 #include <sys/queue.h>
2158 #include <net/if.h>
2159 #include <netinet/ip_fw.h>
2160 #include <string.h>
2161 #include <err.h>
2162
2163 static void ClearAllFWHoles(void);
2164
2165 static int fireWallBaseNum;     /* The first firewall entry free for our use */
2166 static int fireWallNumNums;     /* How many entries can we use? */
2167 static int fireWallActiveNum;   /* Which entry did we last use? */
2168 static char *fireWallField;     /* bool array for entries */
2169
2170 #define fw_setfield(field, num)                         \
2171 do {                                                    \
2172     (field)[num] = 1;                                   \
2173 } /*lint -save -e717 */ while(0) /*lint -restore */
2174 #define fw_clrfield(field, num)                         \
2175 do {                                                    \
2176     (field)[num] = 0;                                   \
2177 } /*lint -save -e717 */ while(0) /*lint -restore */
2178 #define fw_tstfield(field, num) ((field)[num])
2179
2180 void
2181 PacketAliasSetFWBase(unsigned int base, unsigned int num) {
2182     fireWallBaseNum = base;
2183     fireWallNumNums = num;
2184 }
2185
2186 static void
2187 InitPunchFW(void) {
2188     fireWallField = malloc(fireWallNumNums);
2189     if (fireWallField) {
2190         memset(fireWallField, 0, fireWallNumNums);
2191         if (fireWallFD < 0) {
2192             fireWallFD = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
2193         }
2194         ClearAllFWHoles();
2195         fireWallActiveNum = fireWallBaseNum;
2196     }
2197 }
2198
2199 static void
2200 UninitPunchFW(void) {
2201     ClearAllFWHoles();
2202     if (fireWallFD >= 0)
2203         close(fireWallFD);
2204     fireWallFD = -1;
2205     if (fireWallField)
2206         free(fireWallField);
2207     fireWallField = NULL;
2208     packetAliasMode &= ~PKT_ALIAS_PUNCH_FW;
2209 }
2210
2211 /* Make a certain link go through the firewall */
2212 void
2213 PunchFWHole(struct alias_link *link) {
2214     int r;                      /* Result code */
2215     struct ip_fw rule;          /* On-the-fly built rule */
2216     int fwhole;                 /* Where to punch hole */
2217
2218 /* Don't do anything unless we are asked to */
2219     if ( !(packetAliasMode & PKT_ALIAS_PUNCH_FW) ||
2220          fireWallFD < 0 ||
2221          link->link_type != LINK_TCP ||
2222          !link->data.tcp)
2223         return;
2224
2225     memset(&rule, 0, sizeof rule);
2226
2227 /** Build rule **/
2228
2229     /* Find empty slot */
2230     for (fwhole = fireWallActiveNum;
2231          fwhole < fireWallBaseNum + fireWallNumNums &&
2232              fw_tstfield(fireWallField, fwhole);
2233          fwhole++)
2234         ;
2235     if (fwhole >= fireWallBaseNum + fireWallNumNums ||
2236         fw_tstfield(fireWallField, fwhole)) {
2237         for (fwhole = fireWallBaseNum;
2238              fwhole < fireWallActiveNum &&
2239                  fw_tstfield(fireWallField, fwhole);
2240              fwhole++)
2241             ;
2242         if (fwhole == fireWallActiveNum) {
2243             /* No rule point empty - we can't punch more holes. */
2244             fireWallActiveNum = fireWallBaseNum;
2245 #ifdef DEBUG
2246             fprintf(stderr, "libalias: Unable to create firewall hole!\n");
2247 #endif
2248             return;
2249         }
2250     }
2251     /* Start next search at next position */
2252     fireWallActiveNum = fwhole+1;
2253
2254     /* Build generic part of the two rules */
2255     rule.fw_number = fwhole;
2256     rule.fw_nports = 1;         /* Number of source ports; dest ports follow */
2257     rule.fw_flg = IP_FW_F_ACCEPT;
2258     rule.fw_prot = IPPROTO_TCP;
2259     rule.fw_smsk.s_addr = INADDR_BROADCAST;
2260     rule.fw_dmsk.s_addr = INADDR_BROADCAST;
2261
2262     /* Build and apply specific part of the rules */
2263     rule.fw_src = GetOriginalAddress(link);
2264     rule.fw_dst = GetDestAddress(link);
2265     rule.fw_uar.fw_pts[0] = ntohs(GetOriginalPort(link));
2266     rule.fw_uar.fw_pts[1] = ntohs(GetDestPort(link));
2267
2268     /* Skip non-bound links - XXX should not be strictly necessary,
2269        but seems to leave hole if not done.  Leak of non-bound links?
2270        (Code should be left even if the problem is fixed - it is a
2271        clear optimization) */
2272     if (rule.fw_uar.fw_pts[0] != 0 && rule.fw_uar.fw_pts[1] != 0) {
2273         r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, &rule, sizeof rule);
2274 #ifdef DEBUG
2275         if (r)
2276             err(1, "alias punch inbound(1) setsockopt(IP_FW_ADD)");
2277 #endif
2278         rule.fw_src = GetDestAddress(link);
2279         rule.fw_dst = GetOriginalAddress(link);
2280         rule.fw_uar.fw_pts[0] = ntohs(GetDestPort(link));
2281         rule.fw_uar.fw_pts[1] = ntohs(GetOriginalPort(link));
2282         r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, &rule, sizeof rule);
2283 #ifdef DEBUG
2284         if (r)
2285             err(1, "alias punch inbound(2) setsockopt(IP_FW_ADD)");
2286 #endif
2287     }
2288 /* Indicate hole applied */
2289     link->data.tcp->fwhole = fwhole;
2290     fw_setfield(fireWallField, fwhole);
2291 }
2292
2293 /* Remove a hole in a firewall associated with a particular alias
2294    link.  Calling this too often is harmless. */
2295 static void
2296 ClearFWHole(struct alias_link *link) {
2297     if (link->link_type == LINK_TCP && link->data.tcp) {
2298         int fwhole =  link->data.tcp->fwhole; /* Where is the firewall hole? */
2299         struct ip_fw rule;
2300
2301         if (fwhole < 0)
2302             return;
2303
2304         memset(&rule, 0, sizeof rule);
2305         rule.fw_number = fwhole;
2306         while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule))
2307             ;
2308         fw_clrfield(fireWallField, fwhole);
2309         link->data.tcp->fwhole = -1;
2310     }
2311 }
2312
2313 /* Clear out the entire range dedicated to firewall holes. */
2314 static void
2315 ClearAllFWHoles(void) {
2316     struct ip_fw rule;          /* On-the-fly built rule */
2317     int i;
2318     
2319     if (fireWallFD < 0)
2320         return;
2321
2322     memset(&rule, 0, sizeof rule);
2323     for (i = fireWallBaseNum; i < fireWallBaseNum + fireWallNumNums; i++) {
2324         rule.fw_number = i;
2325         while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule))
2326             ;
2327     }
2328     memset(fireWallField, 0, fireWallNumNums);
2329 }
2330 #endif