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