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