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