xref: /freebsd/sys/netinet/libalias/alias_db.c (revision 5ca8e32633c4ffbbcd6762e5888b6a4ba0708c6c)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
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 #ifdef _KERNEL
31 #include <machine/stdarg.h>
32 #include <sys/param.h>
33 #include <sys/kernel.h>
34 #include <sys/systm.h>
35 #include <sys/lock.h>
36 #include <sys/module.h>
37 #include <sys/rwlock.h>
38 #include <sys/syslog.h>
39 #else
40 #include <stdarg.h>
41 #include <stdlib.h>
42 #include <stdio.h>
43 #include <sys/errno.h>
44 #include <sys/time.h>
45 #include <unistd.h>
46 #endif
47 
48 #include <sys/socket.h>
49 #include <netinet/tcp.h>
50 
51 #ifdef _KERNEL
52 #include <netinet/libalias/alias.h>
53 #include <netinet/libalias/alias_local.h>
54 #include <netinet/libalias/alias_mod.h>
55 #include <net/if.h>
56 #else
57 #include "alias.h"
58 #include "alias_local.h"
59 #include "alias_mod.h"
60 #endif
61 
62 #include "alias_db.h"
63 
64 static LIST_HEAD(, libalias) instancehead = LIST_HEAD_INITIALIZER(instancehead);
65 int LibAliasTime;
66 
67 /* Kernel module definition. */
68 #ifdef _KERNEL
69 MALLOC_DEFINE(M_ALIAS, "libalias", "packet aliasing");
70 
71 MODULE_VERSION(libalias, 1);
72 
73 static int
74 alias_mod_handler(module_t mod, int type, void *data)
75 {
76 	switch (type) {
77 	case MOD_QUIESCE:
78 	case MOD_UNLOAD:
79 		finishoff();
80 	case MOD_LOAD:
81 		return (0);
82 	default:
83 		return (EINVAL);
84 	}
85 }
86 
87 static moduledata_t alias_mod = {
88        "alias", alias_mod_handler, NULL
89 };
90 
91 DECLARE_MODULE(alias, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND);
92 #endif
93 
94 SPLAY_GENERATE(splay_out, alias_link, all.out, cmp_out);
95 SPLAY_GENERATE(splay_in, group_in, in, cmp_in);
96 
97 static struct group_in *
98 StartPointIn(struct libalias *la,
99     struct in_addr alias_addr, u_short alias_port, int link_type,
100     int create)
101 {
102 	struct group_in *grp;
103 	struct group_in needle = {
104 		.alias_addr = alias_addr,
105 		.alias_port = alias_port,
106 		.link_type = link_type
107 	};
108 
109 	grp = SPLAY_FIND(splay_in, &la->linkSplayIn, &needle);
110 	if (grp != NULL || !create || (grp = malloc(sizeof(*grp))) == NULL)
111 		return (grp);
112 	grp->alias_addr = alias_addr;
113 	grp->alias_port = alias_port;
114 	grp->link_type = link_type;
115 	LIST_INIT(&grp->full);
116 	LIST_INIT(&grp->partial);
117 	SPLAY_INSERT(splay_in, &la->linkSplayIn, grp);
118 	return (grp);
119 }
120 
121 static int
122 SeqDiff(u_long x, u_long y)
123 {
124 /* Return the difference between two TCP sequence numbers
125  * This function is encapsulated in case there are any unusual
126  * arithmetic conditions that need to be considered.
127  */
128 	return (ntohl(y) - ntohl(x));
129 }
130 
131 #ifdef _KERNEL
132 static void
133 AliasLog(char *str, const char *format, ...)
134 {
135 	va_list ap;
136 
137 	va_start(ap, format);
138 	vsnprintf(str, LIBALIAS_BUF_SIZE, format, ap);
139 	va_end(ap);
140 }
141 #else
142 static void
143 AliasLog(FILE *stream, const char *format, ...)
144 {
145 	va_list ap;
146 
147 	va_start(ap, format);
148 	vfprintf(stream, format, ap);
149 	va_end(ap);
150 	fflush(stream);
151 }
152 #endif
153 
154 static void
155 ShowAliasStats(struct libalias *la)
156 {
157 	LIBALIAS_LOCK_ASSERT(la);
158 	/* Used for debugging */
159 	if (la->logDesc) {
160 		int tot  = la->icmpLinkCount + la->udpLinkCount +
161 		    (la->sctpLinkCount>>1) + /* sctp counts half associations */
162 		    la->tcpLinkCount + la->pptpLinkCount +
163 		    la->protoLinkCount + la->fragmentIdLinkCount +
164 		    la->fragmentPtrLinkCount;
165 
166 		AliasLog(la->logDesc,
167 		    "icmp=%u, udp=%u, tcp=%u, sctp=%u, pptp=%u, proto=%u, frag_id=%u frag_ptr=%u / tot=%u",
168 		    la->icmpLinkCount,
169 		    la->udpLinkCount,
170 		    la->tcpLinkCount,
171 		    la->sctpLinkCount>>1, /* sctp counts half associations */
172 		    la->pptpLinkCount,
173 		    la->protoLinkCount,
174 		    la->fragmentIdLinkCount,
175 		    la->fragmentPtrLinkCount,
176 		    tot);
177 #ifndef _KERNEL
178 		AliasLog(la->logDesc, " (sock=%u)\n", la->sockCount);
179 #endif
180 	}
181 }
182 
183 void SctpShowAliasStats(struct libalias *la)
184 {
185 	ShowAliasStats(la);
186 }
187 
188 /* get random port in network byte order */
189 static u_short
190 _RandomPort(struct libalias *la) {
191 	u_short port;
192 
193 	port = la->aliasPortLower +
194 	    arc4random_uniform(la->aliasPortLength);
195 
196 	return ntohs(port);
197 }
198 
199 /* GetNewPort() allocates port numbers.  Note that if a port number
200    is already in use, that does not mean that it cannot be used by
201    another link concurrently.  This is because GetNewPort() looks for
202    unused triplets: (dest addr, dest port, alias port). */
203 
204 static int
205 GetNewPort(struct libalias *la, struct alias_link *lnk, int alias_port_param)
206 {
207 	int i;
208 	int max_trials;
209 	u_short port;
210 
211 	LIBALIAS_LOCK_ASSERT(la);
212 	/*
213 	 * Description of alias_port_param for GetNewPort().  When
214 	 * this parameter is zero or positive, it precisely specifies
215 	 * the port number.  GetNewPort() will return this number
216 	 * without check that it is in use.
217 	*
218 	 * The aliasing port is automatically selected by one of
219 	 * two methods below:
220 	 *
221 	 * When this parameter is GET_ALIAS_PORT, it indicates to get
222 	 * a randomly selected port number.
223 	 */
224 	if (alias_port_param >= 0 && alias_port_param < 0x10000) {
225 		lnk->alias_port = (u_short) alias_port_param;
226 		return (0);
227 	}
228 	if (alias_port_param != GET_ALIAS_PORT) {
229 #ifdef LIBALIAS_DEBUG
230 		fprintf(stderr, "PacketAlias/GetNewPort(): ");
231 		fprintf(stderr, "input parameter error\n");
232 #endif
233 		return (-1);
234 	}
235 
236 	max_trials = GET_NEW_PORT_MAX_ATTEMPTS;
237 
238 	/*
239 	 * When the PKT_ALIAS_SAME_PORTS option is chosen,
240 	 * the first try will be the actual source port. If
241 	 * this is already in use, the remainder of the
242 	 * trials will be random.
243 	 */
244 	port = (la->packetAliasMode & PKT_ALIAS_SAME_PORTS)
245 	    ? lnk->src_port
246 	    : _RandomPort(la);
247 
248 	/* Port number search */
249 	for (i = 0; i < max_trials; i++, port = _RandomPort(la)) {
250 		struct group_in *grp;
251 		struct alias_link *search_result;
252 
253 		grp = StartPointIn(la, lnk->alias_addr, port, lnk->link_type, 0);
254 		if (grp == NULL)
255 			break;
256 
257 		LIST_FOREACH(search_result, &grp->full, all.in) {
258 			if (lnk->dst_addr.s_addr == search_result->dst_addr.s_addr &&
259 			    lnk->dst_port == search_result->dst_port)
260 			    break;     /* found match */
261 		}
262 		if (search_result == NULL)
263 			break;
264 	}
265 
266 	if (i >= max_trials) {
267 #ifdef LIBALIAS_DEBUG
268 		fprintf(stderr, "PacketAlias/GetNewPort(): ");
269 		fprintf(stderr, "could not find free port\n");
270 #endif
271 		return (-1);
272 	}
273 
274 #ifndef NO_USE_SOCKETS
275 	if ((la->packetAliasMode & PKT_ALIAS_USE_SOCKETS) &&
276 	    (lnk->flags & LINK_PARTIALLY_SPECIFIED) &&
277 	    ((lnk->link_type == LINK_TCP) ||
278 	     (lnk->link_type == LINK_UDP))) {
279 		if (!GetSocket(la, port, &lnk->sockfd, lnk->link_type)) {
280 			return (-1);
281 		}
282 	}
283 #endif
284 	lnk->alias_port = port;
285 
286 	return (0);
287 }
288 
289 #ifndef NO_USE_SOCKETS
290 static		u_short
291 GetSocket(struct libalias *la, u_short port_net, int *sockfd, int link_type)
292 {
293 	int err;
294 	int sock;
295 	struct sockaddr_in sock_addr;
296 
297 	LIBALIAS_LOCK_ASSERT(la);
298 	if (link_type == LINK_TCP)
299 		sock = socket(AF_INET, SOCK_STREAM, 0);
300 	else if (link_type == LINK_UDP)
301 		sock = socket(AF_INET, SOCK_DGRAM, 0);
302 	else {
303 #ifdef LIBALIAS_DEBUG
304 		fprintf(stderr, "PacketAlias/GetSocket(): ");
305 		fprintf(stderr, "incorrect link type\n");
306 #endif
307 		return (0);
308 	}
309 
310 	if (sock < 0) {
311 #ifdef LIBALIAS_DEBUG
312 		fprintf(stderr, "PacketAlias/GetSocket(): ");
313 		fprintf(stderr, "socket() error %d\n", *sockfd);
314 #endif
315 		return (0);
316 	}
317 	sock_addr.sin_family = AF_INET;
318 	sock_addr.sin_addr.s_addr = htonl(INADDR_ANY);
319 	sock_addr.sin_port = port_net;
320 
321 	err = bind(sock,
322 	    (struct sockaddr *)&sock_addr,
323 	    sizeof(sock_addr));
324 	if (err == 0) {
325 		la->sockCount++;
326 		*sockfd = sock;
327 		return (1);
328 	} else {
329 		close(sock);
330 		return (0);
331 	}
332 }
333 #endif
334 
335 /* FindNewPortGroup() returns a base port number for an available
336    range of contiguous port numbers. Note that if a port number
337    is already in use, that does not mean that it cannot be used by
338    another link concurrently.  This is because FindNewPortGroup()
339    looks for unused triplets: (dest addr, dest port, alias port). */
340 
341 int
342 FindNewPortGroup(struct libalias *la,
343     struct in_addr dst_addr,
344     struct in_addr alias_addr,
345     u_short src_port,
346     u_short dst_port,
347     u_short port_count,
348     u_char proto,
349     u_char align)
350 {
351 	int i, j;
352 	int max_trials;
353 	u_short port;
354 	int link_type;
355 
356 	LIBALIAS_LOCK_ASSERT(la);
357 	/*
358 	 * Get link_type from protocol
359 	 */
360 
361 	switch (proto) {
362 	case IPPROTO_UDP:
363 		link_type = LINK_UDP;
364 		break;
365 	case IPPROTO_TCP:
366 		link_type = LINK_TCP;
367 		break;
368 	default:
369 		return (0);
370 		break;
371 	}
372 
373 	/*
374 	 * The aliasing port is automatically selected by one of two
375 	 * methods below:
376 	 */
377 	max_trials = GET_NEW_PORT_MAX_ATTEMPTS;
378 
379 	if (la->packetAliasMode & PKT_ALIAS_SAME_PORTS) {
380 		/*
381 		 * When the ALIAS_SAME_PORTS option is chosen, the first
382 		 * try will be the actual source port. If this is already
383 		 * in use, the remainder of the trials will be random.
384 		 */
385 		port = src_port;
386 
387 	} else {
388 		port = _RandomPort(la);
389 	}
390 
391 	/* Port number search */
392 	for (i = 0; i < max_trials; i++, port = _RandomPort(la)) {
393 		struct alias_link *search_result;
394 
395 		if (align)
396 			port &= htons(0xfffe);
397 
398 		for (j = 0; j < port_count; j++) {
399 			u_short port_j = ntohs(port) + j;
400 
401 			if ((search_result = FindLinkIn(la, dst_addr,
402 			    alias_addr, dst_port, htons(port_j),
403 			    link_type, 0)) != NULL)
404 				break;
405 		}
406 
407 		/* Found a good range, return base */
408 		if (j == port_count)
409 			return (port);
410 	}
411 
412 #ifdef LIBALIAS_DEBUG
413 	fprintf(stderr, "PacketAlias/FindNewPortGroup(): ");
414 	fprintf(stderr, "could not find free port(s)\n");
415 #endif
416 
417 	return (0);
418 }
419 
420 static void
421 CleanupAliasData(struct libalias *la, int deletePermanent)
422 {
423 	struct alias_link *lnk, *lnk_tmp;
424 
425 	LIBALIAS_LOCK_ASSERT(la);
426 
427 	/* permanent entries may stay */
428 	TAILQ_FOREACH_SAFE(lnk, &la->checkExpire, expire.list, lnk_tmp)
429 		DeleteLink(&lnk, deletePermanent);
430 }
431 static void
432 CleanupLink(struct libalias *la, struct alias_link **lnk, int deletePermanent)
433 {
434 	LIBALIAS_LOCK_ASSERT(la);
435 
436 	if (lnk == NULL || *lnk == NULL)
437 		return;
438 
439 	if (LibAliasTime - (*lnk)->timestamp > (*lnk)->expire.time) {
440 		DeleteLink(lnk, deletePermanent);
441 		if ((*lnk) == NULL)
442 			return;
443 	}
444 
445 	/* move to end, swap may fail on a single entry list */
446 	TAILQ_REMOVE(&la->checkExpire, (*lnk), expire.list);
447 	TAILQ_INSERT_TAIL(&la->checkExpire, (*lnk), expire.list);
448 }
449 
450 static struct alias_link *
451 UseLink(struct libalias *la, struct alias_link *lnk)
452 {
453 	CleanupLink(la, &lnk, 0);
454 	if (lnk != NULL)
455 		lnk->timestamp = LibAliasTime;
456 	return (lnk);
457 }
458 
459 static void
460 DeleteLink(struct alias_link **plnk, int deletePermanent)
461 {
462 	struct alias_link *lnk = *plnk;
463 	struct libalias *la = lnk->la;
464 
465 	LIBALIAS_LOCK_ASSERT(la);
466 	/* Don't do anything if the link is marked permanent */
467 	if (!deletePermanent && (lnk->flags & LINK_PERMANENT))
468 		return;
469 
470 #ifndef NO_FW_PUNCH
471 	/* Delete associated firewall hole, if any */
472 	ClearFWHole(lnk);
473 #endif
474 
475 	switch (lnk->link_type) {
476 	case LINK_PPTP:
477 		LIST_REMOVE(lnk, pptp.list);
478 		break;
479 	default: {
480 		struct group_in *grp;
481 
482 		/* Free memory allocated for LSNAT server pool */
483 		if (lnk->server != NULL) {
484 			struct server *head, *curr, *next;
485 
486 			head = curr = lnk->server;
487 			do {
488 				next = curr->next;
489 				free(curr);
490 			} while ((curr = next) != head);
491 		} else {
492 			/* Adjust output table pointers */
493 			SPLAY_REMOVE(splay_out, &la->linkSplayOut, lnk);
494 		}
495 
496 		/* Adjust input table pointers */
497 		LIST_REMOVE(lnk, all.in);
498 
499 		/* Remove intermediate node, if empty */
500 		grp = StartPointIn(la, lnk->alias_addr, lnk->alias_port, lnk->link_type, 0);
501 		if (grp != NULL &&
502 		    LIST_EMPTY(&grp->full) &&
503 		    LIST_EMPTY(&grp->partial)) {
504 			SPLAY_REMOVE(splay_in, &la->linkSplayIn, grp);
505 			free(grp);
506 		}
507 	}
508 		break;
509 	}
510 
511 	/* remove from housekeeping */
512 	TAILQ_REMOVE(&la->checkExpire, lnk, expire.list);
513 
514 #ifndef NO_USE_SOCKETS
515 	/* Close socket, if one has been allocated */
516 	if (lnk->sockfd != -1) {
517 		la->sockCount--;
518 		close(lnk->sockfd);
519 	}
520 #endif
521 	/* Link-type dependent cleanup */
522 	switch (lnk->link_type) {
523 	case LINK_ICMP:
524 		la->icmpLinkCount--;
525 		break;
526 	case LINK_UDP:
527 		la->udpLinkCount--;
528 		break;
529 	case LINK_TCP:
530 		la->tcpLinkCount--;
531 		free(lnk->data.tcp);
532 		break;
533 	case LINK_PPTP:
534 		la->pptpLinkCount--;
535 		break;
536 	case LINK_FRAGMENT_ID:
537 		la->fragmentIdLinkCount--;
538 		break;
539 	case LINK_FRAGMENT_PTR:
540 		la->fragmentPtrLinkCount--;
541 		if (lnk->data.frag_ptr != NULL)
542 			free(lnk->data.frag_ptr);
543 		break;
544 	case LINK_ADDR:
545 		break;
546 	default:
547 		la->protoLinkCount--;
548 		break;
549 	}
550 
551 	/* Free memory */
552 	free(lnk);
553 	*plnk = NULL;
554 
555 	/* Write statistics, if logging enabled */
556 	if (la->packetAliasMode & PKT_ALIAS_LOG) {
557 		ShowAliasStats(la);
558 	}
559 }
560 
561 struct alias_link *
562 AddLink(struct libalias *la, struct in_addr src_addr, struct in_addr dst_addr,
563     struct in_addr alias_addr, u_short src_port, u_short dst_port,
564     int alias_port_param, int link_type)
565 {
566 	struct alias_link *lnk;
567 
568 	LIBALIAS_LOCK_ASSERT(la);
569 
570 	lnk = malloc(sizeof(struct alias_link));
571 	if (lnk == NULL) {
572 #ifdef LIBALIAS_DEBUG
573 		fprintf(stderr, "PacketAlias/AddLink(): ");
574 		fprintf(stderr, "malloc() call failed.\n");
575 #endif
576 		return (NULL);
577 	}
578 	/* Basic initialization */
579 	lnk->la = la;
580 	lnk->src_addr = src_addr;
581 	lnk->dst_addr = dst_addr;
582 	lnk->alias_addr = alias_addr;
583 	lnk->proxy_addr.s_addr = INADDR_ANY;
584 	lnk->src_port = src_port;
585 	lnk->dst_port = dst_port;
586 	lnk->proxy_port = 0;
587 	lnk->server = NULL;
588 	lnk->link_type = link_type;
589 #ifndef NO_USE_SOCKETS
590 	lnk->sockfd = -1;
591 #endif
592 	lnk->flags = 0;
593 	lnk->pflags = 0;
594 	lnk->timestamp = LibAliasTime;
595 
596 	/* Expiration time */
597 	switch (link_type) {
598 	case LINK_ICMP:
599 		lnk->expire.time = ICMP_EXPIRE_TIME;
600 		break;
601 	case LINK_UDP:
602 		lnk->expire.time = UDP_EXPIRE_TIME;
603 		break;
604 	case LINK_TCP:
605 		lnk->expire.time = TCP_EXPIRE_INITIAL;
606 		break;
607 	case LINK_FRAGMENT_ID:
608 		lnk->expire.time = FRAGMENT_ID_EXPIRE_TIME;
609 		break;
610 	case LINK_FRAGMENT_PTR:
611 		lnk->expire.time = FRAGMENT_PTR_EXPIRE_TIME;
612 		break;
613 	default:
614 		lnk->expire.time = PROTO_EXPIRE_TIME;
615 		break;
616 	}
617 
618 	/* Determine alias flags */
619 	if (dst_addr.s_addr == INADDR_ANY)
620 		lnk->flags |= LINK_UNKNOWN_DEST_ADDR;
621 	if (dst_port == 0)
622 		lnk->flags |= LINK_UNKNOWN_DEST_PORT;
623 
624 	/* Determine alias port */
625 	if (GetNewPort(la, lnk, alias_port_param) != 0) {
626 		free(lnk);
627 		return (NULL);
628 	}
629 	/* Link-type dependent initialization */
630 	switch (link_type) {
631 	case LINK_ICMP:
632 		la->icmpLinkCount++;
633 		break;
634 	case LINK_UDP:
635 		la->udpLinkCount++;
636 		break;
637 	case LINK_TCP: {
638 		struct tcp_dat *aux_tcp;
639 		int i;
640 
641 		aux_tcp = malloc(sizeof(struct tcp_dat));
642 		if (aux_tcp == NULL) {
643 #ifdef LIBALIAS_DEBUG
644 			fprintf(stderr, "PacketAlias/AddLink: ");
645 			fprintf(stderr, " cannot allocate auxiliary TCP data\n");
646 #endif
647 			free(lnk);
648 			return (NULL);
649 		}
650 
651 		la->tcpLinkCount++;
652 		aux_tcp->state.in = ALIAS_TCP_STATE_NOT_CONNECTED;
653 		aux_tcp->state.out = ALIAS_TCP_STATE_NOT_CONNECTED;
654 		aux_tcp->state.index = 0;
655 		aux_tcp->state.ack_modified = 0;
656 		for (i = 0; i < N_LINK_TCP_DATA; i++)
657 			aux_tcp->ack[i].active = 0;
658 		aux_tcp->fwhole = -1;
659 		lnk->data.tcp = aux_tcp;
660 	}
661 		break;
662 	case LINK_PPTP:
663 		la->pptpLinkCount++;
664 		break;
665 	case LINK_FRAGMENT_ID:
666 		la->fragmentIdLinkCount++;
667 		break;
668 	case LINK_FRAGMENT_PTR:
669 		la->fragmentPtrLinkCount++;
670 		break;
671 	case LINK_ADDR:
672 		break;
673 	default:
674 		la->protoLinkCount++;
675 		break;
676 	}
677 
678 	switch (link_type) {
679 	case LINK_PPTP:
680 		LIST_INSERT_HEAD(&la->pptpList, lnk, pptp.list);
681 		break;
682 	default: {
683 		struct group_in *grp;
684 
685 		grp = StartPointIn(la, alias_addr, lnk->alias_port, link_type, 1);
686 		if (grp == NULL) {
687 			free(lnk);
688 			return (NULL);
689 		}
690 
691 		/* Set up pointers for output lookup table */
692 		SPLAY_INSERT(splay_out, &la->linkSplayOut, lnk);
693 
694 		/* Set up pointers for input lookup table */
695 		if (lnk->flags & LINK_PARTIALLY_SPECIFIED)
696 			LIST_INSERT_HEAD(&grp->partial, lnk, all.in);
697 		else
698 			LIST_INSERT_HEAD(&grp->full, lnk, all.in);
699 	}
700 		break;
701 	}
702 
703 	/* Include the element into the housekeeping list */
704 	TAILQ_INSERT_TAIL(&la->checkExpire, lnk, expire.list);
705 
706 	if (la->packetAliasMode & PKT_ALIAS_LOG)
707 		ShowAliasStats(la);
708 
709 	return (lnk);
710 }
711 
712 /*
713  * If alias_port_param is less than zero, alias port will be automatically
714  * chosen. If greater than zero, equal to alias port
715  */
716 static struct alias_link *
717 ReLink(struct alias_link *old_lnk,
718     struct in_addr src_addr,
719     struct in_addr dst_addr,
720     struct in_addr alias_addr,
721     u_short src_port,
722     u_short dst_port,
723     int alias_port_param,
724     int link_type,
725     int deletePermanent)
726 {
727 	struct alias_link *new_lnk;
728 	struct libalias *la = old_lnk->la;
729 
730 	LIBALIAS_LOCK_ASSERT(la);
731 	new_lnk = AddLink(la, src_addr, dst_addr, alias_addr,
732 	    src_port, dst_port, alias_port_param,
733 	    link_type);
734 #ifndef NO_FW_PUNCH
735 	if (new_lnk != NULL &&
736 	    old_lnk->link_type == LINK_TCP &&
737 	    old_lnk->data.tcp->fwhole > 0) {
738 		PunchFWHole(new_lnk);
739 	}
740 #endif
741 	DeleteLink(&old_lnk, deletePermanent);
742 	return (new_lnk);
743 }
744 
745 static struct alias_link *
746 _SearchLinkOut(struct libalias *la, struct in_addr src_addr,
747     struct in_addr dst_addr,
748     u_short src_port,
749     u_short dst_port,
750     int link_type) {
751 	struct alias_link *lnk;
752 	struct alias_link needle = {
753 		.src_addr = src_addr,
754 		.dst_addr = dst_addr,
755 		.src_port = src_port,
756 		.dst_port = dst_port,
757 		.link_type = link_type
758 	};
759 
760 	lnk = SPLAY_FIND(splay_out, &la->linkSplayOut, &needle);
761 	return (UseLink(la, lnk));
762 }
763 
764 static struct alias_link *
765 _FindLinkOut(struct libalias *la, struct in_addr src_addr,
766     struct in_addr dst_addr,
767     u_short src_port,
768     u_short dst_port,
769     int link_type,
770     int replace_partial_links)
771 {
772 	struct alias_link *lnk;
773 
774 	LIBALIAS_LOCK_ASSERT(la);
775 	lnk = _SearchLinkOut(la, src_addr, dst_addr, src_port, dst_port, link_type);
776 	if (lnk != NULL || !replace_partial_links)
777 		return (lnk);
778 
779 	/* Search for partially specified links. */
780 	if (dst_port != 0 && dst_addr.s_addr != INADDR_ANY) {
781 		lnk = _SearchLinkOut(la, src_addr, dst_addr, src_port, 0,
782 		    link_type);
783 		if (lnk == NULL)
784 			lnk = _SearchLinkOut(la, src_addr, ANY_ADDR, src_port,
785 			    dst_port, link_type);
786 	}
787 	if (lnk == NULL &&
788 	    (dst_port != 0 || dst_addr.s_addr != INADDR_ANY)) {
789 		lnk = _SearchLinkOut(la, src_addr, ANY_ADDR, src_port, 0,
790 		    link_type);
791 	}
792 	if (lnk != NULL) {
793 		lnk = ReLink(lnk,
794 		    src_addr, dst_addr, lnk->alias_addr,
795 		    src_port, dst_port, lnk->alias_port,
796 		    link_type, 0);
797 	}
798 	return (lnk);
799 }
800 
801 static struct alias_link *
802 FindLinkOut(struct libalias *la, struct in_addr src_addr,
803     struct in_addr dst_addr,
804     u_short src_port,
805     u_short dst_port,
806     int link_type,
807     int replace_partial_links)
808 {
809 	struct alias_link *lnk;
810 
811 	LIBALIAS_LOCK_ASSERT(la);
812 	lnk = _FindLinkOut(la, src_addr, dst_addr, src_port, dst_port,
813 	    link_type, replace_partial_links);
814 
815 	if (lnk == NULL) {
816 		/*
817 		 * The following allows permanent links to be specified as
818 		 * using the default source address (i.e. device interface
819 		 * address) without knowing in advance what that address
820 		 * is.
821 		 */
822 		if (la->aliasAddress.s_addr != INADDR_ANY &&
823 		    src_addr.s_addr == la->aliasAddress.s_addr) {
824 			lnk = _FindLinkOut(la, ANY_ADDR, dst_addr, src_port, dst_port,
825 			    link_type, replace_partial_links);
826 		}
827 	}
828 	return (lnk);
829 }
830 
831 static struct alias_link *
832 _FindLinkIn(struct libalias *la, struct in_addr dst_addr,
833     struct in_addr alias_addr,
834     u_short dst_port,
835     u_short alias_port,
836     int link_type,
837     int replace_partial_links)
838 {
839 	int flags_in;
840 	struct group_in *grp;
841 	struct alias_link *lnk;
842 	struct alias_link *lnk_unknown_all;
843 	struct alias_link *lnk_unknown_dst_addr;
844 	struct alias_link *lnk_unknown_dst_port;
845 	struct in_addr src_addr;
846 	u_short src_port;
847 
848 	LIBALIAS_LOCK_ASSERT(la);
849 	/* Initialize pointers */
850 	lnk_unknown_all = NULL;
851 	lnk_unknown_dst_addr = NULL;
852 	lnk_unknown_dst_port = NULL;
853 
854 	/* If either the dest addr or port is unknown, the search
855 	 * loop will have to know about this. */
856 	flags_in = 0;
857 	if (dst_addr.s_addr == INADDR_ANY)
858 		flags_in |= LINK_UNKNOWN_DEST_ADDR;
859 	if (dst_port == 0)
860 		flags_in |= LINK_UNKNOWN_DEST_PORT;
861 
862 	/* Search loop */
863 	grp = StartPointIn(la, alias_addr, alias_port, link_type, 0);
864 	if (grp == NULL)
865 		return (NULL);
866 
867 	switch (flags_in) {
868 	case 0:
869 		LIST_FOREACH(lnk, &grp->full, all.in) {
870 			if (lnk->dst_addr.s_addr == dst_addr.s_addr &&
871 			    lnk->dst_port == dst_port)
872 				return (UseLink(la, lnk));
873 		}
874 		break;
875 	case LINK_UNKNOWN_DEST_PORT:
876 		LIST_FOREACH(lnk, &grp->full, all.in) {
877 			if(lnk->dst_addr.s_addr == dst_addr.s_addr) {
878 				lnk_unknown_dst_port = lnk;
879 				break;
880 			}
881 		}
882 		break;
883 	case LINK_UNKNOWN_DEST_ADDR:
884 		LIST_FOREACH(lnk, &grp->full, all.in) {
885 			if(lnk->dst_port == dst_port) {
886 				lnk_unknown_dst_addr = lnk;
887 				break;
888 			}
889 		}
890 		break;
891 	case LINK_PARTIALLY_SPECIFIED:
892 		lnk_unknown_all = LIST_FIRST(&grp->full);
893 		break;
894 	}
895 
896 	if (lnk_unknown_dst_port == NULL) {
897 		LIST_FOREACH(lnk, &grp->partial, all.in) {
898 			int flags = (flags_in | lnk->flags) & LINK_PARTIALLY_SPECIFIED;
899 
900 			if (flags == LINK_PARTIALLY_SPECIFIED &&
901 			    lnk_unknown_all == NULL)
902 				lnk_unknown_all = lnk;
903 			if (flags == LINK_UNKNOWN_DEST_ADDR &&
904 			    lnk->dst_port == dst_port &&
905 			    lnk_unknown_dst_addr == NULL)
906 				lnk_unknown_dst_addr = lnk;
907 			if (flags == LINK_UNKNOWN_DEST_PORT &&
908 			    lnk->dst_addr.s_addr == dst_addr.s_addr) {
909 				lnk_unknown_dst_port = lnk;
910 				break;
911 			}
912 		}
913 	}
914 
915 	lnk = (lnk_unknown_dst_port != NULL) ? lnk_unknown_dst_port
916 	    : (lnk_unknown_dst_addr != NULL) ? lnk_unknown_dst_addr
917 	    : lnk_unknown_all;
918 
919 	if (lnk == NULL || !replace_partial_links)
920 		return (lnk);
921 
922 	if (lnk->server != NULL) {	/* LSNAT link */
923 		src_addr = lnk->server->addr;
924 		src_port = lnk->server->port;
925 		lnk->server = lnk->server->next;
926 	} else {
927 		src_addr = lnk->src_addr;
928 		src_port = lnk->src_port;
929 	}
930 
931 	if (link_type == LINK_SCTP) {
932 		lnk->src_addr = src_addr;
933 		lnk->src_port = src_port;
934 	} else {
935 		lnk = ReLink(lnk,
936 		    src_addr, dst_addr, alias_addr,
937 		    src_port, dst_port, alias_port,
938 		    link_type, 0);
939 	}
940 	return (lnk);
941 }
942 
943 static struct alias_link *
944 FindLinkIn(struct libalias *la, struct in_addr dst_addr,
945     struct in_addr alias_addr,
946     u_short dst_port,
947     u_short alias_port,
948     int link_type,
949     int replace_partial_links)
950 {
951 	struct alias_link *lnk;
952 
953 	LIBALIAS_LOCK_ASSERT(la);
954 	lnk = _FindLinkIn(la, dst_addr, alias_addr, dst_port, alias_port,
955 	    link_type, replace_partial_links);
956 
957 	if (lnk == NULL) {
958 		/*
959 		 * The following allows permanent links to be specified as
960 		 * using the default aliasing address (i.e. device
961 		 * interface address) without knowing in advance what that
962 		 * address is.
963 		 */
964 		if (la->aliasAddress.s_addr != INADDR_ANY &&
965 		    alias_addr.s_addr == la->aliasAddress.s_addr) {
966 			lnk = _FindLinkIn(la, dst_addr, ANY_ADDR, dst_port, alias_port,
967 			    link_type, replace_partial_links);
968 		}
969 	}
970 	return (lnk);
971 }
972 
973 /* External routines for finding/adding links
974 
975 -- "external" means outside alias_db.c, but within alias*.c --
976 
977     FindIcmpIn(), FindIcmpOut()
978     FindFragmentIn1(), FindFragmentIn2()
979     AddFragmentPtrLink(), FindFragmentPtr()
980     FindProtoIn(), FindProtoOut()
981     FindUdpTcpIn(), FindUdpTcpOut()
982     AddPptp(), FindPptpOutByCallId(), FindPptpInByCallId(),
983     FindPptpOutByPeerCallId(), FindPptpInByPeerCallId()
984     FindOriginalAddress(), FindAliasAddress()
985 
986 (prototypes in alias_local.h)
987 */
988 
989 struct alias_link *
990 FindIcmpIn(struct libalias *la, struct in_addr dst_addr,
991     struct in_addr alias_addr,
992     u_short id_alias,
993     int create)
994 {
995 	struct alias_link *lnk;
996 
997 	LIBALIAS_LOCK_ASSERT(la);
998 	lnk = FindLinkIn(la, dst_addr, alias_addr,
999 	    NO_DEST_PORT, id_alias,
1000 	    LINK_ICMP, 0);
1001 	if (lnk == NULL && create && !(la->packetAliasMode & PKT_ALIAS_DENY_INCOMING)) {
1002 		struct in_addr target_addr;
1003 
1004 		target_addr = FindOriginalAddress(la, alias_addr);
1005 		lnk = AddLink(la, target_addr, dst_addr, alias_addr,
1006 		    id_alias, NO_DEST_PORT, id_alias,
1007 		    LINK_ICMP);
1008 	}
1009 	return (lnk);
1010 }
1011 
1012 struct alias_link *
1013 FindIcmpOut(struct libalias *la, struct in_addr src_addr,
1014     struct in_addr dst_addr,
1015     u_short id,
1016     int create)
1017 {
1018 	struct alias_link *lnk;
1019 
1020 	LIBALIAS_LOCK_ASSERT(la);
1021 	lnk = FindLinkOut(la, src_addr, dst_addr,
1022 	    id, NO_DEST_PORT,
1023 	    LINK_ICMP, 0);
1024 	if (lnk == NULL && create) {
1025 		struct in_addr alias_addr;
1026 
1027 		alias_addr = FindAliasAddress(la, src_addr);
1028 		lnk = AddLink(la, src_addr, dst_addr, alias_addr,
1029 		    id, NO_DEST_PORT, GET_ALIAS_ID,
1030 		    LINK_ICMP);
1031 	}
1032 	return (lnk);
1033 }
1034 
1035 struct alias_link *
1036 FindFragmentIn1(struct libalias *la, struct in_addr dst_addr,
1037     struct in_addr alias_addr,
1038     u_short ip_id)
1039 {
1040 	struct alias_link *lnk;
1041 
1042 	LIBALIAS_LOCK_ASSERT(la);
1043 	lnk = FindLinkIn(la, dst_addr, alias_addr,
1044 	    NO_DEST_PORT, ip_id,
1045 	    LINK_FRAGMENT_ID, 0);
1046 
1047 	if (lnk == NULL) {
1048 		lnk = AddLink(la, ANY_ADDR, dst_addr, alias_addr,
1049 		    NO_SRC_PORT, NO_DEST_PORT, ip_id,
1050 		    LINK_FRAGMENT_ID);
1051 	}
1052 	return (lnk);
1053 }
1054 
1055 /* Doesn't add a link if one is not found. */
1056 struct alias_link *
1057 FindFragmentIn2(struct libalias *la, struct in_addr dst_addr,
1058     struct in_addr alias_addr, u_short ip_id)
1059 {
1060 	LIBALIAS_LOCK_ASSERT(la);
1061 	return FindLinkIn(la, dst_addr, alias_addr,
1062 	    NO_DEST_PORT, ip_id,
1063 	    LINK_FRAGMENT_ID, 0);
1064 }
1065 
1066 struct alias_link *
1067 AddFragmentPtrLink(struct libalias *la, struct in_addr dst_addr,
1068     u_short ip_id)
1069 {
1070 	LIBALIAS_LOCK_ASSERT(la);
1071 	return AddLink(la, ANY_ADDR, dst_addr, ANY_ADDR,
1072 	    NO_SRC_PORT, NO_DEST_PORT, ip_id,
1073 	    LINK_FRAGMENT_PTR);
1074 }
1075 
1076 struct alias_link *
1077 FindFragmentPtr(struct libalias *la, struct in_addr dst_addr,
1078     u_short ip_id)
1079 {
1080 	LIBALIAS_LOCK_ASSERT(la);
1081 	return FindLinkIn(la, dst_addr, ANY_ADDR,
1082 	    NO_DEST_PORT, ip_id,
1083 	    LINK_FRAGMENT_PTR, 0);
1084 }
1085 
1086 struct alias_link *
1087 FindProtoIn(struct libalias *la, struct in_addr dst_addr,
1088     struct in_addr alias_addr,
1089     u_char proto)
1090 {
1091 	struct alias_link *lnk;
1092 
1093 	LIBALIAS_LOCK_ASSERT(la);
1094 	lnk = FindLinkIn(la, dst_addr, alias_addr,
1095 	    NO_DEST_PORT, 0,
1096 	    proto, 1);
1097 
1098 	if (lnk == NULL && !(la->packetAliasMode & PKT_ALIAS_DENY_INCOMING)) {
1099 		struct in_addr target_addr;
1100 
1101 		target_addr = FindOriginalAddress(la, alias_addr);
1102 		lnk = AddLink(la, target_addr, dst_addr, alias_addr,
1103 		    NO_SRC_PORT, NO_DEST_PORT, 0,
1104 		    proto);
1105 	}
1106 	return (lnk);
1107 }
1108 
1109 struct alias_link *
1110 FindProtoOut(struct libalias *la, struct in_addr src_addr,
1111     struct in_addr dst_addr,
1112     u_char proto)
1113 {
1114 	struct alias_link *lnk;
1115 
1116 	LIBALIAS_LOCK_ASSERT(la);
1117 	lnk = FindLinkOut(la, src_addr, dst_addr,
1118 	    NO_SRC_PORT, NO_DEST_PORT,
1119 	    proto, 1);
1120 
1121 	if (lnk == NULL) {
1122 		struct in_addr alias_addr;
1123 
1124 		alias_addr = FindAliasAddress(la, src_addr);
1125 		lnk = AddLink(la, src_addr, dst_addr, alias_addr,
1126 		    NO_SRC_PORT, NO_DEST_PORT, 0,
1127 		    proto);
1128 	}
1129 	return (lnk);
1130 }
1131 
1132 struct alias_link *
1133 FindUdpTcpIn(struct libalias *la, struct in_addr dst_addr,
1134     struct in_addr alias_addr,
1135     u_short dst_port,
1136     u_short alias_port,
1137     u_char proto,
1138     int create)
1139 {
1140 	int link_type;
1141 	struct alias_link *lnk;
1142 
1143 	LIBALIAS_LOCK_ASSERT(la);
1144 	switch (proto) {
1145 	case IPPROTO_UDP:
1146 		link_type = LINK_UDP;
1147 		break;
1148 	case IPPROTO_TCP:
1149 		link_type = LINK_TCP;
1150 		break;
1151 	default:
1152 		return (NULL);
1153 		break;
1154 	}
1155 
1156 	lnk = FindLinkIn(la, dst_addr, alias_addr,
1157 	    dst_port, alias_port,
1158 	    link_type, create);
1159 
1160 	if (lnk == NULL && create && !(la->packetAliasMode & PKT_ALIAS_DENY_INCOMING)) {
1161 		struct in_addr target_addr;
1162 
1163 		target_addr = FindOriginalAddress(la, alias_addr);
1164 		lnk = AddLink(la, target_addr, dst_addr, alias_addr,
1165 		    alias_port, dst_port, alias_port,
1166 		    link_type);
1167 	}
1168 	return (lnk);
1169 }
1170 
1171 struct alias_link *
1172 FindUdpTcpOut(struct libalias *la, struct in_addr src_addr,
1173     struct in_addr dst_addr,
1174     u_short src_port,
1175     u_short dst_port,
1176     u_char proto,
1177     int create)
1178 {
1179 	int link_type;
1180 	struct alias_link *lnk;
1181 
1182 	LIBALIAS_LOCK_ASSERT(la);
1183 	switch (proto) {
1184 	case IPPROTO_UDP:
1185 		link_type = LINK_UDP;
1186 		break;
1187 	case IPPROTO_TCP:
1188 		link_type = LINK_TCP;
1189 		break;
1190 	default:
1191 		return (NULL);
1192 		break;
1193 	}
1194 
1195 	lnk = FindLinkOut(la, src_addr, dst_addr, src_port, dst_port, link_type, create);
1196 
1197 	if (lnk == NULL && create) {
1198 		struct in_addr alias_addr;
1199 
1200 		alias_addr = FindAliasAddress(la, src_addr);
1201 		lnk = AddLink(la, src_addr, dst_addr, alias_addr,
1202 		    src_port, dst_port, GET_ALIAS_PORT,
1203 		    link_type);
1204 	}
1205 	return (lnk);
1206 }
1207 
1208 struct alias_link *
1209 AddPptp(struct libalias *la, struct in_addr src_addr,
1210     struct in_addr dst_addr,
1211     struct in_addr alias_addr,
1212     u_int16_t src_call_id)
1213 {
1214 	struct alias_link *lnk;
1215 
1216 	LIBALIAS_LOCK_ASSERT(la);
1217 	lnk = AddLink(la, src_addr, dst_addr, alias_addr,
1218 	    src_call_id, 0, GET_ALIAS_PORT,
1219 	    LINK_PPTP);
1220 
1221 	return (lnk);
1222 }
1223 
1224 struct alias_link *
1225 FindPptpOutByCallId(struct libalias *la, struct in_addr src_addr,
1226     struct in_addr dst_addr,
1227     u_int16_t src_call_id)
1228 {
1229 	struct alias_link *lnk;
1230 
1231 	LIBALIAS_LOCK_ASSERT(la);
1232 	LIST_FOREACH(lnk, &la->pptpList, pptp.list)
1233 		if (lnk->src_addr.s_addr == src_addr.s_addr &&
1234 		    lnk->dst_addr.s_addr == dst_addr.s_addr &&
1235 		    lnk->src_port == src_call_id)
1236 			break;
1237 
1238 	return (UseLink(la, lnk));
1239 }
1240 
1241 struct alias_link *
1242 FindPptpOutByPeerCallId(struct libalias *la, struct in_addr src_addr,
1243     struct in_addr dst_addr,
1244     u_int16_t dst_call_id)
1245 {
1246 	struct alias_link *lnk;
1247 
1248 	LIBALIAS_LOCK_ASSERT(la);
1249 	LIST_FOREACH(lnk, &la->pptpList, pptp.list)
1250 		if (lnk->src_addr.s_addr == src_addr.s_addr &&
1251 		    lnk->dst_addr.s_addr == dst_addr.s_addr &&
1252 		    lnk->dst_port == dst_call_id)
1253 			break;
1254 
1255 	return (UseLink(la, lnk));
1256 }
1257 
1258 struct alias_link *
1259 FindPptpInByCallId(struct libalias *la, struct in_addr dst_addr,
1260     struct in_addr alias_addr,
1261     u_int16_t dst_call_id)
1262 {
1263 	struct alias_link *lnk;
1264 
1265 	LIBALIAS_LOCK_ASSERT(la);
1266 
1267 	LIST_FOREACH(lnk, &la->pptpList, pptp.list)
1268 		if (lnk->dst_port == dst_call_id &&
1269 		    lnk->dst_addr.s_addr == dst_addr.s_addr &&
1270 		    lnk->alias_addr.s_addr == alias_addr.s_addr)
1271 			break;
1272 
1273 	return (UseLink(la, lnk));
1274 }
1275 
1276 struct alias_link *
1277 FindPptpInByPeerCallId(struct libalias *la, struct in_addr dst_addr,
1278     struct in_addr alias_addr,
1279     u_int16_t alias_call_id)
1280 {
1281 	struct alias_link *lnk;
1282 
1283 	LIBALIAS_LOCK_ASSERT(la);
1284 	LIST_FOREACH(lnk, &la->pptpList, pptp.list)
1285 		if (lnk->alias_port == alias_call_id &&
1286 		    lnk->dst_addr.s_addr == dst_addr.s_addr &&
1287 		    lnk->alias_addr.s_addr == alias_addr.s_addr)
1288 			break;
1289 
1290 	return (lnk);
1291 }
1292 
1293 struct alias_link *
1294 FindRtspOut(struct libalias *la, struct in_addr src_addr,
1295     struct in_addr dst_addr,
1296     u_short src_port,
1297     u_short alias_port,
1298     u_char proto)
1299 {
1300 	int link_type;
1301 	struct alias_link *lnk;
1302 
1303 	LIBALIAS_LOCK_ASSERT(la);
1304 	switch (proto) {
1305 	case IPPROTO_UDP:
1306 		link_type = LINK_UDP;
1307 		break;
1308 	case IPPROTO_TCP:
1309 		link_type = LINK_TCP;
1310 		break;
1311 	default:
1312 		return (NULL);
1313 		break;
1314 	}
1315 
1316 	lnk = FindLinkOut(la, src_addr, dst_addr, src_port, 0, link_type, 1);
1317 
1318 	if (lnk == NULL) {
1319 		struct in_addr alias_addr;
1320 
1321 		alias_addr = FindAliasAddress(la, src_addr);
1322 		lnk = AddLink(la, src_addr, dst_addr, alias_addr,
1323 		    src_port, 0, alias_port,
1324 		    link_type);
1325 	}
1326 	return (lnk);
1327 }
1328 
1329 struct in_addr
1330 FindOriginalAddress(struct libalias *la, struct in_addr alias_addr)
1331 {
1332 	struct alias_link *lnk;
1333 
1334 	LIBALIAS_LOCK_ASSERT(la);
1335 	lnk = FindLinkIn(la, ANY_ADDR, alias_addr,
1336 	    0, 0, LINK_ADDR, 0);
1337 	if (lnk == NULL) {
1338 		if (la->targetAddress.s_addr == INADDR_ANY)
1339 			return (alias_addr);
1340 		else if (la->targetAddress.s_addr == INADDR_NONE)
1341 			return (la->aliasAddress.s_addr != INADDR_ANY) ?
1342 			    la->aliasAddress : alias_addr;
1343 		else
1344 			return (la->targetAddress);
1345 	} else {
1346 		if (lnk->server != NULL) {	/* LSNAT link */
1347 			struct in_addr src_addr;
1348 
1349 			src_addr = lnk->server->addr;
1350 			lnk->server = lnk->server->next;
1351 			return (src_addr);
1352 		} else if (lnk->src_addr.s_addr == INADDR_ANY)
1353 			return (la->aliasAddress.s_addr != INADDR_ANY) ?
1354 			    la->aliasAddress : alias_addr;
1355 		else
1356 			return (lnk->src_addr);
1357 	}
1358 }
1359 
1360 struct in_addr
1361 FindAliasAddress(struct libalias *la, struct in_addr original_addr)
1362 {
1363 	struct alias_link *lnk;
1364 
1365 	LIBALIAS_LOCK_ASSERT(la);
1366 	lnk = FindLinkOut(la, original_addr, ANY_ADDR,
1367 	    0, 0, LINK_ADDR, 0);
1368 	if (lnk == NULL) {
1369 		return (la->aliasAddress.s_addr != INADDR_ANY) ?
1370 		    la->aliasAddress : original_addr;
1371 	} else {
1372 		if (lnk->alias_addr.s_addr == INADDR_ANY)
1373 			return (la->aliasAddress.s_addr != INADDR_ANY) ?
1374 			    la->aliasAddress : original_addr;
1375 		else
1376 			return (lnk->alias_addr);
1377 	}
1378 }
1379 
1380 /* External routines for getting or changing link data
1381    (external to alias_db.c, but internal to alias*.c)
1382 
1383     SetFragmentData(), GetFragmentData()
1384     SetFragmentPtr(), GetFragmentPtr()
1385     SetStateIn(), SetStateOut(), GetStateIn(), GetStateOut()
1386     GetOriginalAddress(), GetDestAddress(), GetAliasAddress()
1387     GetOriginalPort(), GetAliasPort()
1388     SetAckModified(), GetAckModified()
1389     GetDeltaAckIn(), GetDeltaSeqOut(), AddSeq()
1390     SetProtocolFlags(), GetProtocolFlags()
1391     SetDestCallId()
1392 */
1393 
1394 void
1395 SetFragmentAddr(struct alias_link *lnk, struct in_addr src_addr)
1396 {
1397 	lnk->data.frag_addr = src_addr;
1398 }
1399 
1400 void
1401 GetFragmentAddr(struct alias_link *lnk, struct in_addr *src_addr)
1402 {
1403 	*src_addr = lnk->data.frag_addr;
1404 }
1405 
1406 void
1407 SetFragmentPtr(struct alias_link *lnk, void *fptr)
1408 {
1409 	lnk->data.frag_ptr = fptr;
1410 }
1411 
1412 void
1413 GetFragmentPtr(struct alias_link *lnk, void **fptr)
1414 {
1415 	*fptr = lnk->data.frag_ptr;
1416 }
1417 
1418 void
1419 SetStateIn(struct alias_link *lnk, int state)
1420 {
1421 	/* TCP input state */
1422 	switch (state) {
1423 		case ALIAS_TCP_STATE_DISCONNECTED:
1424 		if (lnk->data.tcp->state.out != ALIAS_TCP_STATE_CONNECTED)
1425 			lnk->expire.time = TCP_EXPIRE_DEAD;
1426 		else
1427 			lnk->expire.time = TCP_EXPIRE_SINGLEDEAD;
1428 		break;
1429 	case ALIAS_TCP_STATE_CONNECTED:
1430 		if (lnk->data.tcp->state.out == ALIAS_TCP_STATE_CONNECTED)
1431 			lnk->expire.time = TCP_EXPIRE_CONNECTED;
1432 		break;
1433 	default:
1434 #ifdef _KERNEL
1435 		panic("libalias:SetStateIn() unknown state");
1436 #else
1437 		abort();
1438 #endif
1439 	}
1440 	lnk->data.tcp->state.in = state;
1441 }
1442 
1443 void
1444 SetStateOut(struct alias_link *lnk, int state)
1445 {
1446 	/* TCP output state */
1447 	switch (state) {
1448 		case ALIAS_TCP_STATE_DISCONNECTED:
1449 		if (lnk->data.tcp->state.in != ALIAS_TCP_STATE_CONNECTED)
1450 			lnk->expire.time = TCP_EXPIRE_DEAD;
1451 		else
1452 			lnk->expire.time = TCP_EXPIRE_SINGLEDEAD;
1453 		break;
1454 	case ALIAS_TCP_STATE_CONNECTED:
1455 		if (lnk->data.tcp->state.in == ALIAS_TCP_STATE_CONNECTED)
1456 			lnk->expire.time = TCP_EXPIRE_CONNECTED;
1457 		break;
1458 	default:
1459 #ifdef _KERNEL
1460 		panic("libalias:SetStateOut() unknown state");
1461 #else
1462 		abort();
1463 #endif
1464 	}
1465 	lnk->data.tcp->state.out = state;
1466 }
1467 
1468 int
1469 GetStateIn(struct alias_link *lnk)
1470 {
1471 	/* TCP input state */
1472 	return (lnk->data.tcp->state.in);
1473 }
1474 
1475 int
1476 GetStateOut(struct alias_link *lnk)
1477 {
1478 	/* TCP output state */
1479 	return (lnk->data.tcp->state.out);
1480 }
1481 
1482 struct in_addr
1483 GetOriginalAddress(struct alias_link *lnk)
1484 {
1485 	if (lnk->src_addr.s_addr == INADDR_ANY)
1486 		return (lnk->la->aliasAddress);
1487 	else
1488 		return (lnk->src_addr);
1489 }
1490 
1491 struct in_addr
1492 GetDestAddress(struct alias_link *lnk)
1493 {
1494 	return (lnk->dst_addr);
1495 }
1496 
1497 struct in_addr
1498 GetAliasAddress(struct alias_link *lnk)
1499 {
1500 	if (lnk->alias_addr.s_addr == INADDR_ANY)
1501 		return (lnk->la->aliasAddress);
1502 	else
1503 		return (lnk->alias_addr);
1504 }
1505 
1506 struct in_addr
1507 GetDefaultAliasAddress(struct libalias *la)
1508 {
1509 	LIBALIAS_LOCK_ASSERT(la);
1510 	return (la->aliasAddress);
1511 }
1512 
1513 void
1514 SetDefaultAliasAddress(struct libalias *la, struct in_addr alias_addr)
1515 {
1516 	LIBALIAS_LOCK_ASSERT(la);
1517 	la->aliasAddress = alias_addr;
1518 }
1519 
1520 u_short
1521 GetOriginalPort(struct alias_link *lnk)
1522 {
1523 	return (lnk->src_port);
1524 }
1525 
1526 u_short
1527 GetAliasPort(struct alias_link *lnk)
1528 {
1529 	return (lnk->alias_port);
1530 }
1531 
1532 #ifndef NO_FW_PUNCH
1533 static u_short
1534 GetDestPort(struct alias_link *lnk)
1535 {
1536 	return (lnk->dst_port);
1537 }
1538 
1539 #endif
1540 
1541 /* Indicate that ACK numbers have been modified in a TCP connection */
1542 void
1543 SetAckModified(struct alias_link *lnk)
1544 {
1545 	lnk->data.tcp->state.ack_modified = 1;
1546 }
1547 
1548 struct in_addr
1549 GetProxyAddress(struct alias_link *lnk)
1550 {
1551 	return (lnk->proxy_addr);
1552 }
1553 
1554 void
1555 SetProxyAddress(struct alias_link *lnk, struct in_addr addr)
1556 {
1557 	lnk->proxy_addr = addr;
1558 }
1559 
1560 u_short
1561 GetProxyPort(struct alias_link *lnk)
1562 {
1563 	return (lnk->proxy_port);
1564 }
1565 
1566 void
1567 SetProxyPort(struct alias_link *lnk, u_short port)
1568 {
1569 	lnk->proxy_port = port;
1570 }
1571 
1572 /* See if ACK numbers have been modified */
1573 int
1574 GetAckModified(struct alias_link *lnk)
1575 {
1576 	return (lnk->data.tcp->state.ack_modified);
1577 }
1578 
1579 /*
1580  * Find out how much the ACK number has been altered for an
1581  * incoming TCP packet.  To do this, a circular list of ACK
1582  * numbers where the TCP packet size was altered is searched.
1583  */
1584 // XXX ip free
1585 int
1586 GetDeltaAckIn(u_long ack, struct alias_link *lnk)
1587 {
1588 	int i, j;
1589 	int delta, ack_diff_min;
1590 
1591 	delta = 0;
1592 	ack_diff_min = -1;
1593 	i = lnk->data.tcp->state.index;
1594 	for (j = 0; j < N_LINK_TCP_DATA; j++) {
1595 		struct ack_data_record x;
1596 
1597 		if (i == 0)
1598 			i = N_LINK_TCP_DATA;
1599 		i--;
1600 		x = lnk->data.tcp->ack[i];
1601 		if (x.active == 1) {
1602 			int ack_diff;
1603 
1604 			ack_diff = SeqDiff(x.ack_new, ack);
1605 			if (ack_diff >= 0) {
1606 				if (ack_diff_min >= 0) {
1607 					if (ack_diff < ack_diff_min) {
1608 						delta = x.delta;
1609 						ack_diff_min = ack_diff;
1610 					}
1611 				} else {
1612 					delta = x.delta;
1613 					ack_diff_min = ack_diff;
1614 				}
1615 			}
1616 		}
1617 	}
1618 	return (delta);
1619 }
1620 
1621 /*
1622  * Find out how much the sequence number has been altered for an
1623  * outgoing TCP packet.  To do this, a circular list of ACK numbers
1624  * where the TCP packet size was altered is searched.
1625  */
1626 // XXX ip free
1627 int
1628 GetDeltaSeqOut(u_long seq, struct alias_link *lnk)
1629 {
1630 	int i, j;
1631 	int delta, seq_diff_min;
1632 
1633 	delta = 0;
1634 	seq_diff_min = -1;
1635 	i = lnk->data.tcp->state.index;
1636 	for (j = 0; j < N_LINK_TCP_DATA; j++) {
1637 		struct ack_data_record x;
1638 
1639 		if (i == 0)
1640 			i = N_LINK_TCP_DATA;
1641 		i--;
1642 		x = lnk->data.tcp->ack[i];
1643 		if (x.active == 1) {
1644 			int seq_diff;
1645 
1646 			seq_diff = SeqDiff(x.ack_old, seq);
1647 			if (seq_diff >= 0) {
1648 				if (seq_diff_min >= 0) {
1649 					if (seq_diff < seq_diff_min) {
1650 						delta = x.delta;
1651 						seq_diff_min = seq_diff;
1652 					}
1653 				} else {
1654 					delta = x.delta;
1655 					seq_diff_min = seq_diff;
1656 				}
1657 			}
1658 		}
1659 	}
1660 	return (delta);
1661 }
1662 
1663 /*
1664  * When a TCP packet has been altered in length, save this
1665  * information in a circular list.  If enough packets have been
1666  * altered, then this list will begin to overwrite itself.
1667  */
1668 // XXX ip free
1669 void
1670 AddSeq(struct alias_link *lnk, int delta, u_int ip_hl, u_short ip_len,
1671     u_long th_seq, u_int th_off)
1672 {
1673 	struct ack_data_record x;
1674 	int hlen, tlen, dlen;
1675 	int i;
1676 
1677 	hlen = (ip_hl + th_off) << 2;
1678 	tlen = ntohs(ip_len);
1679 	dlen = tlen - hlen;
1680 
1681 	x.ack_old = htonl(ntohl(th_seq) + dlen);
1682 	x.ack_new = htonl(ntohl(th_seq) + dlen + delta);
1683 	x.delta = delta;
1684 	x.active = 1;
1685 
1686 	i = lnk->data.tcp->state.index;
1687 	lnk->data.tcp->ack[i] = x;
1688 
1689 	i++;
1690 	if (i == N_LINK_TCP_DATA)
1691 		lnk->data.tcp->state.index = 0;
1692 	else
1693 		lnk->data.tcp->state.index = i;
1694 }
1695 
1696 void
1697 SetExpire(struct alias_link *lnk, int expire)
1698 {
1699 	if (expire == 0) {
1700 		lnk->flags &= ~LINK_PERMANENT;
1701 		DeleteLink(&lnk, 0);
1702 	} else if (expire == -1) {
1703 		lnk->flags |= LINK_PERMANENT;
1704 	} else if (expire > 0) {
1705 		lnk->expire.time = expire;
1706 	} else {
1707 #ifdef LIBALIAS_DEBUG
1708 		fprintf(stderr, "PacketAlias/SetExpire(): ");
1709 		fprintf(stderr, "error in expire parameter\n");
1710 #endif
1711 	}
1712 }
1713 
1714 void
1715 SetProtocolFlags(struct alias_link *lnk, int pflags)
1716 {
1717 	lnk->pflags = pflags;
1718 }
1719 
1720 int
1721 GetProtocolFlags(struct alias_link *lnk)
1722 {
1723 	return (lnk->pflags);
1724 }
1725 
1726 void
1727 SetDestCallId(struct alias_link *lnk, u_int16_t cid)
1728 {
1729 	LIBALIAS_LOCK_ASSERT(lnk->la);
1730 	ReLink(lnk, lnk->src_addr, lnk->dst_addr, lnk->alias_addr,
1731 	    lnk->src_port, cid, lnk->alias_port, lnk->link_type, 1);
1732 }
1733 
1734 /* Miscellaneous Functions
1735 
1736     HouseKeeping()
1737     InitPacketAliasLog()
1738     UninitPacketAliasLog()
1739 */
1740 
1741 /*
1742     Whenever an outgoing or incoming packet is handled, HouseKeeping()
1743     is called to find and remove timed-out aliasing links.  Logic exists
1744     to sweep through the entire table and linked list structure
1745     every 60 seconds.
1746 
1747     (prototype in alias_local.h)
1748 */
1749 
1750 void
1751 HouseKeeping(struct libalias *la)
1752 {
1753 	static unsigned int packets = 0;
1754 	static unsigned int packet_limit = 1000;
1755 
1756 	LIBALIAS_LOCK_ASSERT(la);
1757 	packets++;
1758 
1759 	/*
1760 	 * User space time/gettimeofday/... is very expensive.
1761 	 * Kernel space cache trashing is unnecessary.
1762 	 *
1763 	 * Save system time (seconds) in global variable LibAliasTime
1764 	 * for use by other functions. This is done so as not to
1765 	 * unnecessarily waste timeline by making system calls.
1766 	 *
1767 	 * Reduce the amount of house keeping work substantially by
1768 	 * sampling over the packets.
1769 	 */
1770 	if (packet_limit <= 1 || packets % packet_limit == 0) {
1771 		time_t now;
1772 
1773 #ifdef _KERNEL
1774 		now = time_uptime;
1775 #else
1776 		now = time(NULL);
1777 #endif
1778 		if (now != LibAliasTime) {
1779 			/* retry three times a second */
1780 			packet_limit = packets / 3;
1781 			packets = 0;
1782 			LibAliasTime = now;
1783 		}
1784 
1785 	}
1786 	/* Do a cleanup for the first packets of the new second only */
1787 	if (packets < (la->udpLinkCount + la->tcpLinkCount)) {
1788 		struct alias_link * lnk = TAILQ_FIRST(&la->checkExpire);
1789 
1790 		CleanupLink(la, &lnk, 0);
1791 	}
1792 }
1793 
1794 /* Init the log file and enable logging */
1795 static int
1796 InitPacketAliasLog(struct libalias *la)
1797 {
1798 	LIBALIAS_LOCK_ASSERT(la);
1799 	if (~la->packetAliasMode & PKT_ALIAS_LOG) {
1800 #ifdef _KERNEL
1801 		if ((la->logDesc = malloc(LIBALIAS_BUF_SIZE)))
1802 			;
1803 #else
1804 		if ((la->logDesc = fopen("/var/log/alias.log", "w")))
1805 			fprintf(la->logDesc, "PacketAlias/InitPacketAliasLog: Packet alias logging enabled.\n");
1806 #endif
1807 		else
1808 			return (ENOMEM); /* log initialization failed */
1809 		la->packetAliasMode |= PKT_ALIAS_LOG;
1810 	}
1811 
1812 	return (1);
1813 }
1814 
1815 /* Close the log-file and disable logging. */
1816 static void
1817 UninitPacketAliasLog(struct libalias *la)
1818 {
1819 	LIBALIAS_LOCK_ASSERT(la);
1820 	if (la->logDesc) {
1821 #ifdef _KERNEL
1822 		free(la->logDesc);
1823 #else
1824 		fclose(la->logDesc);
1825 #endif
1826 		la->logDesc = NULL;
1827 	}
1828 	la->packetAliasMode &= ~PKT_ALIAS_LOG;
1829 }
1830 
1831 /* Outside world interfaces
1832 
1833 -- "outside world" means other than alias*.c routines --
1834 
1835     PacketAliasRedirectPort()
1836     PacketAliasAddServer()
1837     PacketAliasRedirectProto()
1838     PacketAliasRedirectAddr()
1839     PacketAliasRedirectDynamic()
1840     PacketAliasRedirectDelete()
1841     PacketAliasSetAddress()
1842     PacketAliasInit()
1843     PacketAliasUninit()
1844     PacketAliasSetMode()
1845 
1846 (prototypes in alias.h)
1847 */
1848 
1849 /* Redirection from a specific public addr:port to a
1850    private addr:port */
1851 struct alias_link *
1852 LibAliasRedirectPort(struct libalias *la, struct in_addr src_addr, u_short src_port,
1853     struct in_addr dst_addr, u_short dst_port,
1854     struct in_addr alias_addr, u_short alias_port,
1855     u_char proto)
1856 {
1857 	int link_type;
1858 	struct alias_link *lnk;
1859 
1860 	LIBALIAS_LOCK(la);
1861 	switch (proto) {
1862 	case IPPROTO_UDP:
1863 		link_type = LINK_UDP;
1864 		break;
1865 	case IPPROTO_TCP:
1866 		link_type = LINK_TCP;
1867 		break;
1868 	case IPPROTO_SCTP:
1869 		link_type = LINK_SCTP;
1870 		break;
1871 	default:
1872 #ifdef LIBALIAS_DEBUG
1873 		fprintf(stderr, "PacketAliasRedirectPort(): ");
1874 		fprintf(stderr, "only SCTP, TCP and UDP protocols allowed\n");
1875 #endif
1876 		lnk = NULL;
1877 		goto getout;
1878 	}
1879 
1880 	lnk = AddLink(la, src_addr, dst_addr, alias_addr,
1881 	    src_port, dst_port, alias_port,
1882 	    link_type);
1883 
1884 	if (lnk != NULL) {
1885 		lnk->flags |= LINK_PERMANENT;
1886 	}
1887 #ifdef LIBALIAS_DEBUG
1888 	else {
1889 		fprintf(stderr, "PacketAliasRedirectPort(): "
1890 		    "call to AddLink() failed\n");
1891 	}
1892 #endif
1893 
1894 getout:
1895 	LIBALIAS_UNLOCK(la);
1896 	return (lnk);
1897 }
1898 
1899 /* Add server to the pool of servers */
1900 int
1901 LibAliasAddServer(struct libalias *la, struct alias_link *lnk, struct in_addr addr, u_short port)
1902 {
1903 	struct server *server;
1904 	int res;
1905 
1906 	LIBALIAS_LOCK(la);
1907 	(void)la;
1908 
1909 	switch (lnk->link_type) {
1910 	case LINK_PPTP:
1911 		server = NULL;
1912 		break;
1913 	default:
1914 		server = malloc(sizeof(struct server));
1915 		break;
1916 	}
1917 
1918 	if (server != NULL) {
1919 		struct server *head;
1920 
1921 		server->addr = addr;
1922 		server->port = port;
1923 
1924 		head = lnk->server;
1925 		if (head == NULL) {
1926 			server->next = server;
1927 			/* not usable for outgoing connections */
1928 			SPLAY_REMOVE(splay_out, &la->linkSplayOut, lnk);
1929 		} else {
1930 			struct server *s;
1931 
1932 			for (s = head; s->next != head; s = s->next)
1933 				;
1934 			s->next = server;
1935 			server->next = head;
1936 		}
1937 		lnk->server = server;
1938 		res = 0;
1939 	} else
1940 		res = -1;
1941 
1942 	LIBALIAS_UNLOCK(la);
1943 	return (res);
1944 }
1945 
1946 /* Redirect packets of a given IP protocol from a specific
1947    public address to a private address */
1948 struct alias_link *
1949 LibAliasRedirectProto(struct libalias *la, struct in_addr src_addr,
1950     struct in_addr dst_addr,
1951     struct in_addr alias_addr,
1952     u_char proto)
1953 {
1954 	struct alias_link *lnk;
1955 
1956 	LIBALIAS_LOCK(la);
1957 	lnk = AddLink(la, src_addr, dst_addr, alias_addr,
1958 	    NO_SRC_PORT, NO_DEST_PORT, 0,
1959 	    proto);
1960 
1961 	if (lnk != NULL) {
1962 		lnk->flags |= LINK_PERMANENT;
1963 	}
1964 #ifdef LIBALIAS_DEBUG
1965 	else {
1966 		fprintf(stderr, "PacketAliasRedirectProto(): "
1967 		    "call to AddLink() failed\n");
1968 	}
1969 #endif
1970 
1971 	LIBALIAS_UNLOCK(la);
1972 	return (lnk);
1973 }
1974 
1975 /* Static address translation */
1976 struct alias_link *
1977 LibAliasRedirectAddr(struct libalias *la, struct in_addr src_addr,
1978     struct in_addr alias_addr)
1979 {
1980 	struct alias_link *lnk;
1981 
1982 	LIBALIAS_LOCK(la);
1983 	lnk = AddLink(la, src_addr, ANY_ADDR, alias_addr,
1984 	    0, 0, 0,
1985 	    LINK_ADDR);
1986 
1987 	if (lnk != NULL) {
1988 		lnk->flags |= LINK_PERMANENT;
1989 	}
1990 #ifdef LIBALIAS_DEBUG
1991 	else {
1992 		fprintf(stderr, "PacketAliasRedirectAddr(): "
1993 		    "call to AddLink() failed\n");
1994 	}
1995 #endif
1996 
1997 	LIBALIAS_UNLOCK(la);
1998 	return (lnk);
1999 }
2000 
2001 /* Mark the aliasing link dynamic */
2002 int
2003 LibAliasRedirectDynamic(struct libalias *la, struct alias_link *lnk)
2004 {
2005 	int res;
2006 
2007 	LIBALIAS_LOCK(la);
2008 	(void)la;
2009 
2010 	if (lnk->flags & LINK_PARTIALLY_SPECIFIED)
2011 		res = -1;
2012 	else {
2013 		lnk->flags &= ~LINK_PERMANENT;
2014 		res = 0;
2015 	}
2016 	LIBALIAS_UNLOCK(la);
2017 	return (res);
2018 }
2019 
2020 /* This is a dangerous function to put in the API,
2021    because an invalid pointer can crash the program. */
2022 void
2023 LibAliasRedirectDelete(struct libalias *la, struct alias_link *lnk)
2024 {
2025 	LIBALIAS_LOCK(la);
2026 	(void)la;
2027 	DeleteLink(&lnk, 1);
2028 	LIBALIAS_UNLOCK(la);
2029 }
2030 
2031 void
2032 LibAliasSetAddress(struct libalias *la, struct in_addr addr)
2033 {
2034 	LIBALIAS_LOCK(la);
2035 	if (la->packetAliasMode & PKT_ALIAS_RESET_ON_ADDR_CHANGE
2036 	    && la->aliasAddress.s_addr != addr.s_addr)
2037 		CleanupAliasData(la, 0);
2038 
2039 	la->aliasAddress = addr;
2040 	LIBALIAS_UNLOCK(la);
2041 }
2042 
2043 void
2044 LibAliasSetAliasPortRange(struct libalias *la, u_short port_low,
2045     u_short port_high)
2046 {
2047 	LIBALIAS_LOCK(la);
2048 	if (port_low) {
2049 		la->aliasPortLower = port_low;
2050 		/* Add 1 to the aliasPortLength as modulo has range of 1 to n-1 */
2051 		la->aliasPortLength = port_high - port_low + 1;
2052 	} else {
2053 		/* Set default values */
2054 		la->aliasPortLower = 0x8000;
2055 		la->aliasPortLength = 0x8000;
2056 	}
2057 	LIBALIAS_UNLOCK(la);
2058 }
2059 
2060 void
2061 LibAliasSetTarget(struct libalias *la, struct in_addr target_addr)
2062 {
2063 	LIBALIAS_LOCK(la);
2064 	la->targetAddress = target_addr;
2065 	LIBALIAS_UNLOCK(la);
2066 }
2067 
2068 static void
2069 finishoff(void)
2070 {
2071 	while (!LIST_EMPTY(&instancehead))
2072 		LibAliasUninit(LIST_FIRST(&instancehead));
2073 }
2074 
2075 struct libalias *
2076 LibAliasInit(struct libalias *la)
2077 {
2078 	if (la == NULL) {
2079 #ifdef _KERNEL
2080 #undef malloc	/* XXX: ugly */
2081 		la = malloc(sizeof *la, M_ALIAS, M_WAITOK | M_ZERO);
2082 #else
2083 		la = calloc(sizeof *la, 1);
2084 		if (la == NULL)
2085 			return (la);
2086 #endif
2087 
2088 #ifndef _KERNEL
2089 		/* kernel cleans up on module unload */
2090 		if (LIST_EMPTY(&instancehead))
2091 			atexit(finishoff);
2092 #endif
2093 		LIST_INSERT_HEAD(&instancehead, la, instancelist);
2094 
2095 #ifdef _KERNEL
2096 		LibAliasTime = time_uptime;
2097 #else
2098 		LibAliasTime = time(NULL);
2099 #endif
2100 
2101 		SPLAY_INIT(&la->linkSplayIn);
2102 		SPLAY_INIT(&la->linkSplayOut);
2103 		LIST_INIT(&la->pptpList);
2104 		TAILQ_INIT(&la->checkExpire);
2105 #ifdef _KERNEL
2106 		AliasSctpInit(la);
2107 #endif
2108 		LIBALIAS_LOCK_INIT(la);
2109 		LIBALIAS_LOCK(la);
2110 	} else {
2111 		LIBALIAS_LOCK(la);
2112 		CleanupAliasData(la, 1);
2113 #ifdef _KERNEL
2114 		AliasSctpTerm(la);
2115 		AliasSctpInit(la);
2116 #endif
2117 	}
2118 
2119 	la->aliasAddress.s_addr = INADDR_ANY;
2120 	la->targetAddress.s_addr = INADDR_ANY;
2121 	la->aliasPortLower = 0x8000;
2122 	la->aliasPortLength = 0x8000;
2123 
2124 	la->icmpLinkCount = 0;
2125 	la->udpLinkCount = 0;
2126 	la->tcpLinkCount = 0;
2127 	la->sctpLinkCount = 0;
2128 	la->pptpLinkCount = 0;
2129 	la->protoLinkCount = 0;
2130 	la->fragmentIdLinkCount = 0;
2131 	la->fragmentPtrLinkCount = 0;
2132 	la->sockCount = 0;
2133 
2134 	la->packetAliasMode = PKT_ALIAS_SAME_PORTS
2135 #ifndef NO_USE_SOCKETS
2136 	    | PKT_ALIAS_USE_SOCKETS
2137 #endif
2138 	    | PKT_ALIAS_RESET_ON_ADDR_CHANGE;
2139 #ifndef NO_FW_PUNCH
2140 	la->fireWallFD = -1;
2141 #endif
2142 #ifndef _KERNEL
2143 	LibAliasRefreshModules();
2144 #endif
2145 	LIBALIAS_UNLOCK(la);
2146 	return (la);
2147 }
2148 
2149 void
2150 LibAliasUninit(struct libalias *la)
2151 {
2152 	LIBALIAS_LOCK(la);
2153 #ifdef _KERNEL
2154 	AliasSctpTerm(la);
2155 #endif
2156 	CleanupAliasData(la, 1);
2157 	UninitPacketAliasLog(la);
2158 #ifndef NO_FW_PUNCH
2159 	UninitPunchFW(la);
2160 #endif
2161 	LIST_REMOVE(la, instancelist);
2162 	LIBALIAS_UNLOCK(la);
2163 	LIBALIAS_LOCK_DESTROY(la);
2164 	free(la);
2165 }
2166 
2167 /* Change mode for some operations */
2168 unsigned int
2169 LibAliasSetMode(
2170     struct libalias *la,
2171     unsigned int flags,		/* Which state to bring flags to */
2172     unsigned int mask		/* Mask of which flags to affect (use 0 to
2173 				 * do a probe for flag values) */
2174 )
2175 {
2176 	int res = -1;
2177 
2178 	LIBALIAS_LOCK(la);
2179 	if (flags & mask & PKT_ALIAS_LOG) {
2180 		/* Enable logging */
2181 		if (InitPacketAliasLog(la) == ENOMEM)
2182 			goto getout;
2183 	} else if (~flags & mask & PKT_ALIAS_LOG)
2184 		/* _Disable_ logging */
2185 		UninitPacketAliasLog(la);
2186 
2187 #ifndef NO_FW_PUNCH
2188 	if (flags & mask & PKT_ALIAS_PUNCH_FW)
2189 		/* Start punching holes in the firewall? */
2190 		InitPunchFW(la);
2191 	else if (~flags & mask & PKT_ALIAS_PUNCH_FW)
2192 		/* Stop punching holes in the firewall? */
2193 		UninitPunchFW(la);
2194 #endif
2195 
2196 	/* Other flags can be set/cleared without special action */
2197 	la->packetAliasMode = (flags & mask) | (la->packetAliasMode & ~mask);
2198 	res = la->packetAliasMode;
2199 getout:
2200 	LIBALIAS_UNLOCK(la);
2201 	return (res);
2202 }
2203 
2204 #ifndef NO_FW_PUNCH
2205 
2206 /*****************
2207   Code to support firewall punching.  This shouldn't really be in this
2208   file, but making variables global is evil too.
2209   ****************/
2210 
2211 /* Firewall include files */
2212 #include <net/if.h>
2213 #include <netinet/ip_fw.h>
2214 #include <string.h>
2215 #include <err.h>
2216 
2217 /*
2218  * helper function, updates the pointer to cmd with the length
2219  * of the current command, and also cleans up the first word of
2220  * the new command in case it has been clobbered before.
2221  */
2222 static ipfw_insn *
2223 next_cmd(ipfw_insn * cmd)
2224 {
2225 	cmd += F_LEN(cmd);
2226 	bzero(cmd, sizeof(*cmd));
2227 	return (cmd);
2228 }
2229 
2230 /*
2231  * A function to fill simple commands of size 1.
2232  * Existing flags are preserved.
2233  */
2234 static ipfw_insn *
2235 fill_cmd(ipfw_insn * cmd, enum ipfw_opcodes opcode, int size,
2236     int flags, u_int16_t arg)
2237 {
2238 	cmd->opcode = opcode;
2239 	cmd->len = ((cmd->len | flags) & (F_NOT | F_OR)) | (size & F_LEN_MASK);
2240 	cmd->arg1 = arg;
2241 	return next_cmd(cmd);
2242 }
2243 
2244 static ipfw_insn *
2245 fill_ip(ipfw_insn * cmd1, enum ipfw_opcodes opcode, u_int32_t addr)
2246 {
2247 	ipfw_insn_ip *cmd = (ipfw_insn_ip *)cmd1;
2248 
2249 	cmd->addr.s_addr = addr;
2250 	return fill_cmd(cmd1, opcode, F_INSN_SIZE(ipfw_insn_u32), 0, 0);
2251 }
2252 
2253 static ipfw_insn *
2254 fill_one_port(ipfw_insn * cmd1, enum ipfw_opcodes opcode, u_int16_t port)
2255 {
2256 	ipfw_insn_u16 *cmd = (ipfw_insn_u16 *)cmd1;
2257 
2258 	cmd->ports[0] = cmd->ports[1] = port;
2259 	return fill_cmd(cmd1, opcode, F_INSN_SIZE(ipfw_insn_u16), 0, 0);
2260 }
2261 
2262 static int
2263 fill_rule(void *buf, int bufsize, int rulenum,
2264     enum ipfw_opcodes action, int proto,
2265     struct in_addr sa, u_int16_t sp, struct in_addr da, u_int16_t dp)
2266 {
2267 	struct ip_fw *rule = (struct ip_fw *)buf;
2268 	ipfw_insn *cmd = (ipfw_insn *)rule->cmd;
2269 
2270 	bzero(buf, bufsize);
2271 	rule->rulenum = rulenum;
2272 
2273 	cmd = fill_cmd(cmd, O_PROTO, F_INSN_SIZE(ipfw_insn), 0, proto);
2274 	cmd = fill_ip(cmd, O_IP_SRC, sa.s_addr);
2275 	cmd = fill_one_port(cmd, O_IP_SRCPORT, sp);
2276 	cmd = fill_ip(cmd, O_IP_DST, da.s_addr);
2277 	cmd = fill_one_port(cmd, O_IP_DSTPORT, dp);
2278 
2279 	rule->act_ofs = (u_int32_t *)cmd - (u_int32_t *)rule->cmd;
2280 	cmd = fill_cmd(cmd, action, F_INSN_SIZE(ipfw_insn), 0, 0);
2281 
2282 	rule->cmd_len = (u_int32_t *)cmd - (u_int32_t *)rule->cmd;
2283 
2284 	return ((char *)cmd - (char *)buf);
2285 }
2286 
2287 static void
2288 InitPunchFW(struct libalias *la)
2289 {
2290 	la->fireWallField = malloc(la->fireWallNumNums);
2291 	if (la->fireWallField) {
2292 		memset(la->fireWallField, 0, la->fireWallNumNums);
2293 		if (la->fireWallFD < 0) {
2294 			la->fireWallFD = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
2295 		}
2296 		ClearAllFWHoles(la);
2297 		la->fireWallActiveNum = la->fireWallBaseNum;
2298 	}
2299 }
2300 
2301 static void
2302 UninitPunchFW(struct libalias *la)
2303 {
2304 	ClearAllFWHoles(la);
2305 	if (la->fireWallFD >= 0)
2306 		close(la->fireWallFD);
2307 	la->fireWallFD = -1;
2308 	if (la->fireWallField)
2309 		free(la->fireWallField);
2310 	la->fireWallField = NULL;
2311 	la->packetAliasMode &= ~PKT_ALIAS_PUNCH_FW;
2312 }
2313 
2314 /* Make a certain link go through the firewall */
2315 void
2316 PunchFWHole(struct alias_link *lnk)
2317 {
2318 	struct libalias *la;
2319 	int r;			/* Result code */
2320 	struct ip_fw rule;	/* On-the-fly built rule */
2321 	int fwhole;		/* Where to punch hole */
2322 
2323 	la = lnk->la;
2324 
2325 	/* Don't do anything unless we are asked to */
2326 	if (!(la->packetAliasMode & PKT_ALIAS_PUNCH_FW) ||
2327 	    la->fireWallFD < 0 ||
2328 	    lnk->link_type != LINK_TCP)
2329 		return;
2330 
2331 	memset(&rule, 0, sizeof rule);
2332 
2333 	/** Build rule **/
2334 
2335 	/* Find empty slot */
2336 	for (fwhole = la->fireWallActiveNum;
2337 	    fwhole < la->fireWallBaseNum + la->fireWallNumNums &&
2338 	    fw_tstfield(la, la->fireWallField, fwhole);
2339 	    fwhole++);
2340 	if (fwhole == la->fireWallBaseNum + la->fireWallNumNums) {
2341 		for (fwhole = la->fireWallBaseNum;
2342 		    fwhole < la->fireWallActiveNum &&
2343 		    fw_tstfield(la, la->fireWallField, fwhole);
2344 		    fwhole++);
2345 		if (fwhole == la->fireWallActiveNum) {
2346 			/* No rule point empty - we can't punch more holes. */
2347 			la->fireWallActiveNum = la->fireWallBaseNum;
2348 #ifdef LIBALIAS_DEBUG
2349 			fprintf(stderr, "libalias: Unable to create firewall hole!\n");
2350 #endif
2351 			return;
2352 		}
2353 	}
2354 	/* Start next search at next position */
2355 	la->fireWallActiveNum = fwhole + 1;
2356 
2357 	/*
2358 	 * generate two rules of the form
2359 	 *
2360 	 * add fwhole accept tcp from OAddr OPort to DAddr DPort add fwhole
2361 	 * accept tcp from DAddr DPort to OAddr OPort
2362 	 */
2363 	if (GetOriginalPort(lnk) != 0 && GetDestPort(lnk) != 0) {
2364 		u_int32_t rulebuf[255];
2365 		int i;
2366 
2367 		i = fill_rule(rulebuf, sizeof(rulebuf), fwhole,
2368 		    O_ACCEPT, IPPROTO_TCP,
2369 		    GetOriginalAddress(lnk), ntohs(GetOriginalPort(lnk)),
2370 		    GetDestAddress(lnk), ntohs(GetDestPort(lnk)));
2371 		r = setsockopt(la->fireWallFD, IPPROTO_IP, IP_FW_ADD, rulebuf, i);
2372 		if (r)
2373 			err(1, "alias punch inbound(1) setsockopt(IP_FW_ADD)");
2374 
2375 		i = fill_rule(rulebuf, sizeof(rulebuf), fwhole,
2376 		    O_ACCEPT, IPPROTO_TCP,
2377 		    GetDestAddress(lnk), ntohs(GetDestPort(lnk)),
2378 		    GetOriginalAddress(lnk), ntohs(GetOriginalPort(lnk)));
2379 		r = setsockopt(la->fireWallFD, IPPROTO_IP, IP_FW_ADD, rulebuf, i);
2380 		if (r)
2381 			err(1, "alias punch inbound(2) setsockopt(IP_FW_ADD)");
2382 	}
2383 
2384 	/* Indicate hole applied */
2385 	lnk->data.tcp->fwhole = fwhole;
2386 	fw_setfield(la, la->fireWallField, fwhole);
2387 }
2388 
2389 /* Remove a hole in a firewall associated with a particular alias
2390    lnk.  Calling this too often is harmless. */
2391 static void
2392 ClearFWHole(struct alias_link *lnk)
2393 {
2394 	struct libalias *la;
2395 
2396 	la = lnk->la;
2397 	if (lnk->link_type == LINK_TCP) {
2398 		int fwhole = lnk->data.tcp->fwhole;  /* Where is the firewall hole? */
2399 		struct ip_fw rule;
2400 
2401 		if (fwhole < 0)
2402 			return;
2403 
2404 		memset(&rule, 0, sizeof rule);	/* useless for ipfw2 */
2405 		while (!setsockopt(la->fireWallFD, IPPROTO_IP, IP_FW_DEL,
2406 		    &fwhole, sizeof fwhole));
2407 		fw_clrfield(la, la->fireWallField, fwhole);
2408 		lnk->data.tcp->fwhole = -1;
2409 	}
2410 }
2411 
2412 /* Clear out the entire range dedicated to firewall holes. */
2413 static void
2414 ClearAllFWHoles(struct libalias *la)
2415 {
2416 	struct ip_fw rule;	/* On-the-fly built rule */
2417 	int i;
2418 
2419 	if (la->fireWallFD < 0)
2420 		return;
2421 
2422 	memset(&rule, 0, sizeof rule);
2423 	for (i = la->fireWallBaseNum; i < la->fireWallBaseNum + la->fireWallNumNums; i++) {
2424 		int r = i;
2425 
2426 		while (!setsockopt(la->fireWallFD, IPPROTO_IP, IP_FW_DEL, &r, sizeof r));
2427 	}
2428 	/* XXX: third arg correct here ? /phk */
2429 	memset(la->fireWallField, 0, la->fireWallNumNums);
2430 }
2431 
2432 #endif /* !NO_FW_PUNCH */
2433 
2434 void
2435 LibAliasSetFWBase(struct libalias *la, unsigned int base, unsigned int num)
2436 {
2437 	LIBALIAS_LOCK(la);
2438 #ifndef NO_FW_PUNCH
2439 	la->fireWallBaseNum = base;
2440 	la->fireWallNumNums = num;
2441 #endif
2442 	LIBALIAS_UNLOCK(la);
2443 }
2444 
2445 void
2446 LibAliasSetSkinnyPort(struct libalias *la, unsigned int port)
2447 {
2448 	LIBALIAS_LOCK(la);
2449 	la->skinnyPort = port;
2450 	LIBALIAS_UNLOCK(la);
2451 }
2452 
2453 /*
2454  * Find the address to redirect incoming packets
2455  */
2456 struct in_addr
2457 FindSctpRedirectAddress(struct libalias *la,  struct sctp_nat_msg *sm)
2458 {
2459 	struct alias_link *lnk;
2460 	struct in_addr redir;
2461 
2462 	LIBALIAS_LOCK_ASSERT(la);
2463 	lnk = FindLinkIn(la, sm->ip_hdr->ip_src, sm->ip_hdr->ip_dst,
2464 	    sm->sctp_hdr->dest_port,sm->sctp_hdr->dest_port, LINK_SCTP, 1);
2465 	if (lnk != NULL) {
2466 		/* port redirect */
2467 		return (lnk->src_addr);
2468 	} else {
2469 		redir = FindOriginalAddress(la,sm->ip_hdr->ip_dst);
2470 		if (redir.s_addr == la->aliasAddress.s_addr ||
2471 		    redir.s_addr == la->targetAddress.s_addr) {
2472 			/* No address found */
2473 			lnk = FindLinkIn(la, sm->ip_hdr->ip_src, sm->ip_hdr->ip_dst,
2474 			    NO_DEST_PORT, 0, LINK_SCTP, 1);
2475 			if (lnk != NULL)
2476 				/* redirect proto */
2477 				return (lnk->src_addr);
2478 		}
2479 		return (redir); /* address redirect */
2480 	}
2481 }
2482