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