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