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