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