More minor IPI work.
[dragonfly/vkernel-mp.git] / lib / libalias / alias_db.c
blob4f96e55344d36586cef6a3307fa4852b63beaf53
1 /* -*- mode: c; tab-width: 8; c-basic-indent: 4; -*- */
3 /*-
4 * Copyright (c) 2001 Charles Mott <cm@linktel.net>
5 * All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
28 * $FreeBSD: src/lib/libalias/alias_db.c,v 1.21.2.14 2002/07/24 03:21:24 luigi Exp $
29 * $DragonFly: src/lib/libalias/alias_db.c,v 1.4 2004/08/20 02:08:17 joerg Exp $
33 Alias_db.c encapsulates all data structures used for storing
34 packet aliasing data. Other parts of the aliasing software
35 access data through functions provided in this file.
37 Data storage is based on the notion of a "link", which is
38 established for ICMP echo/reply packets, UDP datagrams and
39 TCP stream connections. A link stores the original source
40 and destination addresses. For UDP and TCP, it also stores
41 source and destination port numbers, as well as an alias
42 port number. Links are also used to store information about
43 fragments.
45 There is a facility for sweeping through and deleting old
46 links as new packets are sent through. A simple timeout is
47 used for ICMP and UDP links. TCP links are left alone unless
48 there is an incomplete connection, in which case the link
49 can be deleted after a certain amount of time.
52 Initial version: August, 1996 (cjm)
54 Version 1.4: September 16, 1996 (cjm)
55 Facility for handling incoming links added.
57 Version 1.6: September 18, 1996 (cjm)
58 ICMP data handling simplified.
60 Version 1.7: January 9, 1997 (cjm)
61 Fragment handling simplified.
62 Saves pointers for unresolved fragments.
63 Permits links for unspecified remote ports
64 or unspecified remote addresses.
65 Fixed bug which did not properly zero port
66 table entries after a link was deleted.
67 Cleaned up some obsolete comments.
69 Version 1.8: January 14, 1997 (cjm)
70 Fixed data type error in StartPoint().
71 (This error did not exist prior to v1.7
72 and was discovered and fixed by Ari Suutari)
74 Version 1.9: February 1, 1997
75 Optionally, connections initiated from packet aliasing host
76 machine will will not have their port number aliased unless it
77 conflicts with an aliasing port already being used. (cjm)
79 All options earlier being #ifdef'ed are now available through
80 a new interface, SetPacketAliasMode(). This allows run time
81 control (which is now available in PPP+pktAlias through the
82 'alias' keyword). (ee)
84 Added ability to create an alias port without
85 either destination address or port specified.
86 port type = ALIAS_PORT_UNKNOWN_DEST_ALL (ee)
88 Removed K&R style function headers
89 and general cleanup. (ee)
91 Added packetAliasMode to replace compiler #defines's (ee)
93 Allocates sockets for partially specified
94 ports if ALIAS_USE_SOCKETS defined. (cjm)
96 Version 2.0: March, 1997
97 SetAliasAddress() will now clean up alias links
98 if the aliasing address is changed. (cjm)
100 PacketAliasPermanentLink() function added to support permanent
101 links. (J. Fortes suggested the need for this.)
102 Examples:
104 (192.168.0.1, port 23) <-> alias port 6002, unknown dest addr/port
106 (192.168.0.2, port 21) <-> alias port 3604, known dest addr
107 unknown dest port
109 These permanent links allow for incoming connections to
110 machines on the local network. They can be given with a
111 user-chosen amount of specificity, with increasing specificity
112 meaning more security. (cjm)
114 Quite a bit of rework to the basic engine. The portTable[]
115 array, which kept track of which ports were in use was replaced
116 by a table/linked list structure. (cjm)
118 SetExpire() function added. (cjm)
120 DeleteLink() no longer frees memory association with a pointer
121 to a fragment (this bug was first recognized by E. Eklund in
122 v1.9).
124 Version 2.1: May, 1997 (cjm)
125 Packet aliasing engine reworked so that it can handle
126 multiple external addresses rather than just a single
127 host address.
129 PacketAliasRedirectPort() and PacketAliasRedirectAddr()
130 added to the API. The first function is a more generalized
131 version of PacketAliasPermanentLink(). The second function
132 implements static network address translation.
134 Version 3.2: July, 2000 (salander and satoh)
135 Added FindNewPortGroup to get contiguous range of port values.
137 Added QueryUdpTcpIn and QueryUdpTcpOut to look for an aliasing
138 link but not actually add one.
140 Added FindRtspOut, which is closely derived from FindUdpTcpOut,
141 except that the alias port (from FindNewPortGroup) is provided
142 as input.
144 See HISTORY file for additional revisions.
148 /* System include files */
149 #include <sys/param.h>
150 #include <sys/queue.h>
151 #include <sys/socket.h>
152 #include <sys/time.h>
154 #include <errno.h>
155 #include <stdlib.h>
156 #include <stdio.h>
157 #include <unistd.h>
159 /* BSD network include files */
160 #include <netinet/in_systm.h>
161 #include <netinet/in.h>
162 #include <netinet/ip.h>
163 #include <netinet/tcp.h>
164 #include <arpa/inet.h>
166 #include "alias.h"
167 #include "alias_local.h"
172 Constants (note: constants are also defined
173 near relevant functions or structs)
176 /* Sizes of input and output link tables */
177 #define LINK_TABLE_OUT_SIZE 101
178 #define LINK_TABLE_IN_SIZE 4001
180 /* Parameters used for cleanup of expired links */
181 #define ALIAS_CLEANUP_INTERVAL_SECS 60
182 #define ALIAS_CLEANUP_MAX_SPOKES 30
184 /* Timeouts (in seconds) for different link types */
185 #define ICMP_EXPIRE_TIME 60
186 #define UDP_EXPIRE_TIME 60
187 #define PROTO_EXPIRE_TIME 60
188 #define FRAGMENT_ID_EXPIRE_TIME 10
189 #define FRAGMENT_PTR_EXPIRE_TIME 30
191 /* TCP link expire time for different cases */
192 /* When the link has been used and closed - minimal grace time to
193 allow ACKs and potential re-connect in FTP (XXX - is this allowed?) */
194 #ifndef TCP_EXPIRE_DEAD
195 # define TCP_EXPIRE_DEAD 10
196 #endif
198 /* When the link has been used and closed on one side - the other side
199 is allowed to still send data */
200 #ifndef TCP_EXPIRE_SINGLEDEAD
201 # define TCP_EXPIRE_SINGLEDEAD 90
202 #endif
204 /* When the link isn't yet up */
205 #ifndef TCP_EXPIRE_INITIAL
206 # define TCP_EXPIRE_INITIAL 300
207 #endif
209 /* When the link is up */
210 #ifndef TCP_EXPIRE_CONNECTED
211 # define TCP_EXPIRE_CONNECTED 86400
212 #endif
215 /* Dummy port number codes used for FindLinkIn/Out() and AddLink().
216 These constants can be anything except zero, which indicates an
217 unknown port number. */
219 #define NO_DEST_PORT 1
220 #define NO_SRC_PORT 1
224 /* Data Structures
226 The fundamental data structure used in this program is
227 "struct alias_link". Whenever a TCP connection is made,
228 a UDP datagram is sent out, or an ICMP echo request is made,
229 a link record is made (if it has not already been created).
230 The link record is identified by the source address/port
231 and the destination address/port. In the case of an ICMP
232 echo request, the source port is treated as being equivalent
233 with the 16-bit ID number of the ICMP packet.
235 The link record also can store some auxiliary data. For
236 TCP connections that have had sequence and acknowledgment
237 modifications, data space is available to track these changes.
238 A state field is used to keep track in changes to the TCP
239 connection state. ID numbers of fragments can also be
240 stored in the auxiliary space. Pointers to unresolved
241 fragments can also be stored.
243 The link records support two independent chainings. Lookup
244 tables for input and out tables hold the initial pointers
245 the link chains. On input, the lookup table indexes on alias
246 port and link type. On output, the lookup table indexes on
247 source address, destination address, source port, destination
248 port and link type.
251 struct ack_data_record /* used to save changes to ACK/sequence numbers */
253 u_long ack_old;
254 u_long ack_new;
255 int delta;
256 int active;
259 struct tcp_state /* Information about TCP connection */
261 int in; /* State for outside -> inside */
262 int out; /* State for inside -> outside */
263 int index; /* Index to ACK data array */
264 int ack_modified; /* Indicates whether ACK and sequence numbers */
265 /* been modified */
268 #define N_LINK_TCP_DATA 3 /* Number of distinct ACK number changes
269 saved for a modified TCP stream */
270 struct tcp_dat
272 struct tcp_state state;
273 struct ack_data_record ack[N_LINK_TCP_DATA];
274 int fwhole; /* Which firewall record is used for this hole? */
277 struct server /* LSNAT server pool (circular list) */
279 struct in_addr addr;
280 u_short port;
281 struct server *next;
284 struct alias_link /* Main data structure */
286 struct in_addr src_addr; /* Address and port information */
287 struct in_addr dst_addr;
288 struct in_addr alias_addr;
289 struct in_addr proxy_addr;
290 u_short src_port;
291 u_short dst_port;
292 u_short alias_port;
293 u_short proxy_port;
294 struct server *server;
296 int link_type; /* Type of link: TCP, UDP, ICMP, proto, frag */
298 /* values for link_type */
299 #define LINK_ICMP IPPROTO_ICMP
300 #define LINK_UDP IPPROTO_UDP
301 #define LINK_TCP IPPROTO_TCP
302 #define LINK_FRAGMENT_ID (IPPROTO_MAX + 1)
303 #define LINK_FRAGMENT_PTR (IPPROTO_MAX + 2)
304 #define LINK_ADDR (IPPROTO_MAX + 3)
305 #define LINK_PPTP (IPPROTO_MAX + 4)
307 int flags; /* indicates special characteristics */
309 /* flag bits */
310 #define LINK_UNKNOWN_DEST_PORT 0x01
311 #define LINK_UNKNOWN_DEST_ADDR 0x02
312 #define LINK_PERMANENT 0x04
313 #define LINK_PARTIALLY_SPECIFIED 0x03 /* logical-or of first two bits */
314 #define LINK_UNFIREWALLED 0x08
315 #define LINK_LAST_LINE_CRLF_TERMED 0x10
317 int timestamp; /* Time link was last accessed */
318 int expire_time; /* Expire time for link */
320 int sockfd; /* socket descriptor */
322 LIST_ENTRY(alias_link) list_out; /* Linked list of pointers for */
323 LIST_ENTRY(alias_link) list_in; /* input and output lookup tables */
325 union /* Auxiliary data */
327 char *frag_ptr;
328 struct in_addr frag_addr;
329 struct tcp_dat *tcp;
330 } data;
337 /* Global Variables
339 The global variables listed here are only accessed from
340 within alias_db.c and so are prefixed with the static
341 designation.
344 int packetAliasMode; /* Mode flags */
345 /* - documented in alias.h */
347 static struct in_addr aliasAddress; /* Address written onto source */
348 /* field of IP packet. */
350 static struct in_addr targetAddress; /* IP address incoming packets */
351 /* are sent to if no aliasing */
352 /* link already exists */
354 static struct in_addr nullAddress; /* Used as a dummy parameter for */
355 /* some function calls */
356 static LIST_HEAD(, alias_link)
357 linkTableOut[LINK_TABLE_OUT_SIZE]; /* Lookup table of pointers to */
358 /* chains of link records. Each */
359 static LIST_HEAD(, alias_link) /* link record is doubly indexed */
360 linkTableIn[LINK_TABLE_IN_SIZE]; /* into input and output lookup */
361 /* tables. */
363 static int icmpLinkCount; /* Link statistics */
364 static int udpLinkCount;
365 static int tcpLinkCount;
366 static int pptpLinkCount;
367 static int protoLinkCount;
368 static int fragmentIdLinkCount;
369 static int fragmentPtrLinkCount;
370 static int sockCount;
372 static int cleanupIndex; /* Index to chain of link table */
373 /* being inspected for old links */
375 static int timeStamp; /* System time in seconds for */
376 /* current packet */
378 static int lastCleanupTime; /* Last time IncrementalCleanup() */
379 /* was called */
381 static int houseKeepingResidual; /* used by HouseKeeping() */
383 static int deleteAllLinks; /* If equal to zero, DeleteLink() */
384 /* will not remove permanent links */
386 static FILE *monitorFile; /* File descriptor for link */
387 /* statistics monitoring file */
389 static int newDefaultLink; /* Indicates if a new aliasing */
390 /* link has been created after a */
391 /* call to PacketAliasIn/Out(). */
393 #ifndef NO_FW_PUNCH
394 static int fireWallFD = -1; /* File descriptor to be able to */
395 /* control firewall. Opened by */
396 /* PacketAliasSetMode on first */
397 /* setting the PKT_ALIAS_PUNCH_FW */
398 /* flag. */
399 #endif
407 /* Internal utility routines (used only in alias_db.c)
409 Lookup table starting points:
410 StartPointIn() -- link table initial search point for
411 incoming packets
412 StartPointOut() -- link table initial search point for
413 outgoing packets
415 Miscellaneous:
416 SeqDiff() -- difference between two TCP sequences
417 ShowAliasStats() -- send alias statistics to a monitor file
421 /* Local prototypes */
422 static u_int StartPointIn(struct in_addr, u_short, int);
424 static u_int StartPointOut(struct in_addr, struct in_addr,
425 u_short, u_short, int);
427 static int SeqDiff(u_long, u_long);
429 static void ShowAliasStats(void);
431 #ifndef NO_FW_PUNCH
432 /* Firewall control */
433 static void InitPunchFW(void);
434 static void UninitPunchFW(void);
435 static void ClearFWHole(struct alias_link *link);
436 #endif
438 /* Log file control */
439 static void InitPacketAliasLog(void);
440 static void UninitPacketAliasLog(void);
442 static u_int
443 StartPointIn(struct in_addr alias_addr,
444 u_short alias_port,
445 int link_type)
447 u_int n;
449 n = alias_addr.s_addr;
450 if (link_type != LINK_PPTP)
451 n += alias_port;
452 n += link_type;
453 return(n % LINK_TABLE_IN_SIZE);
457 static u_int
458 StartPointOut(struct in_addr src_addr, struct in_addr dst_addr,
459 u_short src_port, u_short dst_port, int link_type)
461 u_int n;
463 n = src_addr.s_addr;
464 n += dst_addr.s_addr;
465 if (link_type != LINK_PPTP) {
466 n += src_port;
467 n += dst_port;
469 n += link_type;
471 return(n % LINK_TABLE_OUT_SIZE);
475 static int
476 SeqDiff(u_long x, u_long y)
478 /* Return the difference between two TCP sequence numbers */
481 This function is encapsulated in case there are any unusual
482 arithmetic conditions that need to be considered.
485 return (ntohl(y) - ntohl(x));
489 static void
490 ShowAliasStats(void)
492 /* Used for debugging */
494 if (monitorFile)
496 fprintf(monitorFile, "icmp=%d, udp=%d, tcp=%d, pptp=%d, proto=%d, frag_id=%d frag_ptr=%d",
497 icmpLinkCount,
498 udpLinkCount,
499 tcpLinkCount,
500 pptpLinkCount,
501 protoLinkCount,
502 fragmentIdLinkCount,
503 fragmentPtrLinkCount);
505 fprintf(monitorFile, " / tot=%d (sock=%d)\n",
506 icmpLinkCount + udpLinkCount
507 + tcpLinkCount
508 + pptpLinkCount
509 + protoLinkCount
510 + fragmentIdLinkCount
511 + fragmentPtrLinkCount,
512 sockCount);
514 fflush(monitorFile);
522 /* Internal routines for finding, deleting and adding links
524 Port Allocation:
525 GetNewPort() -- find and reserve new alias port number
526 GetSocket() -- try to allocate a socket for a given port
528 Link creation and deletion:
529 CleanupAliasData() - remove all link chains from lookup table
530 IncrementalCleanup() - look for stale links in a single chain
531 DeleteLink() - remove link
532 AddLink() - add link
533 ReLink() - change link
535 Link search:
536 FindLinkOut() - find link for outgoing packets
537 FindLinkIn() - find link for incoming packets
539 Port search:
540 FindNewPortGroup() - find an available group of ports
543 /* Local prototypes */
544 static int GetNewPort(struct alias_link *, int);
546 static u_short GetSocket(u_short, int *, int);
548 static void CleanupAliasData(void);
550 static void IncrementalCleanup(void);
552 static void DeleteLink(struct alias_link *);
554 static struct alias_link *
555 AddLink(struct in_addr, struct in_addr, struct in_addr,
556 u_short, u_short, int, int);
558 static struct alias_link *
559 ReLink(struct alias_link *,
560 struct in_addr, struct in_addr, struct in_addr,
561 u_short, u_short, int, int);
563 static struct alias_link *
564 FindLinkOut(struct in_addr, struct in_addr, u_short, u_short, int, int);
566 static struct alias_link *
567 FindLinkIn(struct in_addr, struct in_addr, u_short, u_short, int, int);
570 #define ALIAS_PORT_BASE 0x08000
571 #define ALIAS_PORT_MASK 0x07fff
572 #define ALIAS_PORT_MASK_EVEN 0x07ffe
573 #define GET_NEW_PORT_MAX_ATTEMPTS 20
575 #define GET_ALIAS_PORT -1
576 #define GET_ALIAS_ID GET_ALIAS_PORT
578 #define FIND_EVEN_ALIAS_BASE 1
580 /* GetNewPort() allocates port numbers. Note that if a port number
581 is already in use, that does not mean that it cannot be used by
582 another link concurrently. This is because GetNewPort() looks for
583 unused triplets: (dest addr, dest port, alias port). */
585 static int
586 GetNewPort(struct alias_link *link, int alias_port_param)
588 int i;
589 int max_trials;
590 u_short port_sys;
591 u_short port_net;
594 Description of alias_port_param for GetNewPort(). When
595 this parameter is zero or positive, it precisely specifies
596 the port number. GetNewPort() will return this number
597 without check that it is in use.
599 When this parameter is GET_ALIAS_PORT, it indicates to get a randomly
600 selected port number.
603 if (alias_port_param == GET_ALIAS_PORT)
606 * The aliasing port is automatically selected
607 * by one of two methods below:
609 max_trials = GET_NEW_PORT_MAX_ATTEMPTS;
611 if (packetAliasMode & PKT_ALIAS_SAME_PORTS)
614 * When the PKT_ALIAS_SAME_PORTS option is
615 * chosen, the first try will be the
616 * actual source port. If this is already
617 * in use, the remainder of the trials
618 * will be random.
620 port_net = link->src_port;
621 port_sys = ntohs(port_net);
623 else
625 /* First trial and all subsequent are random. */
626 port_sys = random() & ALIAS_PORT_MASK;
627 port_sys += ALIAS_PORT_BASE;
628 port_net = htons(port_sys);
631 else if (alias_port_param >= 0 && alias_port_param < 0x10000)
633 link->alias_port = (u_short) alias_port_param;
634 return(0);
636 else
638 #ifdef DEBUG
639 fprintf(stderr, "PacketAlias/GetNewPort(): ");
640 fprintf(stderr, "input parameter error\n");
641 #endif
642 return(-1);
646 /* Port number search */
647 for (i=0; i<max_trials; i++)
649 int go_ahead;
650 struct alias_link *search_result;
652 search_result = FindLinkIn(link->dst_addr, link->alias_addr,
653 link->dst_port, port_net,
654 link->link_type, 0);
656 if (search_result == NULL)
657 go_ahead = 1;
658 else if (!(link->flags & LINK_PARTIALLY_SPECIFIED)
659 && (search_result->flags & LINK_PARTIALLY_SPECIFIED))
660 go_ahead = 1;
661 else
662 go_ahead = 0;
664 if (go_ahead)
666 if ((packetAliasMode & PKT_ALIAS_USE_SOCKETS)
667 && (link->flags & LINK_PARTIALLY_SPECIFIED)
668 && ((link->link_type == LINK_TCP) ||
669 (link->link_type == LINK_UDP)))
671 if (GetSocket(port_net, &link->sockfd, link->link_type))
673 link->alias_port = port_net;
674 return(0);
677 else
679 link->alias_port = port_net;
680 return(0);
684 port_sys = random() & ALIAS_PORT_MASK;
685 port_sys += ALIAS_PORT_BASE;
686 port_net = htons(port_sys);
689 #ifdef DEBUG
690 fprintf(stderr, "PacketAlias/GetnewPort(): ");
691 fprintf(stderr, "could not find free port\n");
692 #endif
694 return(-1);
698 static u_short
699 GetSocket(u_short port_net, int *sockfd, int link_type)
701 int err;
702 int sock;
703 struct sockaddr_in sock_addr;
705 if (link_type == LINK_TCP)
706 sock = socket(AF_INET, SOCK_STREAM, 0);
707 else if (link_type == LINK_UDP)
708 sock = socket(AF_INET, SOCK_DGRAM, 0);
709 else
711 #ifdef DEBUG
712 fprintf(stderr, "PacketAlias/GetSocket(): ");
713 fprintf(stderr, "incorrect link type\n");
714 #endif
715 return(0);
718 if (sock < 0)
720 #ifdef DEBUG
721 fprintf(stderr, "PacketAlias/GetSocket(): ");
722 fprintf(stderr, "socket() error %d\n", *sockfd);
723 #endif
724 return(0);
727 sock_addr.sin_family = AF_INET;
728 sock_addr.sin_addr.s_addr = htonl(INADDR_ANY);
729 sock_addr.sin_port = port_net;
731 err = bind(sock,
732 (struct sockaddr *) &sock_addr,
733 sizeof(sock_addr));
734 if (err == 0)
736 sockCount++;
737 *sockfd = sock;
738 return(1);
740 else
742 close(sock);
743 return(0);
748 /* FindNewPortGroup() returns a base port number for an available
749 range of contiguous port numbers. Note that if a port number
750 is already in use, that does not mean that it cannot be used by
751 another link concurrently. This is because FindNewPortGroup()
752 looks for unused triplets: (dest addr, dest port, alias port). */
755 FindNewPortGroup(struct in_addr dst_addr,
756 struct in_addr alias_addr,
757 u_short src_port,
758 u_short dst_port,
759 u_short port_count,
760 u_char proto,
761 u_char align)
763 int i, j;
764 int max_trials;
765 u_short port_sys;
766 int link_type;
769 * Get link_type from protocol
772 switch (proto)
774 case IPPROTO_UDP:
775 link_type = LINK_UDP;
776 break;
777 case IPPROTO_TCP:
778 link_type = LINK_TCP;
779 break;
780 default:
781 return (0);
782 break;
786 * The aliasing port is automatically selected
787 * by one of two methods below:
789 max_trials = GET_NEW_PORT_MAX_ATTEMPTS;
791 if (packetAliasMode & PKT_ALIAS_SAME_PORTS) {
793 * When the ALIAS_SAME_PORTS option is
794 * chosen, the first try will be the
795 * actual source port. If this is already
796 * in use, the remainder of the trials
797 * will be random.
799 port_sys = ntohs(src_port);
801 } else {
803 /* First trial and all subsequent are random. */
804 if (align == FIND_EVEN_ALIAS_BASE)
805 port_sys = random() & ALIAS_PORT_MASK_EVEN;
806 else
807 port_sys = random() & ALIAS_PORT_MASK;
809 port_sys += ALIAS_PORT_BASE;
812 /* Port number search */
813 for (i = 0; i < max_trials; i++) {
815 struct alias_link *search_result;
817 for (j = 0; j < port_count; j++)
818 if (0 != (search_result = FindLinkIn(dst_addr, alias_addr,
819 dst_port, htons(port_sys + j),
820 link_type, 0)))
821 break;
823 /* Found a good range, return base */
824 if (j == port_count)
825 return (htons(port_sys));
827 /* Find a new base to try */
828 if (align == FIND_EVEN_ALIAS_BASE)
829 port_sys = random() & ALIAS_PORT_MASK_EVEN;
830 else
831 port_sys = random() & ALIAS_PORT_MASK;
833 port_sys += ALIAS_PORT_BASE;
836 #ifdef DEBUG
837 fprintf(stderr, "PacketAlias/FindNewPortGroup(): ");
838 fprintf(stderr, "could not find free port(s)\n");
839 #endif
841 return(0);
844 static void
845 CleanupAliasData(void)
847 struct alias_link *link;
848 int i, icount;
850 icount = 0;
851 for (i=0; i<LINK_TABLE_OUT_SIZE; i++)
853 link = LIST_FIRST(&linkTableOut[i]);
854 while (link != NULL)
856 struct alias_link *link_next;
857 link_next = LIST_NEXT(link, list_out);
858 icount++;
859 DeleteLink(link);
860 link = link_next;
864 cleanupIndex =0;
868 static void
869 IncrementalCleanup(void)
871 int icount;
872 struct alias_link *link;
874 icount = 0;
875 link = LIST_FIRST(&linkTableOut[cleanupIndex++]);
876 while (link != NULL)
878 int idelta;
879 struct alias_link *link_next;
881 link_next = LIST_NEXT(link, list_out);
882 idelta = timeStamp - link->timestamp;
883 switch (link->link_type)
885 case LINK_TCP:
886 if (idelta > link->expire_time)
888 struct tcp_dat *tcp_aux;
890 tcp_aux = link->data.tcp;
891 if (tcp_aux->state.in != ALIAS_TCP_STATE_CONNECTED
892 || tcp_aux->state.out != ALIAS_TCP_STATE_CONNECTED)
894 DeleteLink(link);
895 icount++;
898 break;
899 default:
900 if (idelta > link->expire_time)
902 DeleteLink(link);
903 icount++;
905 break;
907 link = link_next;
910 if (cleanupIndex == LINK_TABLE_OUT_SIZE)
911 cleanupIndex = 0;
914 static void
915 DeleteLink(struct alias_link *link)
918 /* Don't do anything if the link is marked permanent */
919 if (deleteAllLinks == 0 && link->flags & LINK_PERMANENT)
920 return;
922 #ifndef NO_FW_PUNCH
923 /* Delete associated firewall hole, if any */
924 ClearFWHole(link);
925 #endif
927 /* Free memory allocated for LSNAT server pool */
928 if (link->server != NULL) {
929 struct server *head, *curr, *next;
931 head = curr = link->server;
932 do {
933 next = curr->next;
934 free(curr);
935 } while ((curr = next) != head);
938 /* Adjust output table pointers */
939 LIST_REMOVE(link, list_out);
941 /* Adjust input table pointers */
942 LIST_REMOVE(link, list_in);
944 /* Close socket, if one has been allocated */
945 if (link->sockfd != -1)
947 sockCount--;
948 close(link->sockfd);
951 /* Link-type dependent cleanup */
952 switch(link->link_type)
954 case LINK_ICMP:
955 icmpLinkCount--;
956 break;
957 case LINK_UDP:
958 udpLinkCount--;
959 break;
960 case LINK_TCP:
961 tcpLinkCount--;
962 free(link->data.tcp);
963 break;
964 case LINK_PPTP:
965 pptpLinkCount--;
966 break;
967 case LINK_FRAGMENT_ID:
968 fragmentIdLinkCount--;
969 break;
970 case LINK_FRAGMENT_PTR:
971 fragmentPtrLinkCount--;
972 if (link->data.frag_ptr != NULL)
973 free(link->data.frag_ptr);
974 break;
975 case LINK_ADDR:
976 break;
977 default:
978 protoLinkCount--;
979 break;
982 /* Free memory */
983 free(link);
985 /* Write statistics, if logging enabled */
986 if (packetAliasMode & PKT_ALIAS_LOG)
988 ShowAliasStats();
993 static struct alias_link *
994 AddLink(struct in_addr src_addr,
995 struct in_addr dst_addr,
996 struct in_addr alias_addr,
997 u_short src_port,
998 u_short dst_port,
999 int alias_port_param, /* if less than zero, alias */
1000 int link_type) /* port will be automatically */
1001 { /* chosen. If greater than */
1002 u_int start_point; /* zero, equal to alias port */
1003 struct alias_link *link;
1005 link = malloc(sizeof(struct alias_link));
1006 if (link != NULL)
1008 /* Basic initialization */
1009 link->src_addr = src_addr;
1010 link->dst_addr = dst_addr;
1011 link->alias_addr = alias_addr;
1012 link->proxy_addr.s_addr = INADDR_ANY;
1013 link->src_port = src_port;
1014 link->dst_port = dst_port;
1015 link->proxy_port = 0;
1016 link->server = NULL;
1017 link->link_type = link_type;
1018 link->sockfd = -1;
1019 link->flags = 0;
1020 link->timestamp = timeStamp;
1022 /* Expiration time */
1023 switch (link_type)
1025 case LINK_ICMP:
1026 link->expire_time = ICMP_EXPIRE_TIME;
1027 break;
1028 case LINK_UDP:
1029 link->expire_time = UDP_EXPIRE_TIME;
1030 break;
1031 case LINK_TCP:
1032 link->expire_time = TCP_EXPIRE_INITIAL;
1033 break;
1034 case LINK_PPTP:
1035 link->flags |= LINK_PERMANENT; /* no timeout. */
1036 break;
1037 case LINK_FRAGMENT_ID:
1038 link->expire_time = FRAGMENT_ID_EXPIRE_TIME;
1039 break;
1040 case LINK_FRAGMENT_PTR:
1041 link->expire_time = FRAGMENT_PTR_EXPIRE_TIME;
1042 break;
1043 case LINK_ADDR:
1044 break;
1045 default:
1046 link->expire_time = PROTO_EXPIRE_TIME;
1047 break;
1050 /* Determine alias flags */
1051 if (dst_addr.s_addr == INADDR_ANY)
1052 link->flags |= LINK_UNKNOWN_DEST_ADDR;
1053 if (dst_port == 0)
1054 link->flags |= LINK_UNKNOWN_DEST_PORT;
1056 /* Determine alias port */
1057 if (GetNewPort(link, alias_port_param) != 0)
1059 free(link);
1060 return(NULL);
1063 /* Link-type dependent initialization */
1064 switch(link_type)
1066 struct tcp_dat *aux_tcp;
1068 case LINK_ICMP:
1069 icmpLinkCount++;
1070 break;
1071 case LINK_UDP:
1072 udpLinkCount++;
1073 break;
1074 case LINK_TCP:
1075 aux_tcp = malloc(sizeof(struct tcp_dat));
1076 if (aux_tcp != NULL)
1078 int i;
1080 tcpLinkCount++;
1081 aux_tcp->state.in = ALIAS_TCP_STATE_NOT_CONNECTED;
1082 aux_tcp->state.out = ALIAS_TCP_STATE_NOT_CONNECTED;
1083 aux_tcp->state.index = 0;
1084 aux_tcp->state.ack_modified = 0;
1085 for (i=0; i<N_LINK_TCP_DATA; i++)
1086 aux_tcp->ack[i].active = 0;
1087 aux_tcp->fwhole = -1;
1088 link->data.tcp = aux_tcp;
1090 else
1092 #ifdef DEBUG
1093 fprintf(stderr, "PacketAlias/AddLink: ");
1094 fprintf(stderr, " cannot allocate auxiliary TCP data\n");
1095 #endif
1096 free(link);
1097 return (NULL);
1099 break;
1100 case LINK_PPTP:
1101 pptpLinkCount++;
1102 break;
1103 case LINK_FRAGMENT_ID:
1104 fragmentIdLinkCount++;
1105 break;
1106 case LINK_FRAGMENT_PTR:
1107 fragmentPtrLinkCount++;
1108 break;
1109 case LINK_ADDR:
1110 break;
1111 default:
1112 protoLinkCount++;
1113 break;
1116 /* Set up pointers for output lookup table */
1117 start_point = StartPointOut(src_addr, dst_addr,
1118 src_port, dst_port, link_type);
1119 LIST_INSERT_HEAD(&linkTableOut[start_point], link, list_out);
1121 /* Set up pointers for input lookup table */
1122 start_point = StartPointIn(alias_addr, link->alias_port, link_type);
1123 LIST_INSERT_HEAD(&linkTableIn[start_point], link, list_in);
1125 else
1127 #ifdef DEBUG
1128 fprintf(stderr, "PacketAlias/AddLink(): ");
1129 fprintf(stderr, "malloc() call failed.\n");
1130 #endif
1133 if (packetAliasMode & PKT_ALIAS_LOG)
1135 ShowAliasStats();
1138 return(link);
1141 static struct alias_link *
1142 ReLink(struct alias_link *old_link,
1143 struct in_addr src_addr,
1144 struct in_addr dst_addr,
1145 struct in_addr alias_addr,
1146 u_short src_port,
1147 u_short dst_port,
1148 int alias_port_param, /* if less than zero, alias */
1149 int link_type) /* port will be automatically */
1150 { /* chosen. If greater than */
1151 struct alias_link *new_link; /* zero, equal to alias port */
1153 new_link = AddLink(src_addr, dst_addr, alias_addr,
1154 src_port, dst_port, alias_port_param,
1155 link_type);
1156 #ifndef NO_FW_PUNCH
1157 if (new_link != NULL &&
1158 old_link->link_type == LINK_TCP &&
1159 old_link->data.tcp->fwhole > 0) {
1160 PunchFWHole(new_link);
1162 #endif
1163 DeleteLink(old_link);
1164 return new_link;
1167 static struct alias_link *
1168 _FindLinkOut(struct in_addr src_addr,
1169 struct in_addr dst_addr,
1170 u_short src_port,
1171 u_short dst_port,
1172 int link_type,
1173 int replace_partial_links)
1175 u_int i;
1176 struct alias_link *link;
1178 i = StartPointOut(src_addr, dst_addr, src_port, dst_port, link_type);
1179 LIST_FOREACH(link, &linkTableOut[i], list_out)
1181 if (link->src_addr.s_addr == src_addr.s_addr
1182 && link->server == NULL
1183 && link->dst_addr.s_addr == dst_addr.s_addr
1184 && link->dst_port == dst_port
1185 && link->src_port == src_port
1186 && link->link_type == link_type)
1188 link->timestamp = timeStamp;
1189 break;
1193 /* Search for partially specified links. */
1194 if (link == NULL && replace_partial_links)
1196 if (dst_port != 0 && dst_addr.s_addr != INADDR_ANY)
1198 link = _FindLinkOut(src_addr, dst_addr, src_port, 0,
1199 link_type, 0);
1200 if (link == NULL)
1201 link = _FindLinkOut(src_addr, nullAddress, src_port,
1202 dst_port, link_type, 0);
1204 if (link == NULL &&
1205 (dst_port != 0 || dst_addr.s_addr != INADDR_ANY))
1207 link = _FindLinkOut(src_addr, nullAddress, src_port, 0,
1208 link_type, 0);
1210 if (link != NULL)
1212 link = ReLink(link,
1213 src_addr, dst_addr, link->alias_addr,
1214 src_port, dst_port, link->alias_port,
1215 link_type);
1219 return(link);
1222 static struct alias_link *
1223 FindLinkOut(struct in_addr src_addr,
1224 struct in_addr dst_addr,
1225 u_short src_port,
1226 u_short dst_port,
1227 int link_type,
1228 int replace_partial_links)
1230 struct alias_link *link;
1232 link = _FindLinkOut(src_addr, dst_addr, src_port, dst_port,
1233 link_type, replace_partial_links);
1235 if (link == NULL)
1237 /* The following allows permanent links to be
1238 specified as using the default source address
1239 (i.e. device interface address) without knowing
1240 in advance what that address is. */
1241 if (aliasAddress.s_addr != 0 &&
1242 src_addr.s_addr == aliasAddress.s_addr)
1244 link = _FindLinkOut(nullAddress, dst_addr, src_port, dst_port,
1245 link_type, replace_partial_links);
1249 return(link);
1253 static struct alias_link *
1254 _FindLinkIn(struct in_addr dst_addr,
1255 struct in_addr alias_addr,
1256 u_short dst_port,
1257 u_short alias_port,
1258 int link_type,
1259 int replace_partial_links)
1261 int flags_in;
1262 u_int start_point;
1263 struct alias_link *link;
1264 struct alias_link *link_fully_specified;
1265 struct alias_link *link_unknown_all;
1266 struct alias_link *link_unknown_dst_addr;
1267 struct alias_link *link_unknown_dst_port;
1269 /* Initialize pointers */
1270 link_fully_specified = NULL;
1271 link_unknown_all = NULL;
1272 link_unknown_dst_addr = NULL;
1273 link_unknown_dst_port = NULL;
1275 /* If either the dest addr or port is unknown, the search
1276 loop will have to know about this. */
1278 flags_in = 0;
1279 if (dst_addr.s_addr == INADDR_ANY)
1280 flags_in |= LINK_UNKNOWN_DEST_ADDR;
1281 if (dst_port == 0)
1282 flags_in |= LINK_UNKNOWN_DEST_PORT;
1284 /* Search loop */
1285 start_point = StartPointIn(alias_addr, alias_port, link_type);
1286 LIST_FOREACH(link, &linkTableIn[start_point], list_in)
1288 int flags;
1290 flags = flags_in | link->flags;
1291 if (!(flags & LINK_PARTIALLY_SPECIFIED))
1293 if (link->alias_addr.s_addr == alias_addr.s_addr
1294 && link->alias_port == alias_port
1295 && link->dst_addr.s_addr == dst_addr.s_addr
1296 && link->dst_port == dst_port
1297 && link->link_type == link_type)
1299 link_fully_specified = link;
1300 break;
1303 else if ((flags & LINK_UNKNOWN_DEST_ADDR)
1304 && (flags & LINK_UNKNOWN_DEST_PORT))
1306 if (link->alias_addr.s_addr == alias_addr.s_addr
1307 && link->alias_port == alias_port
1308 && link->link_type == link_type)
1310 if (link_unknown_all == NULL)
1311 link_unknown_all = link;
1314 else if (flags & LINK_UNKNOWN_DEST_ADDR)
1316 if (link->alias_addr.s_addr == alias_addr.s_addr
1317 && link->alias_port == alias_port
1318 && link->link_type == link_type
1319 && link->dst_port == dst_port)
1321 if (link_unknown_dst_addr == NULL)
1322 link_unknown_dst_addr = link;
1325 else if (flags & LINK_UNKNOWN_DEST_PORT)
1327 if (link->alias_addr.s_addr == alias_addr.s_addr
1328 && link->alias_port == alias_port
1329 && link->link_type == link_type
1330 && link->dst_addr.s_addr == dst_addr.s_addr)
1332 if (link_unknown_dst_port == NULL)
1333 link_unknown_dst_port = link;
1340 if (link_fully_specified != NULL)
1342 link_fully_specified->timestamp = timeStamp;
1343 link = link_fully_specified;
1345 else if (link_unknown_dst_port != NULL)
1346 link = link_unknown_dst_port;
1347 else if (link_unknown_dst_addr != NULL)
1348 link = link_unknown_dst_addr;
1349 else if (link_unknown_all != NULL)
1350 link = link_unknown_all;
1351 else
1352 return (NULL);
1354 if (replace_partial_links &&
1355 (link->flags & LINK_PARTIALLY_SPECIFIED || link->server != NULL))
1357 struct in_addr src_addr;
1358 u_short src_port;
1360 if (link->server != NULL) { /* LSNAT link */
1361 src_addr = link->server->addr;
1362 src_port = link->server->port;
1363 link->server = link->server->next;
1364 } else {
1365 src_addr = link->src_addr;
1366 src_port = link->src_port;
1369 link = ReLink(link,
1370 src_addr, dst_addr, alias_addr,
1371 src_port, dst_port, alias_port,
1372 link_type);
1375 return (link);
1378 static struct alias_link *
1379 FindLinkIn(struct in_addr dst_addr,
1380 struct in_addr alias_addr,
1381 u_short dst_port,
1382 u_short alias_port,
1383 int link_type,
1384 int replace_partial_links)
1386 struct alias_link *link;
1388 link = _FindLinkIn(dst_addr, alias_addr, dst_port, alias_port,
1389 link_type, replace_partial_links);
1391 if (link == NULL)
1393 /* The following allows permanent links to be
1394 specified as using the default aliasing address
1395 (i.e. device interface address) without knowing
1396 in advance what that address is. */
1397 if (aliasAddress.s_addr != 0 &&
1398 alias_addr.s_addr == aliasAddress.s_addr)
1400 link = _FindLinkIn(dst_addr, nullAddress, dst_port, alias_port,
1401 link_type, replace_partial_links);
1405 return(link);
1411 /* External routines for finding/adding links
1413 -- "external" means outside alias_db.c, but within alias*.c --
1415 FindIcmpIn(), FindIcmpOut()
1416 FindFragmentIn1(), FindFragmentIn2()
1417 AddFragmentPtrLink(), FindFragmentPtr()
1418 FindProtoIn(), FindProtoOut()
1419 FindUdpTcpIn(), FindUdpTcpOut()
1420 AddPptp(), FindPptpOutByCallId(), FindPptpInByCallId(),
1421 FindPptpOutByPeerCallId(), FindPptpInByPeerCallId()
1422 FindOriginalAddress(), FindAliasAddress()
1424 (prototypes in alias_local.h)
1428 struct alias_link *
1429 FindIcmpIn(struct in_addr dst_addr,
1430 struct in_addr alias_addr,
1431 u_short id_alias,
1432 int create)
1434 struct alias_link *link;
1436 link = FindLinkIn(dst_addr, alias_addr,
1437 NO_DEST_PORT, id_alias,
1438 LINK_ICMP, 0);
1439 if (link == NULL && create && !(packetAliasMode & PKT_ALIAS_DENY_INCOMING))
1441 struct in_addr target_addr;
1443 target_addr = FindOriginalAddress(alias_addr);
1444 link = AddLink(target_addr, dst_addr, alias_addr,
1445 id_alias, NO_DEST_PORT, id_alias,
1446 LINK_ICMP);
1449 return (link);
1453 struct alias_link *
1454 FindIcmpOut(struct in_addr src_addr,
1455 struct in_addr dst_addr,
1456 u_short id,
1457 int create)
1459 struct alias_link * link;
1461 link = FindLinkOut(src_addr, dst_addr,
1462 id, NO_DEST_PORT,
1463 LINK_ICMP, 0);
1464 if (link == NULL && create)
1466 struct in_addr alias_addr;
1468 alias_addr = FindAliasAddress(src_addr);
1469 link = AddLink(src_addr, dst_addr, alias_addr,
1470 id, NO_DEST_PORT, GET_ALIAS_ID,
1471 LINK_ICMP);
1474 return(link);
1478 struct alias_link *
1479 FindFragmentIn1(struct in_addr dst_addr,
1480 struct in_addr alias_addr,
1481 u_short ip_id)
1483 struct alias_link *link;
1485 link = FindLinkIn(dst_addr, alias_addr,
1486 NO_DEST_PORT, ip_id,
1487 LINK_FRAGMENT_ID, 0);
1489 if (link == NULL)
1491 link = AddLink(nullAddress, dst_addr, alias_addr,
1492 NO_SRC_PORT, NO_DEST_PORT, ip_id,
1493 LINK_FRAGMENT_ID);
1496 return(link);
1500 struct alias_link *
1501 FindFragmentIn2(struct in_addr dst_addr, /* Doesn't add a link if one */
1502 struct in_addr alias_addr, /* is not found. */
1503 u_short ip_id)
1505 return FindLinkIn(dst_addr, alias_addr,
1506 NO_DEST_PORT, ip_id,
1507 LINK_FRAGMENT_ID, 0);
1511 struct alias_link *
1512 AddFragmentPtrLink(struct in_addr dst_addr,
1513 u_short ip_id)
1515 return AddLink(nullAddress, dst_addr, nullAddress,
1516 NO_SRC_PORT, NO_DEST_PORT, ip_id,
1517 LINK_FRAGMENT_PTR);
1521 struct alias_link *
1522 FindFragmentPtr(struct in_addr dst_addr,
1523 u_short ip_id)
1525 return FindLinkIn(dst_addr, nullAddress,
1526 NO_DEST_PORT, ip_id,
1527 LINK_FRAGMENT_PTR, 0);
1531 struct alias_link *
1532 FindProtoIn(struct in_addr dst_addr,
1533 struct in_addr alias_addr,
1534 u_char proto)
1536 struct alias_link *link;
1538 link = FindLinkIn(dst_addr, alias_addr,
1539 NO_DEST_PORT, 0,
1540 proto, 1);
1542 if (link == NULL && !(packetAliasMode & PKT_ALIAS_DENY_INCOMING))
1544 struct in_addr target_addr;
1546 target_addr = FindOriginalAddress(alias_addr);
1547 link = AddLink(target_addr, dst_addr, alias_addr,
1548 NO_SRC_PORT, NO_DEST_PORT, 0,
1549 proto);
1552 return (link);
1556 struct alias_link *
1557 FindProtoOut(struct in_addr src_addr,
1558 struct in_addr dst_addr,
1559 u_char proto)
1561 struct alias_link *link;
1563 link = FindLinkOut(src_addr, dst_addr,
1564 NO_SRC_PORT, NO_DEST_PORT,
1565 proto, 1);
1567 if (link == NULL)
1569 struct in_addr alias_addr;
1571 alias_addr = FindAliasAddress(src_addr);
1572 link = AddLink(src_addr, dst_addr, alias_addr,
1573 NO_SRC_PORT, NO_DEST_PORT, 0,
1574 proto);
1577 return (link);
1581 struct alias_link *
1582 FindUdpTcpIn(struct in_addr dst_addr,
1583 struct in_addr alias_addr,
1584 u_short dst_port,
1585 u_short alias_port,
1586 u_char proto,
1587 int create)
1589 int link_type;
1590 struct alias_link *link;
1592 switch (proto)
1594 case IPPROTO_UDP:
1595 link_type = LINK_UDP;
1596 break;
1597 case IPPROTO_TCP:
1598 link_type = LINK_TCP;
1599 break;
1600 default:
1601 return NULL;
1602 break;
1605 link = FindLinkIn(dst_addr, alias_addr,
1606 dst_port, alias_port,
1607 link_type, create);
1609 if (link == NULL && create && !(packetAliasMode & PKT_ALIAS_DENY_INCOMING))
1611 struct in_addr target_addr;
1613 target_addr = FindOriginalAddress(alias_addr);
1614 link = AddLink(target_addr, dst_addr, alias_addr,
1615 alias_port, dst_port, alias_port,
1616 link_type);
1619 return(link);
1623 struct alias_link *
1624 FindUdpTcpOut(struct in_addr src_addr,
1625 struct in_addr dst_addr,
1626 u_short src_port,
1627 u_short dst_port,
1628 u_char proto,
1629 int create)
1631 int link_type;
1632 struct alias_link *link;
1634 switch (proto)
1636 case IPPROTO_UDP:
1637 link_type = LINK_UDP;
1638 break;
1639 case IPPROTO_TCP:
1640 link_type = LINK_TCP;
1641 break;
1642 default:
1643 return NULL;
1644 break;
1647 link = FindLinkOut(src_addr, dst_addr, src_port, dst_port, link_type, create);
1649 if (link == NULL && create)
1651 struct in_addr alias_addr;
1653 alias_addr = FindAliasAddress(src_addr);
1654 link = AddLink(src_addr, dst_addr, alias_addr,
1655 src_port, dst_port, GET_ALIAS_PORT,
1656 link_type);
1659 return(link);
1663 struct alias_link *
1664 AddPptp(struct in_addr src_addr,
1665 struct in_addr dst_addr,
1666 struct in_addr alias_addr,
1667 u_int16_t src_call_id)
1669 struct alias_link *link;
1671 link = AddLink(src_addr, dst_addr, alias_addr,
1672 src_call_id, 0, GET_ALIAS_PORT,
1673 LINK_PPTP);
1675 return (link);
1679 struct alias_link *
1680 FindPptpOutByCallId(struct in_addr src_addr,
1681 struct in_addr dst_addr,
1682 u_int16_t src_call_id)
1684 u_int i;
1685 struct alias_link *link;
1687 i = StartPointOut(src_addr, dst_addr, 0, 0, LINK_PPTP);
1688 LIST_FOREACH(link, &linkTableOut[i], list_out)
1689 if (link->link_type == LINK_PPTP &&
1690 link->src_addr.s_addr == src_addr.s_addr &&
1691 link->dst_addr.s_addr == dst_addr.s_addr &&
1692 link->src_port == src_call_id)
1693 break;
1695 return (link);
1699 struct alias_link *
1700 FindPptpOutByPeerCallId(struct in_addr src_addr,
1701 struct in_addr dst_addr,
1702 u_int16_t dst_call_id)
1704 u_int i;
1705 struct alias_link *link;
1707 i = StartPointOut(src_addr, dst_addr, 0, 0, LINK_PPTP);
1708 LIST_FOREACH(link, &linkTableOut[i], list_out)
1709 if (link->link_type == LINK_PPTP &&
1710 link->src_addr.s_addr == src_addr.s_addr &&
1711 link->dst_addr.s_addr == dst_addr.s_addr &&
1712 link->dst_port == dst_call_id)
1713 break;
1715 return (link);
1719 struct alias_link *
1720 FindPptpInByCallId(struct in_addr dst_addr,
1721 struct in_addr alias_addr,
1722 u_int16_t dst_call_id)
1724 u_int i;
1725 struct alias_link *link;
1727 i = StartPointIn(alias_addr, 0, LINK_PPTP);
1728 LIST_FOREACH(link, &linkTableIn[i], list_in)
1729 if (link->link_type == LINK_PPTP &&
1730 link->dst_addr.s_addr == dst_addr.s_addr &&
1731 link->alias_addr.s_addr == alias_addr.s_addr &&
1732 link->dst_port == dst_call_id)
1733 break;
1735 return (link);
1739 struct alias_link *
1740 FindPptpInByPeerCallId(struct in_addr dst_addr,
1741 struct in_addr alias_addr,
1742 u_int16_t alias_call_id)
1744 struct alias_link *link;
1746 link = FindLinkIn(dst_addr, alias_addr,
1747 0/* any */, alias_call_id,
1748 LINK_PPTP, 0);
1751 return (link);
1755 struct alias_link *
1756 FindRtspOut(struct in_addr src_addr,
1757 struct in_addr dst_addr,
1758 u_short src_port,
1759 u_short alias_port,
1760 u_char proto)
1762 int link_type;
1763 struct alias_link *link;
1765 switch (proto)
1767 case IPPROTO_UDP:
1768 link_type = LINK_UDP;
1769 break;
1770 case IPPROTO_TCP:
1771 link_type = LINK_TCP;
1772 break;
1773 default:
1774 return NULL;
1775 break;
1778 link = FindLinkOut(src_addr, dst_addr, src_port, 0, link_type, 1);
1780 if (link == NULL)
1782 struct in_addr alias_addr;
1784 alias_addr = FindAliasAddress(src_addr);
1785 link = AddLink(src_addr, dst_addr, alias_addr,
1786 src_port, 0, alias_port,
1787 link_type);
1790 return(link);
1794 struct in_addr
1795 FindOriginalAddress(struct in_addr alias_addr)
1797 struct alias_link *link;
1799 link = FindLinkIn(nullAddress, alias_addr,
1800 0, 0, LINK_ADDR, 0);
1801 if (link == NULL)
1803 newDefaultLink = 1;
1804 if (targetAddress.s_addr == INADDR_ANY)
1805 return alias_addr;
1806 else if (targetAddress.s_addr == INADDR_NONE)
1807 return aliasAddress;
1808 else
1809 return targetAddress;
1811 else
1813 if (link->server != NULL) { /* LSNAT link */
1814 struct in_addr src_addr;
1816 src_addr = link->server->addr;
1817 link->server = link->server->next;
1818 return (src_addr);
1819 } else if (link->src_addr.s_addr == INADDR_ANY)
1820 return aliasAddress;
1821 else
1822 return link->src_addr;
1827 struct in_addr
1828 FindAliasAddress(struct in_addr original_addr)
1830 struct alias_link *link;
1832 link = FindLinkOut(original_addr, nullAddress,
1833 0, 0, LINK_ADDR, 0);
1834 if (link == NULL)
1836 return aliasAddress;
1838 else
1840 if (link->alias_addr.s_addr == INADDR_ANY)
1841 return aliasAddress;
1842 else
1843 return link->alias_addr;
1848 /* External routines for getting or changing link data
1849 (external to alias_db.c, but internal to alias*.c)
1851 SetFragmentData(), GetFragmentData()
1852 SetFragmentPtr(), GetFragmentPtr()
1853 SetStateIn(), SetStateOut(), GetStateIn(), GetStateOut()
1854 GetOriginalAddress(), GetDestAddress(), GetAliasAddress()
1855 GetOriginalPort(), GetAliasPort()
1856 SetAckModified(), GetAckModified()
1857 GetDeltaAckIn(), GetDeltaSeqOut(), AddSeq()
1858 SetLastLineCrlfTermed(), GetLastLineCrlfTermed()
1859 SetDestCallId()
1863 void
1864 SetFragmentAddr(struct alias_link *link, struct in_addr src_addr)
1866 link->data.frag_addr = src_addr;
1870 void
1871 GetFragmentAddr(struct alias_link *link, struct in_addr *src_addr)
1873 *src_addr = link->data.frag_addr;
1877 void
1878 SetFragmentPtr(struct alias_link *link, char *fptr)
1880 link->data.frag_ptr = fptr;
1884 void
1885 GetFragmentPtr(struct alias_link *link, char **fptr)
1887 *fptr = link->data.frag_ptr;
1891 void
1892 SetStateIn(struct alias_link *link, int state)
1894 /* TCP input state */
1895 switch (state) {
1896 case ALIAS_TCP_STATE_DISCONNECTED:
1897 if (link->data.tcp->state.out != ALIAS_TCP_STATE_CONNECTED)
1898 link->expire_time = TCP_EXPIRE_DEAD;
1899 else
1900 link->expire_time = TCP_EXPIRE_SINGLEDEAD;
1901 break;
1902 case ALIAS_TCP_STATE_CONNECTED:
1903 if (link->data.tcp->state.out == ALIAS_TCP_STATE_CONNECTED)
1904 link->expire_time = TCP_EXPIRE_CONNECTED;
1905 break;
1906 default:
1907 abort();
1909 link->data.tcp->state.in = state;
1913 void
1914 SetStateOut(struct alias_link *link, int state)
1916 /* TCP output state */
1917 switch (state) {
1918 case ALIAS_TCP_STATE_DISCONNECTED:
1919 if (link->data.tcp->state.in != ALIAS_TCP_STATE_CONNECTED)
1920 link->expire_time = TCP_EXPIRE_DEAD;
1921 else
1922 link->expire_time = TCP_EXPIRE_SINGLEDEAD;
1923 break;
1924 case ALIAS_TCP_STATE_CONNECTED:
1925 if (link->data.tcp->state.in == ALIAS_TCP_STATE_CONNECTED)
1926 link->expire_time = TCP_EXPIRE_CONNECTED;
1927 break;
1928 default:
1929 abort();
1931 link->data.tcp->state.out = state;
1936 GetStateIn(struct alias_link *link)
1938 /* TCP input state */
1939 return link->data.tcp->state.in;
1944 GetStateOut(struct alias_link *link)
1946 /* TCP output state */
1947 return link->data.tcp->state.out;
1951 struct in_addr
1952 GetOriginalAddress(struct alias_link *link)
1954 if (link->src_addr.s_addr == INADDR_ANY)
1955 return aliasAddress;
1956 else
1957 return(link->src_addr);
1961 struct in_addr
1962 GetDestAddress(struct alias_link *link)
1964 return(link->dst_addr);
1968 struct in_addr
1969 GetAliasAddress(struct alias_link *link)
1971 if (link->alias_addr.s_addr == INADDR_ANY)
1972 return aliasAddress;
1973 else
1974 return link->alias_addr;
1978 struct in_addr
1979 GetDefaultAliasAddress()
1981 return aliasAddress;
1985 void
1986 SetDefaultAliasAddress(struct in_addr alias_addr)
1988 aliasAddress = alias_addr;
1992 u_short
1993 GetOriginalPort(struct alias_link *link)
1995 return(link->src_port);
1999 u_short
2000 GetAliasPort(struct alias_link *link)
2002 return(link->alias_port);
2005 #ifndef NO_FW_PUNCH
2006 static u_short
2007 GetDestPort(struct alias_link *link)
2009 return(link->dst_port);
2011 #endif
2013 void
2014 SetAckModified(struct alias_link *link)
2016 /* Indicate that ACK numbers have been modified in a TCP connection */
2017 link->data.tcp->state.ack_modified = 1;
2021 struct in_addr
2022 GetProxyAddress(struct alias_link *link)
2024 return link->proxy_addr;
2028 void
2029 SetProxyAddress(struct alias_link *link, struct in_addr addr)
2031 link->proxy_addr = addr;
2035 u_short
2036 GetProxyPort(struct alias_link *link)
2038 return link->proxy_port;
2042 void
2043 SetProxyPort(struct alias_link *link, u_short port)
2045 link->proxy_port = port;
2050 GetAckModified(struct alias_link *link)
2052 /* See if ACK numbers have been modified */
2053 return link->data.tcp->state.ack_modified;
2058 GetDeltaAckIn(struct ip *pip, struct alias_link *link)
2061 Find out how much the ACK number has been altered for an incoming
2062 TCP packet. To do this, a circular list of ACK numbers where the TCP
2063 packet size was altered is searched.
2066 int i;
2067 struct tcphdr *tc;
2068 int delta, ack_diff_min;
2069 u_long ack;
2071 tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
2072 ack = tc->th_ack;
2074 delta = 0;
2075 ack_diff_min = -1;
2076 for (i=0; i<N_LINK_TCP_DATA; i++)
2078 struct ack_data_record x;
2080 x = link->data.tcp->ack[i];
2081 if (x.active == 1)
2083 int ack_diff;
2085 ack_diff = SeqDiff(x.ack_new, ack);
2086 if (ack_diff >= 0)
2088 if (ack_diff_min >= 0)
2090 if (ack_diff < ack_diff_min)
2092 delta = x.delta;
2093 ack_diff_min = ack_diff;
2096 else
2098 delta = x.delta;
2099 ack_diff_min = ack_diff;
2104 return (delta);
2109 GetDeltaSeqOut(struct ip *pip, struct alias_link *link)
2112 Find out how much the sequence number has been altered for an outgoing
2113 TCP packet. To do this, a circular list of ACK numbers where the TCP
2114 packet size was altered is searched.
2117 int i;
2118 struct tcphdr *tc;
2119 int delta, seq_diff_min;
2120 u_long seq;
2122 tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
2123 seq = tc->th_seq;
2125 delta = 0;
2126 seq_diff_min = -1;
2127 for (i=0; i<N_LINK_TCP_DATA; i++)
2129 struct ack_data_record x;
2131 x = link->data.tcp->ack[i];
2132 if (x.active == 1)
2134 int seq_diff;
2136 seq_diff = SeqDiff(x.ack_old, seq);
2137 if (seq_diff >= 0)
2139 if (seq_diff_min >= 0)
2141 if (seq_diff < seq_diff_min)
2143 delta = x.delta;
2144 seq_diff_min = seq_diff;
2147 else
2149 delta = x.delta;
2150 seq_diff_min = seq_diff;
2155 return (delta);
2159 void
2160 AddSeq(struct ip *pip, struct alias_link *link, int delta)
2163 When a TCP packet has been altered in length, save this
2164 information in a circular list. If enough packets have
2165 been altered, then this list will begin to overwrite itself.
2168 struct tcphdr *tc;
2169 struct ack_data_record x;
2170 int hlen, tlen, dlen;
2171 int i;
2173 tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
2175 hlen = (pip->ip_hl + tc->th_off) << 2;
2176 tlen = ntohs(pip->ip_len);
2177 dlen = tlen - hlen;
2179 x.ack_old = htonl(ntohl(tc->th_seq) + dlen);
2180 x.ack_new = htonl(ntohl(tc->th_seq) + dlen + delta);
2181 x.delta = delta;
2182 x.active = 1;
2184 i = link->data.tcp->state.index;
2185 link->data.tcp->ack[i] = x;
2187 i++;
2188 if (i == N_LINK_TCP_DATA)
2189 link->data.tcp->state.index = 0;
2190 else
2191 link->data.tcp->state.index = i;
2194 void
2195 SetExpire(struct alias_link *link, int expire)
2197 if (expire == 0)
2199 link->flags &= ~LINK_PERMANENT;
2200 DeleteLink(link);
2202 else if (expire == -1)
2204 link->flags |= LINK_PERMANENT;
2206 else if (expire > 0)
2208 link->expire_time = expire;
2210 else
2212 #ifdef DEBUG
2213 fprintf(stderr, "PacketAlias/SetExpire(): ");
2214 fprintf(stderr, "error in expire parameter\n");
2215 #endif
2219 void
2220 ClearCheckNewLink(void)
2222 newDefaultLink = 0;
2225 void
2226 SetLastLineCrlfTermed(struct alias_link *link, int yes)
2229 if (yes)
2230 link->flags |= LINK_LAST_LINE_CRLF_TERMED;
2231 else
2232 link->flags &= ~LINK_LAST_LINE_CRLF_TERMED;
2236 GetLastLineCrlfTermed(struct alias_link *link)
2239 return (link->flags & LINK_LAST_LINE_CRLF_TERMED);
2242 void
2243 SetDestCallId(struct alias_link *link, u_int16_t cid)
2246 deleteAllLinks = 1;
2247 link = ReLink(link, link->src_addr, link->dst_addr, link->alias_addr,
2248 link->src_port, cid, link->alias_port, link->link_type);
2249 deleteAllLinks = 0;
2253 /* Miscellaneous Functions
2255 HouseKeeping()
2256 InitPacketAliasLog()
2257 UninitPacketAliasLog()
2261 Whenever an outgoing or incoming packet is handled, HouseKeeping()
2262 is called to find and remove timed-out aliasing links. Logic exists
2263 to sweep through the entire table and linked list structure
2264 every 60 seconds.
2266 (prototype in alias_local.h)
2269 void
2270 HouseKeeping(void)
2272 int i, n, n100;
2273 struct timeval tv;
2274 struct timezone tz;
2277 * Save system time (seconds) in global variable timeStamp for
2278 * use by other functions. This is done so as not to unnecessarily
2279 * waste timeline by making system calls.
2281 gettimeofday(&tv, &tz);
2282 timeStamp = tv.tv_sec;
2284 /* Compute number of spokes (output table link chains) to cover */
2285 n100 = LINK_TABLE_OUT_SIZE * 100 + houseKeepingResidual;
2286 n100 *= timeStamp - lastCleanupTime;
2287 n100 /= ALIAS_CLEANUP_INTERVAL_SECS;
2289 n = n100/100;
2291 /* Handle different cases */
2292 if (n > ALIAS_CLEANUP_MAX_SPOKES)
2294 n = ALIAS_CLEANUP_MAX_SPOKES;
2295 lastCleanupTime = timeStamp;
2296 houseKeepingResidual = 0;
2298 for (i=0; i<n; i++)
2299 IncrementalCleanup();
2301 else if (n > 0)
2303 lastCleanupTime = timeStamp;
2304 houseKeepingResidual = n100 - 100*n;
2306 for (i=0; i<n; i++)
2307 IncrementalCleanup();
2309 else if (n < 0)
2311 #ifdef DEBUG
2312 fprintf(stderr, "PacketAlias/HouseKeeping(): ");
2313 fprintf(stderr, "something unexpected in time values\n");
2314 #endif
2315 lastCleanupTime = timeStamp;
2316 houseKeepingResidual = 0;
2321 /* Init the log file and enable logging */
2322 static void
2323 InitPacketAliasLog(void)
2325 if ((~packetAliasMode & PKT_ALIAS_LOG)
2326 && (monitorFile = fopen("/var/log/alias.log", "w")))
2328 packetAliasMode |= PKT_ALIAS_LOG;
2329 fprintf(monitorFile,
2330 "PacketAlias/InitPacketAliasLog: Packet alias logging enabled.\n");
2335 /* Close the log-file and disable logging. */
2336 static void
2337 UninitPacketAliasLog(void)
2339 if (monitorFile) {
2340 fclose(monitorFile);
2341 monitorFile = NULL;
2343 packetAliasMode &= ~PKT_ALIAS_LOG;
2351 /* Outside world interfaces
2353 -- "outside world" means other than alias*.c routines --
2355 PacketAliasRedirectPort()
2356 PacketAliasAddServer()
2357 PacketAliasRedirectProto()
2358 PacketAliasRedirectAddr()
2359 PacketAliasRedirectDelete()
2360 PacketAliasSetAddress()
2361 PacketAliasInit()
2362 PacketAliasUninit()
2363 PacketAliasSetMode()
2365 (prototypes in alias.h)
2368 /* Redirection from a specific public addr:port to a
2369 private addr:port */
2370 struct alias_link *
2371 PacketAliasRedirectPort(struct in_addr src_addr, u_short src_port,
2372 struct in_addr dst_addr, u_short dst_port,
2373 struct in_addr alias_addr, u_short alias_port,
2374 u_char proto)
2376 int link_type;
2377 struct alias_link *link;
2379 switch(proto)
2381 case IPPROTO_UDP:
2382 link_type = LINK_UDP;
2383 break;
2384 case IPPROTO_TCP:
2385 link_type = LINK_TCP;
2386 break;
2387 default:
2388 #ifdef DEBUG
2389 fprintf(stderr, "PacketAliasRedirectPort(): ");
2390 fprintf(stderr, "only TCP and UDP protocols allowed\n");
2391 #endif
2392 return NULL;
2395 link = AddLink(src_addr, dst_addr, alias_addr,
2396 src_port, dst_port, alias_port,
2397 link_type);
2399 if (link != NULL)
2401 link->flags |= LINK_PERMANENT;
2403 #ifdef DEBUG
2404 else
2406 fprintf(stderr, "PacketAliasRedirectPort(): "
2407 "call to AddLink() failed\n");
2409 #endif
2411 return link;
2414 /* Add server to the pool of servers */
2416 PacketAliasAddServer(struct alias_link *link, struct in_addr addr, u_short port)
2418 struct server *server;
2420 server = malloc(sizeof(struct server));
2422 if (server != NULL) {
2423 struct server *head;
2425 server->addr = addr;
2426 server->port = port;
2428 head = link->server;
2429 if (head == NULL)
2430 server->next = server;
2431 else {
2432 struct server *s;
2434 for (s = head; s->next != head; s = s->next);
2435 s->next = server;
2436 server->next = head;
2438 link->server = server;
2439 return (0);
2440 } else
2441 return (-1);
2444 /* Redirect packets of a given IP protocol from a specific
2445 public address to a private address */
2446 struct alias_link *
2447 PacketAliasRedirectProto(struct in_addr src_addr,
2448 struct in_addr dst_addr,
2449 struct in_addr alias_addr,
2450 u_char proto)
2452 struct alias_link *link;
2454 link = AddLink(src_addr, dst_addr, alias_addr,
2455 NO_SRC_PORT, NO_DEST_PORT, 0,
2456 proto);
2458 if (link != NULL)
2460 link->flags |= LINK_PERMANENT;
2462 #ifdef DEBUG
2463 else
2465 fprintf(stderr, "PacketAliasRedirectProto(): "
2466 "call to AddLink() failed\n");
2468 #endif
2470 return link;
2473 /* Static address translation */
2474 struct alias_link *
2475 PacketAliasRedirectAddr(struct in_addr src_addr,
2476 struct in_addr alias_addr)
2478 struct alias_link *link;
2480 link = AddLink(src_addr, nullAddress, alias_addr,
2481 0, 0, 0,
2482 LINK_ADDR);
2484 if (link != NULL)
2486 link->flags |= LINK_PERMANENT;
2488 #ifdef DEBUG
2489 else
2491 fprintf(stderr, "PacketAliasRedirectAddr(): "
2492 "call to AddLink() failed\n");
2494 #endif
2496 return link;
2500 void
2501 PacketAliasRedirectDelete(struct alias_link *link)
2503 /* This is a dangerous function to put in the API,
2504 because an invalid pointer can crash the program. */
2506 deleteAllLinks = 1;
2507 DeleteLink(link);
2508 deleteAllLinks = 0;
2512 void
2513 PacketAliasSetAddress(struct in_addr addr)
2515 if (packetAliasMode & PKT_ALIAS_RESET_ON_ADDR_CHANGE
2516 && aliasAddress.s_addr != addr.s_addr)
2517 CleanupAliasData();
2519 aliasAddress = addr;
2523 void
2524 PacketAliasSetTarget(struct in_addr target_addr)
2526 targetAddress = target_addr;
2530 void
2531 PacketAliasInit(void)
2533 int i;
2534 struct timeval tv;
2535 struct timezone tz;
2536 static int firstCall = 1;
2538 if (firstCall == 1)
2540 gettimeofday(&tv, &tz);
2541 timeStamp = tv.tv_sec;
2542 lastCleanupTime = tv.tv_sec;
2543 houseKeepingResidual = 0;
2545 for (i=0; i<LINK_TABLE_OUT_SIZE; i++)
2546 LIST_INIT(&linkTableOut[i]);
2547 for (i=0; i<LINK_TABLE_IN_SIZE; i++)
2548 LIST_INIT(&linkTableIn[i]);
2550 atexit(PacketAliasUninit);
2551 firstCall = 0;
2553 else
2555 deleteAllLinks = 1;
2556 CleanupAliasData();
2557 deleteAllLinks = 0;
2560 aliasAddress.s_addr = INADDR_ANY;
2561 targetAddress.s_addr = INADDR_ANY;
2563 icmpLinkCount = 0;
2564 udpLinkCount = 0;
2565 tcpLinkCount = 0;
2566 pptpLinkCount = 0;
2567 protoLinkCount = 0;
2568 fragmentIdLinkCount = 0;
2569 fragmentPtrLinkCount = 0;
2570 sockCount = 0;
2572 cleanupIndex =0;
2574 packetAliasMode = PKT_ALIAS_SAME_PORTS
2575 | PKT_ALIAS_USE_SOCKETS
2576 | PKT_ALIAS_RESET_ON_ADDR_CHANGE;
2579 void
2580 PacketAliasUninit(void) {
2581 deleteAllLinks = 1;
2582 CleanupAliasData();
2583 deleteAllLinks = 0;
2584 UninitPacketAliasLog();
2585 #ifndef NO_FW_PUNCH
2586 UninitPunchFW();
2587 #endif
2591 /* Change mode for some operations */
2592 unsigned int
2593 PacketAliasSetMode(
2594 unsigned int flags, /* Which state to bring flags to */
2595 unsigned int mask /* Mask of which flags to affect (use 0 to do a
2596 probe for flag values) */
2599 /* Enable logging? */
2600 if (flags & mask & PKT_ALIAS_LOG)
2602 InitPacketAliasLog(); /* Do the enable */
2603 } else
2604 /* _Disable_ logging? */
2605 if (~flags & mask & PKT_ALIAS_LOG) {
2606 UninitPacketAliasLog();
2609 #ifndef NO_FW_PUNCH
2610 /* Start punching holes in the firewall? */
2611 if (flags & mask & PKT_ALIAS_PUNCH_FW) {
2612 InitPunchFW();
2613 } else
2614 /* Stop punching holes in the firewall? */
2615 if (~flags & mask & PKT_ALIAS_PUNCH_FW) {
2616 UninitPunchFW();
2618 #endif
2620 /* Other flags can be set/cleared without special action */
2621 packetAliasMode = (flags & mask) | (packetAliasMode & ~mask);
2622 return packetAliasMode;
2627 PacketAliasCheckNewLink(void)
2629 return newDefaultLink;
2633 #ifndef NO_FW_PUNCH
2635 /*****************
2636 Code to support firewall punching. This shouldn't really be in this
2637 file, but making variables global is evil too.
2638 ****************/
2640 /* Firewall include files */
2641 #include <net/if.h>
2642 #include <net/ipfw/ip_fw.h>
2643 #include <string.h>
2644 #include <err.h>
2646 #if IPFW2 /* support for new firewall code */
2648 * helper function, updates the pointer to cmd with the length
2649 * of the current command, and also cleans up the first word of
2650 * the new command in case it has been clobbered before.
2652 static ipfw_insn *
2653 next_cmd(ipfw_insn *cmd)
2655 cmd += F_LEN(cmd);
2656 bzero(cmd, sizeof(*cmd));
2657 return cmd;
2661 * A function to fill simple commands of size 1.
2662 * Existing flags are preserved.
2664 static ipfw_insn *
2665 fill_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode, int size,
2666 int flags, u_int16_t arg)
2668 cmd->opcode = opcode;
2669 cmd->len = ((cmd->len | flags) & (F_NOT | F_OR)) | (size & F_LEN_MASK);
2670 cmd->arg1 = arg;
2671 return next_cmd(cmd);
2674 static ipfw_insn *
2675 fill_ip(ipfw_insn *cmd1, enum ipfw_opcodes opcode, u_int32_t addr)
2677 ipfw_insn_ip *cmd = (ipfw_insn_ip *)cmd1;
2679 cmd->addr.s_addr = addr;
2680 return fill_cmd(cmd1, opcode, F_INSN_SIZE(ipfw_insn_u32), 0, 0);
2683 static ipfw_insn *
2684 fill_one_port(ipfw_insn *cmd1, enum ipfw_opcodes opcode, u_int16_t port)
2686 ipfw_insn_u16 *cmd = (ipfw_insn_u16 *)cmd1;
2688 cmd->ports[0] = cmd->ports[1] = port;
2689 return fill_cmd(cmd1, opcode, F_INSN_SIZE(ipfw_insn_u16), 0, 0);
2692 static int
2693 fill_rule(void *buf, int bufsize, int rulenum,
2694 enum ipfw_opcodes action, int proto,
2695 struct in_addr sa, u_int16_t sp, struct in_addr da, u_int16_t dp)
2697 struct ip_fw *rule = (struct ip_fw *)buf;
2698 ipfw_insn *cmd = (ipfw_insn *)rule->cmd;
2700 bzero(buf, bufsize);
2701 rule->rulenum = rulenum;
2703 cmd = fill_cmd(cmd, O_PROTO, F_INSN_SIZE(ipfw_insn), 0, proto);
2704 cmd = fill_ip(cmd, O_IP_SRC, sa.s_addr);
2705 cmd = fill_one_port(cmd, O_IP_SRCPORT, sp);
2706 cmd = fill_ip(cmd, O_IP_DST, da.s_addr);
2707 cmd = fill_one_port(cmd, O_IP_DSTPORT, dp);
2709 rule->act_ofs = (u_int32_t *)cmd - (u_int32_t *)rule->cmd;
2710 cmd = fill_cmd(cmd, action, F_INSN_SIZE(ipfw_insn), 0, 0);
2712 rule->cmd_len = (u_int32_t *)cmd - (u_int32_t *)rule->cmd;
2714 return ((void *)cmd - buf);
2716 #endif /* IPFW2 */
2718 static void ClearAllFWHoles(void);
2720 static int fireWallBaseNum; /* The first firewall entry free for our use */
2721 static int fireWallNumNums; /* How many entries can we use? */
2722 static int fireWallActiveNum; /* Which entry did we last use? */
2723 static char *fireWallField; /* bool array for entries */
2725 #define fw_setfield(field, num) \
2726 do { \
2727 (field)[(num) - fireWallBaseNum] = 1; \
2728 } /*lint -save -e717 */ while(0) /*lint -restore */
2729 #define fw_clrfield(field, num) \
2730 do { \
2731 (field)[(num) - fireWallBaseNum] = 0; \
2732 } /*lint -save -e717 */ while(0) /*lint -restore */
2733 #define fw_tstfield(field, num) ((field)[(num) - fireWallBaseNum])
2735 static void
2736 InitPunchFW(void) {
2737 fireWallField = malloc(fireWallNumNums);
2738 if (fireWallField) {
2739 memset(fireWallField, 0, fireWallNumNums);
2740 if (fireWallFD < 0) {
2741 fireWallFD = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
2743 ClearAllFWHoles();
2744 fireWallActiveNum = fireWallBaseNum;
2748 static void
2749 UninitPunchFW(void) {
2750 ClearAllFWHoles();
2751 if (fireWallFD >= 0)
2752 close(fireWallFD);
2753 fireWallFD = -1;
2754 if (fireWallField)
2755 free(fireWallField);
2756 fireWallField = NULL;
2757 packetAliasMode &= ~PKT_ALIAS_PUNCH_FW;
2760 /* Make a certain link go through the firewall */
2761 void
2762 PunchFWHole(struct alias_link *link) {
2763 int r; /* Result code */
2764 struct ip_fw rule; /* On-the-fly built rule */
2765 int fwhole; /* Where to punch hole */
2767 /* Don't do anything unless we are asked to */
2768 if ( !(packetAliasMode & PKT_ALIAS_PUNCH_FW) ||
2769 fireWallFD < 0 ||
2770 link->link_type != LINK_TCP)
2771 return;
2773 memset(&rule, 0, sizeof rule);
2775 /** Build rule **/
2777 /* Find empty slot */
2778 for (fwhole = fireWallActiveNum;
2779 fwhole < fireWallBaseNum + fireWallNumNums &&
2780 fw_tstfield(fireWallField, fwhole);
2781 fwhole++)
2783 if (fwhole == fireWallBaseNum + fireWallNumNums) {
2784 for (fwhole = fireWallBaseNum;
2785 fwhole < fireWallActiveNum &&
2786 fw_tstfield(fireWallField, fwhole);
2787 fwhole++)
2789 if (fwhole == fireWallActiveNum) {
2790 /* No rule point empty - we can't punch more holes. */
2791 fireWallActiveNum = fireWallBaseNum;
2792 #ifdef DEBUG
2793 fprintf(stderr, "libalias: Unable to create firewall hole!\n");
2794 #endif
2795 return;
2798 /* Start next search at next position */
2799 fireWallActiveNum = fwhole+1;
2802 * generate two rules of the form
2804 * add fwhole accept tcp from OAddr OPort to DAddr DPort
2805 * add fwhole accept tcp from DAddr DPort to OAddr OPort
2807 #if IPFW2
2808 if (GetOriginalPort(link) != 0 && GetDestPort(link) != 0) {
2809 u_int32_t rulebuf[255];
2810 int i;
2812 i = fill_rule(rulebuf, sizeof(rulebuf), fwhole,
2813 O_ACCEPT, IPPROTO_TCP,
2814 GetOriginalAddress(link), ntohs(GetOriginalPort(link)),
2815 GetDestAddress(link), ntohs(GetDestPort(link)) );
2816 r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, rulebuf, i);
2817 if (r)
2818 err(1, "alias punch inbound(1) setsockopt(IP_FW_ADD)");
2820 i = fill_rule(rulebuf, sizeof(rulebuf), fwhole,
2821 O_ACCEPT, IPPROTO_TCP,
2822 GetDestAddress(link), ntohs(GetDestPort(link)),
2823 GetOriginalAddress(link), ntohs(GetOriginalPort(link)) );
2824 r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, rulebuf, i);
2825 if (r)
2826 err(1, "alias punch inbound(2) setsockopt(IP_FW_ADD)");
2828 #else /* !IPFW2, old code to generate ipfw rule */
2830 /* Build generic part of the two rules */
2831 rule.fw_number = fwhole;
2832 IP_FW_SETNSRCP(&rule, 1); /* Number of source ports. */
2833 IP_FW_SETNDSTP(&rule, 1); /* Number of destination ports. */
2834 rule.fw_flg = IP_FW_F_ACCEPT | IP_FW_F_IN | IP_FW_F_OUT;
2835 rule.fw_prot = IPPROTO_TCP;
2836 rule.fw_smsk.s_addr = INADDR_BROADCAST;
2837 rule.fw_dmsk.s_addr = INADDR_BROADCAST;
2839 /* Build and apply specific part of the rules */
2840 rule.fw_src = GetOriginalAddress(link);
2841 rule.fw_dst = GetDestAddress(link);
2842 rule.fw_uar.fw_pts[0] = ntohs(GetOriginalPort(link));
2843 rule.fw_uar.fw_pts[1] = ntohs(GetDestPort(link));
2845 /* Skip non-bound links - XXX should not be strictly necessary,
2846 but seems to leave hole if not done. Leak of non-bound links?
2847 (Code should be left even if the problem is fixed - it is a
2848 clear optimization) */
2849 if (rule.fw_uar.fw_pts[0] != 0 && rule.fw_uar.fw_pts[1] != 0) {
2850 r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, &rule, sizeof rule);
2851 #ifdef DEBUG
2852 if (r)
2853 err(1, "alias punch inbound(1) setsockopt(IP_FW_ADD)");
2854 #endif
2855 rule.fw_src = GetDestAddress(link);
2856 rule.fw_dst = GetOriginalAddress(link);
2857 rule.fw_uar.fw_pts[0] = ntohs(GetDestPort(link));
2858 rule.fw_uar.fw_pts[1] = ntohs(GetOriginalPort(link));
2859 r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, &rule, sizeof rule);
2860 #ifdef DEBUG
2861 if (r)
2862 err(1, "alias punch inbound(2) setsockopt(IP_FW_ADD)");
2863 #endif
2865 #endif /* !IPFW2 */
2866 /* Indicate hole applied */
2867 link->data.tcp->fwhole = fwhole;
2868 fw_setfield(fireWallField, fwhole);
2871 /* Remove a hole in a firewall associated with a particular alias
2872 link. Calling this too often is harmless. */
2873 static void
2874 ClearFWHole(struct alias_link *link) {
2875 if (link->link_type == LINK_TCP) {
2876 int fwhole = link->data.tcp->fwhole; /* Where is the firewall hole? */
2877 struct ip_fw rule;
2879 if (fwhole < 0)
2880 return;
2882 memset(&rule, 0, sizeof rule); /* useless for ipfw2 */
2883 #if IPFW2
2884 while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL,
2885 &fwhole, sizeof fwhole))
2887 #else /* !IPFW2 */
2888 rule.fw_number = fwhole;
2889 while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL,
2890 &rule, sizeof rule))
2892 #endif /* !IPFW2 */
2893 fw_clrfield(fireWallField, fwhole);
2894 link->data.tcp->fwhole = -1;
2898 /* Clear out the entire range dedicated to firewall holes. */
2899 static void
2900 ClearAllFWHoles(void) {
2901 struct ip_fw rule; /* On-the-fly built rule */
2902 int i;
2904 if (fireWallFD < 0)
2905 return;
2907 memset(&rule, 0, sizeof rule);
2908 for (i = fireWallBaseNum; i < fireWallBaseNum + fireWallNumNums; i++) {
2909 #if IPFW2
2910 int r = i;
2911 while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL, &r, sizeof r))
2913 #else /* !IPFW2 */
2914 rule.fw_number = i;
2915 while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule))
2917 #endif /* !IPFW2 */
2919 memset(fireWallField, 0, fireWallNumNums);
2921 #endif
2923 void
2924 PacketAliasSetFWBase(unsigned int base, unsigned int num) {
2925 #ifndef NO_FW_PUNCH
2926 fireWallBaseNum = base;
2927 fireWallNumNums = num;
2928 #endif