xref: /freebsd/sys/netinet/libalias/alias_db.c (revision 2be1a816b9ff69588e55be0a84cbe2a31efc0f2f)
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 // XXX ip free
2009 int
2010 GetDeltaAckIn(u_long ack, 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 	int delta, ack_diff_min;
2020 
2021 	delta = 0;
2022 	ack_diff_min = -1;
2023 	for (i = 0; i < N_LINK_TCP_DATA; i++) {
2024 		struct ack_data_record x;
2025 
2026 		x = lnk->data.tcp->ack[i];
2027 		if (x.active == 1) {
2028 			int ack_diff;
2029 
2030 			ack_diff = SeqDiff(x.ack_new, ack);
2031 			if (ack_diff >= 0) {
2032 				if (ack_diff_min >= 0) {
2033 					if (ack_diff < ack_diff_min) {
2034 						delta = x.delta;
2035 						ack_diff_min = ack_diff;
2036 					}
2037 				} else {
2038 					delta = x.delta;
2039 					ack_diff_min = ack_diff;
2040 				}
2041 			}
2042 		}
2043 	}
2044 	return (delta);
2045 }
2046 
2047 // XXX ip free
2048 int
2049 GetDeltaSeqOut(u_long seq, struct alias_link *lnk)
2050 {
2051 /*
2052 Find out how much the sequence number has been altered for an outgoing
2053 TCP packet.  To do this, a circular list of ACK numbers where the TCP
2054 packet size was altered is searched.
2055 */
2056 
2057 	int i;
2058 	int delta, seq_diff_min;
2059 
2060 	delta = 0;
2061 	seq_diff_min = -1;
2062 	for (i = 0; i < N_LINK_TCP_DATA; i++) {
2063 		struct ack_data_record x;
2064 
2065 		x = lnk->data.tcp->ack[i];
2066 		if (x.active == 1) {
2067 			int seq_diff;
2068 
2069 			seq_diff = SeqDiff(x.ack_old, seq);
2070 			if (seq_diff >= 0) {
2071 				if (seq_diff_min >= 0) {
2072 					if (seq_diff < seq_diff_min) {
2073 						delta = x.delta;
2074 						seq_diff_min = seq_diff;
2075 					}
2076 				} else {
2077 					delta = x.delta;
2078 					seq_diff_min = seq_diff;
2079 				}
2080 			}
2081 		}
2082 	}
2083 	return (delta);
2084 }
2085 
2086 // XXX ip free
2087 void
2088 AddSeq(struct alias_link *lnk, int delta, u_int ip_hl, u_short ip_len,
2089     u_long th_seq, u_int th_off)
2090 {
2091 /*
2092 When a TCP packet has been altered in length, save this
2093 information in a circular list.  If enough packets have
2094 been altered, then this list will begin to overwrite itself.
2095 */
2096 
2097 	struct ack_data_record x;
2098 	int hlen, tlen, dlen;
2099 	int i;
2100 
2101 	hlen = (ip_hl + th_off) << 2;
2102 	tlen = ntohs(ip_len);
2103 	dlen = tlen - hlen;
2104 
2105 	x.ack_old = htonl(ntohl(th_seq) + dlen);
2106 	x.ack_new = htonl(ntohl(th_seq) + dlen + delta);
2107 	x.delta = delta;
2108 	x.active = 1;
2109 
2110 	i = lnk->data.tcp->state.index;
2111 	lnk->data.tcp->ack[i] = x;
2112 
2113 	i++;
2114 	if (i == N_LINK_TCP_DATA)
2115 		lnk->data.tcp->state.index = 0;
2116 	else
2117 		lnk->data.tcp->state.index = i;
2118 }
2119 
2120 void
2121 SetExpire(struct alias_link *lnk, int expire)
2122 {
2123 	if (expire == 0) {
2124 		lnk->flags &= ~LINK_PERMANENT;
2125 		DeleteLink(lnk);
2126 	} else if (expire == -1) {
2127 		lnk->flags |= LINK_PERMANENT;
2128 	} else if (expire > 0) {
2129 		lnk->expire_time = expire;
2130 	} else {
2131 #ifdef LIBALIAS_DEBUG
2132 		fprintf(stderr, "PacketAlias/SetExpire(): ");
2133 		fprintf(stderr, "error in expire parameter\n");
2134 #endif
2135 	}
2136 }
2137 
2138 void
2139 ClearCheckNewLink(struct libalias *la)
2140 {
2141 
2142 	LIBALIAS_LOCK_ASSERT(la);
2143 	la->newDefaultLink = 0;
2144 }
2145 
2146 void
2147 SetProtocolFlags(struct alias_link *lnk, int pflags)
2148 {
2149 
2150 	lnk->pflags = pflags;;
2151 }
2152 
2153 int
2154 GetProtocolFlags(struct alias_link *lnk)
2155 {
2156 
2157 	return (lnk->pflags);
2158 }
2159 
2160 void
2161 SetDestCallId(struct alias_link *lnk, u_int16_t cid)
2162 {
2163 	struct libalias *la = lnk->la;
2164 
2165 	LIBALIAS_LOCK_ASSERT(la);
2166 	la->deleteAllLinks = 1;
2167 	ReLink(lnk, lnk->src_addr, lnk->dst_addr, lnk->alias_addr,
2168 	    lnk->src_port, cid, lnk->alias_port, lnk->link_type);
2169 	la->deleteAllLinks = 0;
2170 }
2171 
2172 
2173 /* Miscellaneous Functions
2174 
2175     HouseKeeping()
2176     InitPacketAliasLog()
2177     UninitPacketAliasLog()
2178 */
2179 
2180 /*
2181     Whenever an outgoing or incoming packet is handled, HouseKeeping()
2182     is called to find and remove timed-out aliasing links.  Logic exists
2183     to sweep through the entire table and linked list structure
2184     every 60 seconds.
2185 
2186     (prototype in alias_local.h)
2187 */
2188 
2189 void
2190 HouseKeeping(struct libalias *la)
2191 {
2192 	int i, n, n100;
2193 #ifndef	_KERNEL
2194 	struct timeval tv;
2195 	struct timezone tz;
2196 #endif
2197 
2198 	LIBALIAS_LOCK_ASSERT(la);
2199 	/*
2200 	 * Save system time (seconds) in global variable timeStamp for use
2201 	 * by other functions. This is done so as not to unnecessarily
2202 	 * waste timeline by making system calls.
2203 	 */
2204 #ifdef	_KERNEL
2205 	la->timeStamp = time_uptime;
2206 #else
2207 	gettimeofday(&tv, &tz);
2208 	la->timeStamp = tv.tv_sec;
2209 #endif
2210 
2211 	/* Compute number of spokes (output table link chains) to cover */
2212 	n100 = LINK_TABLE_OUT_SIZE * 100 + la->houseKeepingResidual;
2213 	n100 *= la->timeStamp - la->lastCleanupTime;
2214 	n100 /= ALIAS_CLEANUP_INTERVAL_SECS;
2215 
2216 	n = n100 / 100;
2217 
2218 	/* Handle different cases */
2219 	if (n > ALIAS_CLEANUP_MAX_SPOKES) {
2220 		n = ALIAS_CLEANUP_MAX_SPOKES;
2221 		la->lastCleanupTime = la->timeStamp;
2222 		la->houseKeepingResidual = 0;
2223 
2224 		for (i = 0; i < n; i++)
2225 			IncrementalCleanup(la);
2226 	} else if (n > 0) {
2227 		la->lastCleanupTime = la->timeStamp;
2228 		la->houseKeepingResidual = n100 - 100 * n;
2229 
2230 		for (i = 0; i < n; i++)
2231 			IncrementalCleanup(la);
2232 	} else if (n < 0) {
2233 #ifdef LIBALIAS_DEBUG
2234 		fprintf(stderr, "PacketAlias/HouseKeeping(): ");
2235 		fprintf(stderr, "something unexpected in time values\n");
2236 #endif
2237 		la->lastCleanupTime = la->timeStamp;
2238 		la->houseKeepingResidual = 0;
2239 	}
2240 }
2241 
2242 /* Init the log file and enable logging */
2243 static int
2244 InitPacketAliasLog(struct libalias *la)
2245 {
2246 
2247 	LIBALIAS_LOCK_ASSERT(la);
2248 	if (~la->packetAliasMode & PKT_ALIAS_LOG) {
2249 #ifdef _KERNEL
2250 		if ((la->logDesc = malloc(LIBALIAS_BUF_SIZE)))
2251 			;
2252 #else
2253 		if ((la->logDesc = fopen("/var/log/alias.log", "w")))
2254 			fprintf(la->logDesc, "PacketAlias/InitPacketAliasLog: Packet alias logging enabled.\n");
2255 #endif
2256 		else
2257 			return (ENOMEM); /* log initialization failed */
2258 		la->packetAliasMode |= PKT_ALIAS_LOG;
2259 	}
2260 
2261 	return (1);
2262 }
2263 
2264 /* Close the log-file and disable logging. */
2265 static void
2266 UninitPacketAliasLog(struct libalias *la)
2267 {
2268 
2269 	LIBALIAS_LOCK_ASSERT(la);
2270 	if (la->logDesc) {
2271 #ifdef _KERNEL
2272 		free(la->logDesc);
2273 #else
2274 		fclose(la->logDesc);
2275 #endif
2276 		la->logDesc = NULL;
2277 	}
2278 	la->packetAliasMode &= ~PKT_ALIAS_LOG;
2279 }
2280 
2281 /* Outside world interfaces
2282 
2283 -- "outside world" means other than alias*.c routines --
2284 
2285     PacketAliasRedirectPort()
2286     PacketAliasAddServer()
2287     PacketAliasRedirectProto()
2288     PacketAliasRedirectAddr()
2289     PacketAliasRedirectDynamic()
2290     PacketAliasRedirectDelete()
2291     PacketAliasSetAddress()
2292     PacketAliasInit()
2293     PacketAliasUninit()
2294     PacketAliasSetMode()
2295 
2296 (prototypes in alias.h)
2297 */
2298 
2299 /* Redirection from a specific public addr:port to a
2300    private addr:port */
2301 struct alias_link *
2302 LibAliasRedirectPort(struct libalias *la, struct in_addr src_addr, u_short src_port,
2303     struct in_addr dst_addr, u_short dst_port,
2304     struct in_addr alias_addr, u_short alias_port,
2305     u_char proto)
2306 {
2307 	int link_type;
2308 	struct alias_link *lnk;
2309 
2310 	LIBALIAS_LOCK(la);
2311 	switch (proto) {
2312 	case IPPROTO_UDP:
2313 		link_type = LINK_UDP;
2314 		break;
2315 	case IPPROTO_TCP:
2316 		link_type = LINK_TCP;
2317 		break;
2318 	default:
2319 #ifdef LIBALIAS_DEBUG
2320 		fprintf(stderr, "PacketAliasRedirectPort(): ");
2321 		fprintf(stderr, "only TCP and UDP protocols allowed\n");
2322 #endif
2323 		lnk = NULL;
2324 		goto getout;
2325 	}
2326 
2327 	lnk = AddLink(la, src_addr, dst_addr, alias_addr,
2328 	    src_port, dst_port, alias_port,
2329 	    link_type);
2330 
2331 	if (lnk != NULL) {
2332 		lnk->flags |= LINK_PERMANENT;
2333 	}
2334 #ifdef LIBALIAS_DEBUG
2335 	else {
2336 		fprintf(stderr, "PacketAliasRedirectPort(): "
2337 		    "call to AddLink() failed\n");
2338 	}
2339 #endif
2340 
2341 getout:
2342 	LIBALIAS_UNLOCK(la);
2343 	return (lnk);
2344 }
2345 
2346 /* Add server to the pool of servers */
2347 int
2348 LibAliasAddServer(struct libalias *la, struct alias_link *lnk, struct in_addr addr, u_short port)
2349 {
2350 	struct server *server;
2351 	int res;
2352 
2353 	LIBALIAS_LOCK(la);
2354 	(void)la;
2355 
2356 	server = malloc(sizeof(struct server));
2357 
2358 	if (server != NULL) {
2359 		struct server *head;
2360 
2361 		server->addr = addr;
2362 		server->port = port;
2363 
2364 		head = lnk->server;
2365 		if (head == NULL)
2366 			server->next = server;
2367 		else {
2368 			struct server *s;
2369 
2370 			for (s = head; s->next != head; s = s->next);
2371 			s->next = server;
2372 			server->next = head;
2373 		}
2374 		lnk->server = server;
2375 		res = 0;
2376 	} else
2377 		res = -1;
2378 
2379 	LIBALIAS_UNLOCK(la);
2380 	return (res);
2381 }
2382 
2383 /* Redirect packets of a given IP protocol from a specific
2384    public address to a private address */
2385 struct alias_link *
2386 LibAliasRedirectProto(struct libalias *la, struct in_addr src_addr,
2387     struct in_addr dst_addr,
2388     struct in_addr alias_addr,
2389     u_char proto)
2390 {
2391 	struct alias_link *lnk;
2392 
2393 	LIBALIAS_LOCK(la);
2394 	lnk = AddLink(la, src_addr, dst_addr, alias_addr,
2395 	    NO_SRC_PORT, NO_DEST_PORT, 0,
2396 	    proto);
2397 
2398 	if (lnk != NULL) {
2399 		lnk->flags |= LINK_PERMANENT;
2400 	}
2401 #ifdef LIBALIAS_DEBUG
2402 	else {
2403 		fprintf(stderr, "PacketAliasRedirectProto(): "
2404 		    "call to AddLink() failed\n");
2405 	}
2406 #endif
2407 
2408 	LIBALIAS_UNLOCK(la);
2409 	return (lnk);
2410 }
2411 
2412 /* Static address translation */
2413 struct alias_link *
2414 LibAliasRedirectAddr(struct libalias *la, struct in_addr src_addr,
2415     struct in_addr alias_addr)
2416 {
2417 	struct alias_link *lnk;
2418 
2419 	LIBALIAS_LOCK(la);
2420 	lnk = AddLink(la, src_addr, la->nullAddress, alias_addr,
2421 	    0, 0, 0,
2422 	    LINK_ADDR);
2423 
2424 	if (lnk != NULL) {
2425 		lnk->flags |= LINK_PERMANENT;
2426 	}
2427 #ifdef LIBALIAS_DEBUG
2428 	else {
2429 		fprintf(stderr, "PacketAliasRedirectAddr(): "
2430 		    "call to AddLink() failed\n");
2431 	}
2432 #endif
2433 
2434 	LIBALIAS_UNLOCK(la);
2435 	return (lnk);
2436 }
2437 
2438 
2439 /* Mark the aliasing link dynamic */
2440 int
2441 LibAliasRedirectDynamic(struct libalias *la, struct alias_link *lnk)
2442 {
2443 	int res;
2444 
2445 	LIBALIAS_LOCK(la);
2446 	(void)la;
2447 
2448 	if (lnk->flags & LINK_PARTIALLY_SPECIFIED)
2449 		res = -1;
2450 	else {
2451 		lnk->flags &= ~LINK_PERMANENT;
2452 		res = 0;
2453 	}
2454 	LIBALIAS_UNLOCK(la);
2455 	return (res);
2456 }
2457 
2458 
2459 void
2460 LibAliasRedirectDelete(struct libalias *la, struct alias_link *lnk)
2461 {
2462 /* This is a dangerous function to put in the API,
2463    because an invalid pointer can crash the program. */
2464 
2465 	LIBALIAS_LOCK(la);
2466 	la->deleteAllLinks = 1;
2467 	DeleteLink(lnk);
2468 	la->deleteAllLinks = 0;
2469 	LIBALIAS_UNLOCK(la);
2470 }
2471 
2472 
2473 void
2474 LibAliasSetAddress(struct libalias *la, struct in_addr addr)
2475 {
2476 
2477 	LIBALIAS_LOCK(la);
2478 	if (la->packetAliasMode & PKT_ALIAS_RESET_ON_ADDR_CHANGE
2479 	    && la->aliasAddress.s_addr != addr.s_addr)
2480 		CleanupAliasData(la);
2481 
2482 	la->aliasAddress = addr;
2483 	LIBALIAS_UNLOCK(la);
2484 }
2485 
2486 
2487 void
2488 LibAliasSetTarget(struct libalias *la, struct in_addr target_addr)
2489 {
2490 
2491 	LIBALIAS_LOCK(la);
2492 	la->targetAddress = target_addr;
2493 	LIBALIAS_UNLOCK(la);
2494 }
2495 
2496 static void
2497 finishoff(void)
2498 {
2499 
2500 	while (!LIST_EMPTY(&instancehead))
2501 		LibAliasUninit(LIST_FIRST(&instancehead));
2502 }
2503 
2504 struct libalias *
2505 LibAliasInit(struct libalias *la)
2506 {
2507 	int i;
2508 #ifndef	_KERNEL
2509 	struct timeval tv;
2510 	struct timezone tz;
2511 #endif
2512 
2513 	if (la == NULL) {
2514 		la = calloc(sizeof *la, 1);
2515 		if (la == NULL)
2516 			return (la);
2517 
2518 #ifndef	_KERNEL		/* kernel cleans up on module unload */
2519 		if (LIST_EMPTY(&instancehead))
2520 			atexit(finishoff);
2521 #endif
2522 		LIST_INSERT_HEAD(&instancehead, la, instancelist);
2523 
2524 #ifdef	_KERNEL
2525 		la->timeStamp = time_uptime;
2526 		la->lastCleanupTime = time_uptime;
2527 #else
2528 		gettimeofday(&tv, &tz);
2529 		la->timeStamp = tv.tv_sec;
2530 		la->lastCleanupTime = tv.tv_sec;
2531 #endif
2532 		la->houseKeepingResidual = 0;
2533 
2534 		for (i = 0; i < LINK_TABLE_OUT_SIZE; i++)
2535 			LIST_INIT(&la->linkTableOut[i]);
2536 		for (i = 0; i < LINK_TABLE_IN_SIZE; i++)
2537 			LIST_INIT(&la->linkTableIn[i]);
2538 		LIBALIAS_LOCK_INIT(la);
2539 		LIBALIAS_LOCK(la);
2540 	} else {
2541 		LIBALIAS_LOCK(la);
2542 		la->deleteAllLinks = 1;
2543 		CleanupAliasData(la);
2544 		la->deleteAllLinks = 0;
2545 	}
2546 
2547 	la->aliasAddress.s_addr = INADDR_ANY;
2548 	la->targetAddress.s_addr = INADDR_ANY;
2549 
2550 	la->icmpLinkCount = 0;
2551 	la->udpLinkCount = 0;
2552 	la->tcpLinkCount = 0;
2553 	la->pptpLinkCount = 0;
2554 	la->protoLinkCount = 0;
2555 	la->fragmentIdLinkCount = 0;
2556 	la->fragmentPtrLinkCount = 0;
2557 	la->sockCount = 0;
2558 
2559 	la->cleanupIndex = 0;
2560 
2561 	la->packetAliasMode = PKT_ALIAS_SAME_PORTS
2562 #ifndef	NO_USE_SOCKETS
2563 	    | PKT_ALIAS_USE_SOCKETS
2564 #endif
2565 	    | PKT_ALIAS_RESET_ON_ADDR_CHANGE;
2566 #ifndef NO_FW_PUNCH
2567 	la->fireWallFD = -1;
2568 #endif
2569 #ifndef _KERNEL
2570 	LibAliasRefreshModules();
2571 #endif
2572 	LIBALIAS_UNLOCK(la);
2573 	return (la);
2574 }
2575 
2576 void
2577 LibAliasUninit(struct libalias *la)
2578 {
2579 
2580 	LIBALIAS_LOCK(la);
2581 	la->deleteAllLinks = 1;
2582 	CleanupAliasData(la);
2583 	la->deleteAllLinks = 0;
2584 	UninitPacketAliasLog(la);
2585 #ifndef NO_FW_PUNCH
2586 	UninitPunchFW(la);
2587 #endif
2588 	LIST_REMOVE(la, instancelist);
2589 	LIBALIAS_UNLOCK(la);
2590 	LIBALIAS_LOCK_DESTROY(la);
2591 	free(la);
2592 }
2593 
2594 /* Change mode for some operations */
2595 unsigned int
2596 LibAliasSetMode(
2597     struct libalias *la,
2598     unsigned int flags,		/* Which state to bring flags to */
2599     unsigned int mask		/* Mask of which flags to affect (use 0 to
2600 				 * do a probe for flag values) */
2601 )
2602 {
2603 	int res = -1;
2604 
2605 	LIBALIAS_LOCK(la);
2606 /* Enable logging? */
2607 	if (flags & mask & PKT_ALIAS_LOG) {
2608 		/* Do the enable */
2609 		if (InitPacketAliasLog(la) == ENOMEM)
2610 			goto getout;
2611 	} else
2612 /* _Disable_ logging? */
2613 	if (~flags & mask & PKT_ALIAS_LOG) {
2614 		UninitPacketAliasLog(la);
2615 	}
2616 #ifndef NO_FW_PUNCH
2617 /* Start punching holes in the firewall? */
2618 	if (flags & mask & PKT_ALIAS_PUNCH_FW) {
2619 		InitPunchFW(la);
2620 	} else
2621 /* Stop punching holes in the firewall? */
2622 	if (~flags & mask & PKT_ALIAS_PUNCH_FW) {
2623 		UninitPunchFW(la);
2624 	}
2625 #endif
2626 
2627 /* Other flags can be set/cleared without special action */
2628 	la->packetAliasMode = (flags & mask) | (la->packetAliasMode & ~mask);
2629 	res = la->packetAliasMode;
2630 getout:
2631 	LIBALIAS_UNLOCK(la);
2632 	return (res);
2633 }
2634 
2635 
2636 int
2637 LibAliasCheckNewLink(struct libalias *la)
2638 {
2639 	int res;
2640 
2641 	LIBALIAS_LOCK(la);
2642 	res = la->newDefaultLink;
2643 	LIBALIAS_UNLOCK(la);
2644 	return (res);
2645 }
2646 
2647 
2648 #ifndef NO_FW_PUNCH
2649 
2650 /*****************
2651   Code to support firewall punching.  This shouldn't really be in this
2652   file, but making variables global is evil too.
2653   ****************/
2654 
2655 /* Firewall include files */
2656 #include <net/if.h>
2657 #include <netinet/ip_fw.h>
2658 #include <string.h>
2659 #include <err.h>
2660 
2661 /*
2662  * helper function, updates the pointer to cmd with the length
2663  * of the current command, and also cleans up the first word of
2664  * the new command in case it has been clobbered before.
2665  */
2666 static ipfw_insn *
2667 next_cmd(ipfw_insn * cmd)
2668 {
2669 	cmd += F_LEN(cmd);
2670 	bzero(cmd, sizeof(*cmd));
2671 	return (cmd);
2672 }
2673 
2674 /*
2675  * A function to fill simple commands of size 1.
2676  * Existing flags are preserved.
2677  */
2678 static ipfw_insn *
2679 fill_cmd(ipfw_insn * cmd, enum ipfw_opcodes opcode, int size,
2680     int flags, u_int16_t arg)
2681 {
2682 	cmd->opcode = opcode;
2683 	cmd->len = ((cmd->len | flags) & (F_NOT | F_OR)) | (size & F_LEN_MASK);
2684 	cmd->arg1 = arg;
2685 	return next_cmd(cmd);
2686 }
2687 
2688 static ipfw_insn *
2689 fill_ip(ipfw_insn * cmd1, enum ipfw_opcodes opcode, u_int32_t addr)
2690 {
2691 	ipfw_insn_ip *cmd = (ipfw_insn_ip *) cmd1;
2692 
2693 	cmd->addr.s_addr = addr;
2694 	return fill_cmd(cmd1, opcode, F_INSN_SIZE(ipfw_insn_u32), 0, 0);
2695 }
2696 
2697 static ipfw_insn *
2698 fill_one_port(ipfw_insn * cmd1, enum ipfw_opcodes opcode, u_int16_t port)
2699 {
2700 	ipfw_insn_u16 *cmd = (ipfw_insn_u16 *) cmd1;
2701 
2702 	cmd->ports[0] = cmd->ports[1] = port;
2703 	return fill_cmd(cmd1, opcode, F_INSN_SIZE(ipfw_insn_u16), 0, 0);
2704 }
2705 
2706 static int
2707 fill_rule(void *buf, int bufsize, int rulenum,
2708     enum ipfw_opcodes action, int proto,
2709     struct in_addr sa, u_int16_t sp, struct in_addr da, u_int16_t dp)
2710 {
2711 	struct ip_fw *rule = (struct ip_fw *)buf;
2712 	ipfw_insn *cmd = (ipfw_insn *) rule->cmd;
2713 
2714 	bzero(buf, bufsize);
2715 	rule->rulenum = rulenum;
2716 
2717 	cmd = fill_cmd(cmd, O_PROTO, F_INSN_SIZE(ipfw_insn), 0, proto);
2718 	cmd = fill_ip(cmd, O_IP_SRC, sa.s_addr);
2719 	cmd = fill_one_port(cmd, O_IP_SRCPORT, sp);
2720 	cmd = fill_ip(cmd, O_IP_DST, da.s_addr);
2721 	cmd = fill_one_port(cmd, O_IP_DSTPORT, dp);
2722 
2723 	rule->act_ofs = (u_int32_t *) cmd - (u_int32_t *) rule->cmd;
2724 	cmd = fill_cmd(cmd, action, F_INSN_SIZE(ipfw_insn), 0, 0);
2725 
2726 	rule->cmd_len = (u_int32_t *) cmd - (u_int32_t *) rule->cmd;
2727 
2728 	return ((char *)cmd - (char *)buf);
2729 }
2730 
2731 static void	ClearAllFWHoles(struct libalias *la);
2732 
2733 
2734 #define fw_setfield(la, field, num)                         \
2735 do {                                                    \
2736     (field)[(num) - la->fireWallBaseNum] = 1;               \
2737 } /*lint -save -e717 */ while(0)/* lint -restore */
2738 
2739 #define fw_clrfield(la, field, num)                         \
2740 do {                                                    \
2741     (field)[(num) - la->fireWallBaseNum] = 0;               \
2742 } /*lint -save -e717 */ while(0)/* lint -restore */
2743 
2744 #define fw_tstfield(la, field, num) ((field)[(num) - la->fireWallBaseNum])
2745 
2746 static void
2747 InitPunchFW(struct libalias *la)
2748 {
2749 
2750 	LIBALIAS_LOCK_ASSERT(la);
2751 	la->fireWallField = malloc(la->fireWallNumNums);
2752 	if (la->fireWallField) {
2753 		memset(la->fireWallField, 0, la->fireWallNumNums);
2754 		if (la->fireWallFD < 0) {
2755 			la->fireWallFD = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
2756 		}
2757 		ClearAllFWHoles(la);
2758 		la->fireWallActiveNum = la->fireWallBaseNum;
2759 	}
2760 }
2761 
2762 static void
2763 UninitPunchFW(struct libalias *la)
2764 {
2765 
2766 	LIBALIAS_LOCK_ASSERT(la);
2767 	ClearAllFWHoles(la);
2768 	if (la->fireWallFD >= 0)
2769 		close(la->fireWallFD);
2770 	la->fireWallFD = -1;
2771 	if (la->fireWallField)
2772 		free(la->fireWallField);
2773 	la->fireWallField = NULL;
2774 	la->packetAliasMode &= ~PKT_ALIAS_PUNCH_FW;
2775 }
2776 
2777 /* Make a certain link go through the firewall */
2778 void
2779 PunchFWHole(struct alias_link *lnk)
2780 {
2781 	struct libalias *la;
2782 	int r;			/* Result code */
2783 	struct ip_fw rule;	/* On-the-fly built rule */
2784 	int fwhole;		/* Where to punch hole */
2785 
2786 	LIBALIAS_LOCK_ASSERT(la);
2787 	la = lnk->la;
2788 
2789 /* Don't do anything unless we are asked to */
2790 	if (!(la->packetAliasMode & PKT_ALIAS_PUNCH_FW) ||
2791 	    la->fireWallFD < 0 ||
2792 	    lnk->link_type != LINK_TCP)
2793 		return;
2794 
2795 	memset(&rule, 0, sizeof rule);
2796 
2797 /** Build rule **/
2798 
2799 	/* Find empty slot */
2800 	for (fwhole = la->fireWallActiveNum;
2801 	    fwhole < la->fireWallBaseNum + la->fireWallNumNums &&
2802 	    fw_tstfield(la, la->fireWallField, fwhole);
2803 	    fwhole++);
2804 	if (fwhole == la->fireWallBaseNum + la->fireWallNumNums) {
2805 		for (fwhole = la->fireWallBaseNum;
2806 		    fwhole < la->fireWallActiveNum &&
2807 		    fw_tstfield(la, la->fireWallField, fwhole);
2808 		    fwhole++);
2809 		if (fwhole == la->fireWallActiveNum) {
2810 			/* No rule point empty - we can't punch more holes. */
2811 			la->fireWallActiveNum = la->fireWallBaseNum;
2812 #ifdef LIBALIAS_DEBUG
2813 			fprintf(stderr, "libalias: Unable to create firewall hole!\n");
2814 #endif
2815 			return;
2816 		}
2817 	}
2818 	/* Start next search at next position */
2819 	la->fireWallActiveNum = fwhole + 1;
2820 
2821 	/*
2822 	 * generate two rules of the form
2823 	 *
2824 	 * add fwhole accept tcp from OAddr OPort to DAddr DPort add fwhole
2825 	 * accept tcp from DAddr DPort to OAddr OPort
2826 	 */
2827 	if (GetOriginalPort(lnk) != 0 && GetDestPort(lnk) != 0) {
2828 		u_int32_t rulebuf[255];
2829 		int i;
2830 
2831 		i = fill_rule(rulebuf, sizeof(rulebuf), fwhole,
2832 		    O_ACCEPT, IPPROTO_TCP,
2833 		    GetOriginalAddress(lnk), ntohs(GetOriginalPort(lnk)),
2834 		    GetDestAddress(lnk), ntohs(GetDestPort(lnk)));
2835 		r = setsockopt(la->fireWallFD, IPPROTO_IP, IP_FW_ADD, rulebuf, i);
2836 		if (r)
2837 			err(1, "alias punch inbound(1) setsockopt(IP_FW_ADD)");
2838 
2839 		i = fill_rule(rulebuf, sizeof(rulebuf), fwhole,
2840 		    O_ACCEPT, IPPROTO_TCP,
2841 		    GetDestAddress(lnk), ntohs(GetDestPort(lnk)),
2842 		    GetOriginalAddress(lnk), ntohs(GetOriginalPort(lnk)));
2843 		r = setsockopt(la->fireWallFD, IPPROTO_IP, IP_FW_ADD, rulebuf, i);
2844 		if (r)
2845 			err(1, "alias punch inbound(2) setsockopt(IP_FW_ADD)");
2846 	}
2847 
2848 /* Indicate hole applied */
2849 	lnk->data.tcp->fwhole = fwhole;
2850 	fw_setfield(la, la->fireWallField, fwhole);
2851 }
2852 
2853 /* Remove a hole in a firewall associated with a particular alias
2854    lnk.  Calling this too often is harmless. */
2855 static void
2856 ClearFWHole(struct alias_link *lnk)
2857 {
2858 	struct libalias *la;
2859 
2860 	LIBALIAS_LOCK_ASSERT(la);
2861 	la = lnk->la;
2862 	if (lnk->link_type == LINK_TCP) {
2863 		int fwhole = lnk->data.tcp->fwhole;	/* Where is the firewall
2864 							 * hole? */
2865 		struct ip_fw rule;
2866 
2867 		if (fwhole < 0)
2868 			return;
2869 
2870 		memset(&rule, 0, sizeof rule);	/* useless for ipfw2 */
2871 		while (!setsockopt(la->fireWallFD, IPPROTO_IP, IP_FW_DEL,
2872 		    &fwhole, sizeof fwhole));
2873 		fw_clrfield(la, la->fireWallField, fwhole);
2874 		lnk->data.tcp->fwhole = -1;
2875 	}
2876 }
2877 
2878 /* Clear out the entire range dedicated to firewall holes. */
2879 static void
2880 ClearAllFWHoles(struct libalias *la)
2881 {
2882 	struct ip_fw rule;	/* On-the-fly built rule */
2883 	int i;
2884 
2885 	LIBALIAS_LOCK_ASSERT(la);
2886 	if (la->fireWallFD < 0)
2887 		return;
2888 
2889 	memset(&rule, 0, sizeof rule);
2890 	for (i = la->fireWallBaseNum; i < la->fireWallBaseNum + la->fireWallNumNums; i++) {
2891 		int r = i;
2892 
2893 		while (!setsockopt(la->fireWallFD, IPPROTO_IP, IP_FW_DEL, &r, sizeof r));
2894 	}
2895 	/* XXX: third arg correct here ? /phk */
2896 	memset(la->fireWallField, 0, la->fireWallNumNums);
2897 }
2898 
2899 #endif
2900 
2901 void
2902 LibAliasSetFWBase(struct libalias *la, unsigned int base, unsigned int num)
2903 {
2904 
2905 	LIBALIAS_LOCK(la);
2906 #ifndef NO_FW_PUNCH
2907 	la->fireWallBaseNum = base;
2908 	la->fireWallNumNums = num;
2909 #endif
2910 	LIBALIAS_UNLOCK(la);
2911 }
2912 
2913 void
2914 LibAliasSetSkinnyPort(struct libalias *la, unsigned int port)
2915 {
2916 
2917 	LIBALIAS_LOCK(la);
2918 	la->skinnyPort = port;
2919 	LIBALIAS_UNLOCK(la);
2920 }
2921