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