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