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