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