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