xref: /freebsd/sys/netinet/libalias/alias_db.c (revision 308399a179a49b7b858c725de10177fdb0502fd2)
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 				struct alias_link *found;
873 
874 				found = UseLink(la, lnk);
875 				if (found != NULL)
876 					return (found);
877 				/* link expired */
878 				break;
879 			}
880 		}
881 		break;
882 	case LINK_UNKNOWN_DEST_PORT:
883 		LIST_FOREACH(lnk, &grp->full, all.in) {
884 			if(lnk->dst_addr.s_addr == dst_addr.s_addr) {
885 				lnk_unknown_dst_port = lnk;
886 				break;
887 			}
888 		}
889 		break;
890 	case LINK_UNKNOWN_DEST_ADDR:
891 		LIST_FOREACH(lnk, &grp->full, all.in) {
892 			if(lnk->dst_port == dst_port) {
893 				lnk_unknown_dst_addr = lnk;
894 				break;
895 			}
896 		}
897 		break;
898 	case LINK_PARTIALLY_SPECIFIED:
899 		lnk_unknown_all = LIST_FIRST(&grp->full);
900 		break;
901 	}
902 
903 	if (lnk_unknown_dst_port == NULL) {
904 		LIST_FOREACH(lnk, &grp->partial, all.in) {
905 			int flags = (flags_in | lnk->flags) & LINK_PARTIALLY_SPECIFIED;
906 
907 			if (flags == LINK_PARTIALLY_SPECIFIED &&
908 			    lnk_unknown_all == NULL)
909 				lnk_unknown_all = lnk;
910 			if (flags == LINK_UNKNOWN_DEST_ADDR &&
911 			    lnk->dst_port == dst_port &&
912 			    lnk_unknown_dst_addr == NULL)
913 				lnk_unknown_dst_addr = lnk;
914 			if (flags == LINK_UNKNOWN_DEST_PORT &&
915 			    lnk->dst_addr.s_addr == dst_addr.s_addr) {
916 				lnk_unknown_dst_port = lnk;
917 				break;
918 			}
919 		}
920 	}
921 
922 	lnk = (lnk_unknown_dst_port != NULL) ? lnk_unknown_dst_port
923 	    : (lnk_unknown_dst_addr != NULL) ? lnk_unknown_dst_addr
924 	    : lnk_unknown_all;
925 
926 	if (lnk == NULL || !replace_partial_links)
927 		return (lnk);
928 
929 	if (lnk->server != NULL) {	/* LSNAT link */
930 		src_addr = lnk->server->addr;
931 		src_port = lnk->server->port;
932 		lnk->server = lnk->server->next;
933 	} else {
934 		src_addr = lnk->src_addr;
935 		src_port = lnk->src_port;
936 	}
937 
938 	if (link_type == LINK_SCTP) {
939 		lnk->src_addr = src_addr;
940 		lnk->src_port = src_port;
941 	} else {
942 		lnk = ReLink(lnk,
943 		    src_addr, dst_addr, alias_addr,
944 		    src_port, dst_port, alias_port,
945 		    link_type, 0);
946 	}
947 	return (lnk);
948 }
949 
950 static struct alias_link *
951 FindLinkIn(struct libalias *la, struct in_addr dst_addr,
952     struct in_addr alias_addr,
953     u_short dst_port,
954     u_short alias_port,
955     int link_type,
956     int replace_partial_links)
957 {
958 	struct alias_link *lnk;
959 
960 	LIBALIAS_LOCK_ASSERT(la);
961 	lnk = _FindLinkIn(la, dst_addr, alias_addr, dst_port, alias_port,
962 	    link_type, replace_partial_links);
963 
964 	if (lnk == NULL) {
965 		/*
966 		 * The following allows permanent links to be specified as
967 		 * using the default aliasing address (i.e. device
968 		 * interface address) without knowing in advance what that
969 		 * address is.
970 		 */
971 		if (la->aliasAddress.s_addr != INADDR_ANY &&
972 		    alias_addr.s_addr == la->aliasAddress.s_addr) {
973 			lnk = _FindLinkIn(la, dst_addr, ANY_ADDR, dst_port, alias_port,
974 			    link_type, replace_partial_links);
975 		}
976 	}
977 	return (lnk);
978 }
979 
980 /* External routines for finding/adding links
981 
982 -- "external" means outside alias_db.c, but within alias*.c --
983 
984     FindIcmpIn(), FindIcmpOut()
985     FindFragmentIn1(), FindFragmentIn2()
986     AddFragmentPtrLink(), FindFragmentPtr()
987     FindProtoIn(), FindProtoOut()
988     FindUdpTcpIn(), FindUdpTcpOut()
989     AddPptp(), FindPptpOutByCallId(), FindPptpInByCallId(),
990     FindPptpOutByPeerCallId(), FindPptpInByPeerCallId()
991     FindOriginalAddress(), FindAliasAddress()
992 
993 (prototypes in alias_local.h)
994 */
995 
996 struct alias_link *
997 FindIcmpIn(struct libalias *la, struct in_addr dst_addr,
998     struct in_addr alias_addr,
999     u_short id_alias,
1000     int create)
1001 {
1002 	struct alias_link *lnk;
1003 
1004 	LIBALIAS_LOCK_ASSERT(la);
1005 	lnk = FindLinkIn(la, dst_addr, alias_addr,
1006 	    NO_DEST_PORT, id_alias,
1007 	    LINK_ICMP, 0);
1008 	if (lnk == NULL && create && !(la->packetAliasMode & PKT_ALIAS_DENY_INCOMING)) {
1009 		struct in_addr target_addr;
1010 
1011 		target_addr = FindOriginalAddress(la, alias_addr);
1012 		lnk = AddLink(la, target_addr, dst_addr, alias_addr,
1013 		    id_alias, NO_DEST_PORT, id_alias,
1014 		    LINK_ICMP);
1015 	}
1016 	return (lnk);
1017 }
1018 
1019 struct alias_link *
1020 FindIcmpOut(struct libalias *la, struct in_addr src_addr,
1021     struct in_addr dst_addr,
1022     u_short id,
1023     int create)
1024 {
1025 	struct alias_link *lnk;
1026 
1027 	LIBALIAS_LOCK_ASSERT(la);
1028 	lnk = FindLinkOut(la, src_addr, dst_addr,
1029 	    id, NO_DEST_PORT,
1030 	    LINK_ICMP, 0);
1031 	if (lnk == NULL && create) {
1032 		struct in_addr alias_addr;
1033 
1034 		alias_addr = FindAliasAddress(la, src_addr);
1035 		lnk = AddLink(la, src_addr, dst_addr, alias_addr,
1036 		    id, NO_DEST_PORT, GET_ALIAS_ID,
1037 		    LINK_ICMP);
1038 	}
1039 	return (lnk);
1040 }
1041 
1042 struct alias_link *
1043 FindFragmentIn1(struct libalias *la, struct in_addr dst_addr,
1044     struct in_addr alias_addr,
1045     u_short ip_id)
1046 {
1047 	struct alias_link *lnk;
1048 
1049 	LIBALIAS_LOCK_ASSERT(la);
1050 	lnk = FindLinkIn(la, dst_addr, alias_addr,
1051 	    NO_DEST_PORT, ip_id,
1052 	    LINK_FRAGMENT_ID, 0);
1053 
1054 	if (lnk == NULL) {
1055 		lnk = AddLink(la, ANY_ADDR, dst_addr, alias_addr,
1056 		    NO_SRC_PORT, NO_DEST_PORT, ip_id,
1057 		    LINK_FRAGMENT_ID);
1058 	}
1059 	return (lnk);
1060 }
1061 
1062 /* Doesn't add a link if one is not found. */
1063 struct alias_link *
1064 FindFragmentIn2(struct libalias *la, struct in_addr dst_addr,
1065     struct in_addr alias_addr, u_short ip_id)
1066 {
1067 	LIBALIAS_LOCK_ASSERT(la);
1068 	return FindLinkIn(la, dst_addr, alias_addr,
1069 	    NO_DEST_PORT, ip_id,
1070 	    LINK_FRAGMENT_ID, 0);
1071 }
1072 
1073 struct alias_link *
1074 AddFragmentPtrLink(struct libalias *la, struct in_addr dst_addr,
1075     u_short ip_id)
1076 {
1077 	LIBALIAS_LOCK_ASSERT(la);
1078 	return AddLink(la, ANY_ADDR, dst_addr, ANY_ADDR,
1079 	    NO_SRC_PORT, NO_DEST_PORT, ip_id,
1080 	    LINK_FRAGMENT_PTR);
1081 }
1082 
1083 struct alias_link *
1084 FindFragmentPtr(struct libalias *la, struct in_addr dst_addr,
1085     u_short ip_id)
1086 {
1087 	LIBALIAS_LOCK_ASSERT(la);
1088 	return FindLinkIn(la, dst_addr, ANY_ADDR,
1089 	    NO_DEST_PORT, ip_id,
1090 	    LINK_FRAGMENT_PTR, 0);
1091 }
1092 
1093 struct alias_link *
1094 FindProtoIn(struct libalias *la, struct in_addr dst_addr,
1095     struct in_addr alias_addr,
1096     u_char proto)
1097 {
1098 	struct alias_link *lnk;
1099 
1100 	LIBALIAS_LOCK_ASSERT(la);
1101 	lnk = FindLinkIn(la, dst_addr, alias_addr,
1102 	    NO_DEST_PORT, 0,
1103 	    proto, 1);
1104 
1105 	if (lnk == NULL && !(la->packetAliasMode & PKT_ALIAS_DENY_INCOMING)) {
1106 		struct in_addr target_addr;
1107 
1108 		target_addr = FindOriginalAddress(la, alias_addr);
1109 		lnk = AddLink(la, target_addr, dst_addr, alias_addr,
1110 		    NO_SRC_PORT, NO_DEST_PORT, 0,
1111 		    proto);
1112 	}
1113 	return (lnk);
1114 }
1115 
1116 struct alias_link *
1117 FindProtoOut(struct libalias *la, struct in_addr src_addr,
1118     struct in_addr dst_addr,
1119     u_char proto)
1120 {
1121 	struct alias_link *lnk;
1122 
1123 	LIBALIAS_LOCK_ASSERT(la);
1124 	lnk = FindLinkOut(la, src_addr, dst_addr,
1125 	    NO_SRC_PORT, NO_DEST_PORT,
1126 	    proto, 1);
1127 
1128 	if (lnk == NULL) {
1129 		struct in_addr alias_addr;
1130 
1131 		alias_addr = FindAliasAddress(la, src_addr);
1132 		lnk = AddLink(la, src_addr, dst_addr, alias_addr,
1133 		    NO_SRC_PORT, NO_DEST_PORT, 0,
1134 		    proto);
1135 	}
1136 	return (lnk);
1137 }
1138 
1139 struct alias_link *
1140 FindUdpTcpIn(struct libalias *la, struct in_addr dst_addr,
1141     struct in_addr alias_addr,
1142     u_short dst_port,
1143     u_short alias_port,
1144     u_char proto,
1145     int create)
1146 {
1147 	int link_type;
1148 	struct alias_link *lnk;
1149 
1150 	LIBALIAS_LOCK_ASSERT(la);
1151 	switch (proto) {
1152 	case IPPROTO_UDP:
1153 		link_type = LINK_UDP;
1154 		break;
1155 	case IPPROTO_TCP:
1156 		link_type = LINK_TCP;
1157 		break;
1158 	default:
1159 		return (NULL);
1160 		break;
1161 	}
1162 
1163 	lnk = FindLinkIn(la, dst_addr, alias_addr,
1164 	    dst_port, alias_port,
1165 	    link_type, create);
1166 
1167 	if (lnk == NULL && create && !(la->packetAliasMode & PKT_ALIAS_DENY_INCOMING)) {
1168 		struct in_addr target_addr;
1169 
1170 		target_addr = FindOriginalAddress(la, alias_addr);
1171 		lnk = AddLink(la, target_addr, dst_addr, alias_addr,
1172 		    alias_port, dst_port, alias_port,
1173 		    link_type);
1174 	}
1175 	return (lnk);
1176 }
1177 
1178 struct alias_link *
1179 FindUdpTcpOut(struct libalias *la, struct in_addr src_addr,
1180     struct in_addr dst_addr,
1181     u_short src_port,
1182     u_short dst_port,
1183     u_char proto,
1184     int create)
1185 {
1186 	int link_type;
1187 	struct alias_link *lnk;
1188 
1189 	LIBALIAS_LOCK_ASSERT(la);
1190 	switch (proto) {
1191 	case IPPROTO_UDP:
1192 		link_type = LINK_UDP;
1193 		break;
1194 	case IPPROTO_TCP:
1195 		link_type = LINK_TCP;
1196 		break;
1197 	default:
1198 		return (NULL);
1199 		break;
1200 	}
1201 
1202 	lnk = FindLinkOut(la, src_addr, dst_addr, src_port, dst_port, link_type, create);
1203 
1204 	if (lnk == NULL && create) {
1205 		struct in_addr alias_addr;
1206 
1207 		alias_addr = FindAliasAddress(la, src_addr);
1208 		lnk = AddLink(la, src_addr, dst_addr, alias_addr,
1209 		    src_port, dst_port, GET_ALIAS_PORT,
1210 		    link_type);
1211 	}
1212 	return (lnk);
1213 }
1214 
1215 struct alias_link *
1216 AddPptp(struct libalias *la, struct in_addr src_addr,
1217     struct in_addr dst_addr,
1218     struct in_addr alias_addr,
1219     u_int16_t src_call_id)
1220 {
1221 	struct alias_link *lnk;
1222 
1223 	LIBALIAS_LOCK_ASSERT(la);
1224 	lnk = AddLink(la, src_addr, dst_addr, alias_addr,
1225 	    src_call_id, 0, GET_ALIAS_PORT,
1226 	    LINK_PPTP);
1227 
1228 	return (lnk);
1229 }
1230 
1231 struct alias_link *
1232 FindPptpOutByCallId(struct libalias *la, struct in_addr src_addr,
1233     struct in_addr dst_addr,
1234     u_int16_t src_call_id)
1235 {
1236 	struct alias_link *lnk;
1237 
1238 	LIBALIAS_LOCK_ASSERT(la);
1239 	LIST_FOREACH(lnk, &la->pptpList, pptp.list)
1240 		if (lnk->src_addr.s_addr == src_addr.s_addr &&
1241 		    lnk->dst_addr.s_addr == dst_addr.s_addr &&
1242 		    lnk->src_port == src_call_id)
1243 			break;
1244 
1245 	return (UseLink(la, lnk));
1246 }
1247 
1248 struct alias_link *
1249 FindPptpOutByPeerCallId(struct libalias *la, struct in_addr src_addr,
1250     struct in_addr dst_addr,
1251     u_int16_t dst_call_id)
1252 {
1253 	struct alias_link *lnk;
1254 
1255 	LIBALIAS_LOCK_ASSERT(la);
1256 	LIST_FOREACH(lnk, &la->pptpList, pptp.list)
1257 		if (lnk->src_addr.s_addr == src_addr.s_addr &&
1258 		    lnk->dst_addr.s_addr == dst_addr.s_addr &&
1259 		    lnk->dst_port == dst_call_id)
1260 			break;
1261 
1262 	return (UseLink(la, lnk));
1263 }
1264 
1265 struct alias_link *
1266 FindPptpInByCallId(struct libalias *la, struct in_addr dst_addr,
1267     struct in_addr alias_addr,
1268     u_int16_t dst_call_id)
1269 {
1270 	struct alias_link *lnk;
1271 
1272 	LIBALIAS_LOCK_ASSERT(la);
1273 
1274 	LIST_FOREACH(lnk, &la->pptpList, pptp.list)
1275 		if (lnk->dst_port == dst_call_id &&
1276 		    lnk->dst_addr.s_addr == dst_addr.s_addr &&
1277 		    lnk->alias_addr.s_addr == alias_addr.s_addr)
1278 			break;
1279 
1280 	return (UseLink(la, lnk));
1281 }
1282 
1283 struct alias_link *
1284 FindPptpInByPeerCallId(struct libalias *la, struct in_addr dst_addr,
1285     struct in_addr alias_addr,
1286     u_int16_t alias_call_id)
1287 {
1288 	struct alias_link *lnk;
1289 
1290 	LIBALIAS_LOCK_ASSERT(la);
1291 	LIST_FOREACH(lnk, &la->pptpList, pptp.list)
1292 		if (lnk->alias_port == alias_call_id &&
1293 		    lnk->dst_addr.s_addr == dst_addr.s_addr &&
1294 		    lnk->alias_addr.s_addr == alias_addr.s_addr)
1295 			break;
1296 
1297 	return (lnk);
1298 }
1299 
1300 struct alias_link *
1301 FindRtspOut(struct libalias *la, struct in_addr src_addr,
1302     struct in_addr dst_addr,
1303     u_short src_port,
1304     u_short alias_port,
1305     u_char proto)
1306 {
1307 	int link_type;
1308 	struct alias_link *lnk;
1309 
1310 	LIBALIAS_LOCK_ASSERT(la);
1311 	switch (proto) {
1312 	case IPPROTO_UDP:
1313 		link_type = LINK_UDP;
1314 		break;
1315 	case IPPROTO_TCP:
1316 		link_type = LINK_TCP;
1317 		break;
1318 	default:
1319 		return (NULL);
1320 		break;
1321 	}
1322 
1323 	lnk = FindLinkOut(la, src_addr, dst_addr, src_port, 0, link_type, 1);
1324 
1325 	if (lnk == NULL) {
1326 		struct in_addr alias_addr;
1327 
1328 		alias_addr = FindAliasAddress(la, src_addr);
1329 		lnk = AddLink(la, src_addr, dst_addr, alias_addr,
1330 		    src_port, 0, alias_port,
1331 		    link_type);
1332 	}
1333 	return (lnk);
1334 }
1335 
1336 struct in_addr
1337 FindOriginalAddress(struct libalias *la, struct in_addr alias_addr)
1338 {
1339 	struct alias_link *lnk;
1340 
1341 	LIBALIAS_LOCK_ASSERT(la);
1342 	lnk = FindLinkIn(la, ANY_ADDR, alias_addr,
1343 	    0, 0, LINK_ADDR, 0);
1344 	if (lnk == NULL) {
1345 		if (la->targetAddress.s_addr == INADDR_ANY)
1346 			return (alias_addr);
1347 		else if (la->targetAddress.s_addr == INADDR_NONE)
1348 			return (la->aliasAddress.s_addr != INADDR_ANY) ?
1349 			    la->aliasAddress : alias_addr;
1350 		else
1351 			return (la->targetAddress);
1352 	} else {
1353 		if (lnk->server != NULL) {	/* LSNAT link */
1354 			struct in_addr src_addr;
1355 
1356 			src_addr = lnk->server->addr;
1357 			lnk->server = lnk->server->next;
1358 			return (src_addr);
1359 		} else if (lnk->src_addr.s_addr == INADDR_ANY)
1360 			return (la->aliasAddress.s_addr != INADDR_ANY) ?
1361 			    la->aliasAddress : alias_addr;
1362 		else
1363 			return (lnk->src_addr);
1364 	}
1365 }
1366 
1367 struct in_addr
1368 FindAliasAddress(struct libalias *la, struct in_addr original_addr)
1369 {
1370 	struct alias_link *lnk;
1371 
1372 	LIBALIAS_LOCK_ASSERT(la);
1373 	lnk = FindLinkOut(la, original_addr, ANY_ADDR,
1374 	    0, 0, LINK_ADDR, 0);
1375 	if (lnk == NULL) {
1376 		return (la->aliasAddress.s_addr != INADDR_ANY) ?
1377 		    la->aliasAddress : original_addr;
1378 	} else {
1379 		if (lnk->alias_addr.s_addr == INADDR_ANY)
1380 			return (la->aliasAddress.s_addr != INADDR_ANY) ?
1381 			    la->aliasAddress : original_addr;
1382 		else
1383 			return (lnk->alias_addr);
1384 	}
1385 }
1386 
1387 /* External routines for getting or changing link data
1388    (external to alias_db.c, but internal to alias*.c)
1389 
1390     SetFragmentData(), GetFragmentData()
1391     SetFragmentPtr(), GetFragmentPtr()
1392     SetStateIn(), SetStateOut(), GetStateIn(), GetStateOut()
1393     GetOriginalAddress(), GetDestAddress(), GetAliasAddress()
1394     GetOriginalPort(), GetAliasPort()
1395     SetAckModified(), GetAckModified()
1396     GetDeltaAckIn(), GetDeltaSeqOut(), AddSeq()
1397     SetProtocolFlags(), GetProtocolFlags()
1398     SetDestCallId()
1399 */
1400 
1401 void
1402 SetFragmentAddr(struct alias_link *lnk, struct in_addr src_addr)
1403 {
1404 	lnk->data.frag_addr = src_addr;
1405 }
1406 
1407 void
1408 GetFragmentAddr(struct alias_link *lnk, struct in_addr *src_addr)
1409 {
1410 	*src_addr = lnk->data.frag_addr;
1411 }
1412 
1413 void
1414 SetFragmentPtr(struct alias_link *lnk, void *fptr)
1415 {
1416 	lnk->data.frag_ptr = fptr;
1417 }
1418 
1419 void
1420 GetFragmentPtr(struct alias_link *lnk, void **fptr)
1421 {
1422 	*fptr = lnk->data.frag_ptr;
1423 }
1424 
1425 void
1426 SetStateIn(struct alias_link *lnk, int state)
1427 {
1428 	/* TCP input state */
1429 	switch (state) {
1430 		case ALIAS_TCP_STATE_DISCONNECTED:
1431 		if (lnk->data.tcp->state.out != ALIAS_TCP_STATE_CONNECTED)
1432 			lnk->expire.time = TCP_EXPIRE_DEAD;
1433 		else
1434 			lnk->expire.time = TCP_EXPIRE_SINGLEDEAD;
1435 		break;
1436 	case ALIAS_TCP_STATE_CONNECTED:
1437 		if (lnk->data.tcp->state.out == ALIAS_TCP_STATE_CONNECTED)
1438 			lnk->expire.time = TCP_EXPIRE_CONNECTED;
1439 		break;
1440 	default:
1441 #ifdef _KERNEL
1442 		panic("libalias:SetStateIn() unknown state");
1443 #else
1444 		abort();
1445 #endif
1446 	}
1447 	lnk->data.tcp->state.in = state;
1448 }
1449 
1450 void
1451 SetStateOut(struct alias_link *lnk, int state)
1452 {
1453 	/* TCP output state */
1454 	switch (state) {
1455 		case ALIAS_TCP_STATE_DISCONNECTED:
1456 		if (lnk->data.tcp->state.in != ALIAS_TCP_STATE_CONNECTED)
1457 			lnk->expire.time = TCP_EXPIRE_DEAD;
1458 		else
1459 			lnk->expire.time = TCP_EXPIRE_SINGLEDEAD;
1460 		break;
1461 	case ALIAS_TCP_STATE_CONNECTED:
1462 		if (lnk->data.tcp->state.in == ALIAS_TCP_STATE_CONNECTED)
1463 			lnk->expire.time = TCP_EXPIRE_CONNECTED;
1464 		break;
1465 	default:
1466 #ifdef _KERNEL
1467 		panic("libalias:SetStateOut() unknown state");
1468 #else
1469 		abort();
1470 #endif
1471 	}
1472 	lnk->data.tcp->state.out = state;
1473 }
1474 
1475 int
1476 GetStateIn(struct alias_link *lnk)
1477 {
1478 	/* TCP input state */
1479 	return (lnk->data.tcp->state.in);
1480 }
1481 
1482 int
1483 GetStateOut(struct alias_link *lnk)
1484 {
1485 	/* TCP output state */
1486 	return (lnk->data.tcp->state.out);
1487 }
1488 
1489 struct in_addr
1490 GetOriginalAddress(struct alias_link *lnk)
1491 {
1492 	if (lnk->src_addr.s_addr == INADDR_ANY)
1493 		return (lnk->la->aliasAddress);
1494 	else
1495 		return (lnk->src_addr);
1496 }
1497 
1498 struct in_addr
1499 GetDestAddress(struct alias_link *lnk)
1500 {
1501 	return (lnk->dst_addr);
1502 }
1503 
1504 struct in_addr
1505 GetAliasAddress(struct alias_link *lnk)
1506 {
1507 	if (lnk->alias_addr.s_addr == INADDR_ANY)
1508 		return (lnk->la->aliasAddress);
1509 	else
1510 		return (lnk->alias_addr);
1511 }
1512 
1513 struct in_addr
1514 GetDefaultAliasAddress(struct libalias *la)
1515 {
1516 	LIBALIAS_LOCK_ASSERT(la);
1517 	return (la->aliasAddress);
1518 }
1519 
1520 void
1521 SetDefaultAliasAddress(struct libalias *la, struct in_addr alias_addr)
1522 {
1523 	LIBALIAS_LOCK_ASSERT(la);
1524 	la->aliasAddress = alias_addr;
1525 }
1526 
1527 u_short
1528 GetOriginalPort(struct alias_link *lnk)
1529 {
1530 	return (lnk->src_port);
1531 }
1532 
1533 u_short
1534 GetAliasPort(struct alias_link *lnk)
1535 {
1536 	return (lnk->alias_port);
1537 }
1538 
1539 #ifndef NO_FW_PUNCH
1540 static u_short
1541 GetDestPort(struct alias_link *lnk)
1542 {
1543 	return (lnk->dst_port);
1544 }
1545 
1546 #endif
1547 
1548 /* Indicate that ACK numbers have been modified in a TCP connection */
1549 void
1550 SetAckModified(struct alias_link *lnk)
1551 {
1552 	lnk->data.tcp->state.ack_modified = 1;
1553 }
1554 
1555 struct in_addr
1556 GetProxyAddress(struct alias_link *lnk)
1557 {
1558 	return (lnk->proxy_addr);
1559 }
1560 
1561 void
1562 SetProxyAddress(struct alias_link *lnk, struct in_addr addr)
1563 {
1564 	lnk->proxy_addr = addr;
1565 }
1566 
1567 u_short
1568 GetProxyPort(struct alias_link *lnk)
1569 {
1570 	return (lnk->proxy_port);
1571 }
1572 
1573 void
1574 SetProxyPort(struct alias_link *lnk, u_short port)
1575 {
1576 	lnk->proxy_port = port;
1577 }
1578 
1579 /* See if ACK numbers have been modified */
1580 int
1581 GetAckModified(struct alias_link *lnk)
1582 {
1583 	return (lnk->data.tcp->state.ack_modified);
1584 }
1585 
1586 /*
1587  * Find out how much the ACK number has been altered for an
1588  * incoming TCP packet.  To do this, a circular list of ACK
1589  * numbers where the TCP packet size was altered is searched.
1590  */
1591 // XXX ip free
1592 int
1593 GetDeltaAckIn(u_long ack, struct alias_link *lnk)
1594 {
1595 	int i, j;
1596 	int delta, ack_diff_min;
1597 
1598 	delta = 0;
1599 	ack_diff_min = -1;
1600 	i = lnk->data.tcp->state.index;
1601 	for (j = 0; j < N_LINK_TCP_DATA; j++) {
1602 		struct ack_data_record x;
1603 
1604 		if (i == 0)
1605 			i = N_LINK_TCP_DATA;
1606 		i--;
1607 		x = lnk->data.tcp->ack[i];
1608 		if (x.active == 1) {
1609 			int ack_diff;
1610 
1611 			ack_diff = SeqDiff(x.ack_new, ack);
1612 			if (ack_diff >= 0) {
1613 				if (ack_diff_min >= 0) {
1614 					if (ack_diff < ack_diff_min) {
1615 						delta = x.delta;
1616 						ack_diff_min = ack_diff;
1617 					}
1618 				} else {
1619 					delta = x.delta;
1620 					ack_diff_min = ack_diff;
1621 				}
1622 			}
1623 		}
1624 	}
1625 	return (delta);
1626 }
1627 
1628 /*
1629  * Find out how much the sequence number has been altered for an
1630  * outgoing TCP packet.  To do this, a circular list of ACK numbers
1631  * where the TCP packet size was altered is searched.
1632  */
1633 // XXX ip free
1634 int
1635 GetDeltaSeqOut(u_long seq, struct alias_link *lnk)
1636 {
1637 	int i, j;
1638 	int delta, seq_diff_min;
1639 
1640 	delta = 0;
1641 	seq_diff_min = -1;
1642 	i = lnk->data.tcp->state.index;
1643 	for (j = 0; j < N_LINK_TCP_DATA; j++) {
1644 		struct ack_data_record x;
1645 
1646 		if (i == 0)
1647 			i = N_LINK_TCP_DATA;
1648 		i--;
1649 		x = lnk->data.tcp->ack[i];
1650 		if (x.active == 1) {
1651 			int seq_diff;
1652 
1653 			seq_diff = SeqDiff(x.ack_old, seq);
1654 			if (seq_diff >= 0) {
1655 				if (seq_diff_min >= 0) {
1656 					if (seq_diff < seq_diff_min) {
1657 						delta = x.delta;
1658 						seq_diff_min = seq_diff;
1659 					}
1660 				} else {
1661 					delta = x.delta;
1662 					seq_diff_min = seq_diff;
1663 				}
1664 			}
1665 		}
1666 	}
1667 	return (delta);
1668 }
1669 
1670 /*
1671  * When a TCP packet has been altered in length, save this
1672  * information in a circular list.  If enough packets have been
1673  * altered, then this list will begin to overwrite itself.
1674  */
1675 // XXX ip free
1676 void
1677 AddSeq(struct alias_link *lnk, int delta, u_int ip_hl, u_short ip_len,
1678     u_long th_seq, u_int th_off)
1679 {
1680 	struct ack_data_record x;
1681 	int hlen, tlen, dlen;
1682 	int i;
1683 
1684 	hlen = (ip_hl + th_off) << 2;
1685 	tlen = ntohs(ip_len);
1686 	dlen = tlen - hlen;
1687 
1688 	x.ack_old = htonl(ntohl(th_seq) + dlen);
1689 	x.ack_new = htonl(ntohl(th_seq) + dlen + delta);
1690 	x.delta = delta;
1691 	x.active = 1;
1692 
1693 	i = lnk->data.tcp->state.index;
1694 	lnk->data.tcp->ack[i] = x;
1695 
1696 	i++;
1697 	if (i == N_LINK_TCP_DATA)
1698 		lnk->data.tcp->state.index = 0;
1699 	else
1700 		lnk->data.tcp->state.index = i;
1701 }
1702 
1703 void
1704 SetExpire(struct alias_link *lnk, int expire)
1705 {
1706 	if (expire == 0) {
1707 		lnk->flags &= ~LINK_PERMANENT;
1708 		DeleteLink(&lnk, 0);
1709 	} else if (expire == -1) {
1710 		lnk->flags |= LINK_PERMANENT;
1711 	} else if (expire > 0) {
1712 		lnk->expire.time = expire;
1713 	} else {
1714 #ifdef LIBALIAS_DEBUG
1715 		fprintf(stderr, "PacketAlias/SetExpire(): ");
1716 		fprintf(stderr, "error in expire parameter\n");
1717 #endif
1718 	}
1719 }
1720 
1721 void
1722 SetProtocolFlags(struct alias_link *lnk, int pflags)
1723 {
1724 	lnk->pflags = pflags;
1725 }
1726 
1727 int
1728 GetProtocolFlags(struct alias_link *lnk)
1729 {
1730 	return (lnk->pflags);
1731 }
1732 
1733 void
1734 SetDestCallId(struct alias_link *lnk, u_int16_t cid)
1735 {
1736 	LIBALIAS_LOCK_ASSERT(lnk->la);
1737 	ReLink(lnk, lnk->src_addr, lnk->dst_addr, lnk->alias_addr,
1738 	    lnk->src_port, cid, lnk->alias_port, lnk->link_type, 1);
1739 }
1740 
1741 /* Miscellaneous Functions
1742 
1743     HouseKeeping()
1744     InitPacketAliasLog()
1745     UninitPacketAliasLog()
1746 */
1747 
1748 /*
1749     Whenever an outgoing or incoming packet is handled, HouseKeeping()
1750     is called to find and remove timed-out aliasing links.  Logic exists
1751     to sweep through the entire table and linked list structure
1752     every 60 seconds.
1753 
1754     (prototype in alias_local.h)
1755 */
1756 
1757 void
1758 HouseKeeping(struct libalias *la)
1759 {
1760 	static unsigned int packets = 0;
1761 	static unsigned int packet_limit = 1000;
1762 
1763 	LIBALIAS_LOCK_ASSERT(la);
1764 	packets++;
1765 
1766 	/*
1767 	 * User space time/gettimeofday/... is very expensive.
1768 	 * Kernel space cache trashing is unnecessary.
1769 	 *
1770 	 * Save system time (seconds) in global variable LibAliasTime
1771 	 * for use by other functions. This is done so as not to
1772 	 * unnecessarily waste timeline by making system calls.
1773 	 *
1774 	 * Reduce the amount of house keeping work substantially by
1775 	 * sampling over the packets.
1776 	 */
1777 	if (packet_limit <= 1 || packets % packet_limit == 0) {
1778 		time_t now;
1779 
1780 #ifdef _KERNEL
1781 		now = time_uptime;
1782 #else
1783 		now = time(NULL);
1784 #endif
1785 		if (now != LibAliasTime) {
1786 			/* retry three times a second */
1787 			packet_limit = packets / 3;
1788 			packets = 0;
1789 			LibAliasTime = now;
1790 		}
1791 
1792 	}
1793 	/* Do a cleanup for the first packets of the new second only */
1794 	if (packets < (la->udpLinkCount + la->tcpLinkCount)) {
1795 		struct alias_link * lnk = TAILQ_FIRST(&la->checkExpire);
1796 
1797 		CleanupLink(la, &lnk, 0);
1798 	}
1799 }
1800 
1801 /* Init the log file and enable logging */
1802 static int
1803 InitPacketAliasLog(struct libalias *la)
1804 {
1805 	LIBALIAS_LOCK_ASSERT(la);
1806 	if (~la->packetAliasMode & PKT_ALIAS_LOG) {
1807 #ifdef _KERNEL
1808 		if ((la->logDesc = malloc(LIBALIAS_BUF_SIZE)))
1809 			;
1810 #else
1811 		if ((la->logDesc = fopen("/var/log/alias.log", "w")))
1812 			fprintf(la->logDesc, "PacketAlias/InitPacketAliasLog: Packet alias logging enabled.\n");
1813 #endif
1814 		else
1815 			return (ENOMEM); /* log initialization failed */
1816 		la->packetAliasMode |= PKT_ALIAS_LOG;
1817 	}
1818 
1819 	return (1);
1820 }
1821 
1822 /* Close the log-file and disable logging. */
1823 static void
1824 UninitPacketAliasLog(struct libalias *la)
1825 {
1826 	LIBALIAS_LOCK_ASSERT(la);
1827 	if (la->logDesc) {
1828 #ifdef _KERNEL
1829 		free(la->logDesc);
1830 #else
1831 		fclose(la->logDesc);
1832 #endif
1833 		la->logDesc = NULL;
1834 	}
1835 	la->packetAliasMode &= ~PKT_ALIAS_LOG;
1836 }
1837 
1838 /* Outside world interfaces
1839 
1840 -- "outside world" means other than alias*.c routines --
1841 
1842     PacketAliasRedirectPort()
1843     PacketAliasAddServer()
1844     PacketAliasRedirectProto()
1845     PacketAliasRedirectAddr()
1846     PacketAliasRedirectDynamic()
1847     PacketAliasRedirectDelete()
1848     PacketAliasSetAddress()
1849     PacketAliasInit()
1850     PacketAliasUninit()
1851     PacketAliasSetMode()
1852 
1853 (prototypes in alias.h)
1854 */
1855 
1856 /* Redirection from a specific public addr:port to a
1857    private addr:port */
1858 struct alias_link *
1859 LibAliasRedirectPort(struct libalias *la, struct in_addr src_addr, u_short src_port,
1860     struct in_addr dst_addr, u_short dst_port,
1861     struct in_addr alias_addr, u_short alias_port,
1862     u_char proto)
1863 {
1864 	int link_type;
1865 	struct alias_link *lnk;
1866 
1867 	LIBALIAS_LOCK(la);
1868 	switch (proto) {
1869 	case IPPROTO_UDP:
1870 		link_type = LINK_UDP;
1871 		break;
1872 	case IPPROTO_TCP:
1873 		link_type = LINK_TCP;
1874 		break;
1875 	case IPPROTO_SCTP:
1876 		link_type = LINK_SCTP;
1877 		break;
1878 	default:
1879 #ifdef LIBALIAS_DEBUG
1880 		fprintf(stderr, "PacketAliasRedirectPort(): ");
1881 		fprintf(stderr, "only SCTP, TCP and UDP protocols allowed\n");
1882 #endif
1883 		lnk = NULL;
1884 		goto getout;
1885 	}
1886 
1887 	lnk = AddLink(la, src_addr, dst_addr, alias_addr,
1888 	    src_port, dst_port, alias_port,
1889 	    link_type);
1890 
1891 	if (lnk != NULL) {
1892 		lnk->flags |= LINK_PERMANENT;
1893 	}
1894 #ifdef LIBALIAS_DEBUG
1895 	else {
1896 		fprintf(stderr, "PacketAliasRedirectPort(): "
1897 		    "call to AddLink() failed\n");
1898 	}
1899 #endif
1900 
1901 getout:
1902 	LIBALIAS_UNLOCK(la);
1903 	return (lnk);
1904 }
1905 
1906 /* Add server to the pool of servers */
1907 int
1908 LibAliasAddServer(struct libalias *la, struct alias_link *lnk, struct in_addr addr, u_short port)
1909 {
1910 	struct server *server;
1911 	int res;
1912 
1913 	LIBALIAS_LOCK(la);
1914 	(void)la;
1915 
1916 	switch (lnk->link_type) {
1917 	case LINK_PPTP:
1918 		server = NULL;
1919 		break;
1920 	default:
1921 		server = malloc(sizeof(struct server));
1922 		break;
1923 	}
1924 
1925 	if (server != NULL) {
1926 		struct server *head;
1927 
1928 		server->addr = addr;
1929 		server->port = port;
1930 
1931 		head = lnk->server;
1932 		if (head == NULL) {
1933 			server->next = server;
1934 			/* not usable for outgoing connections */
1935 			SPLAY_REMOVE(splay_out, &la->linkSplayOut, lnk);
1936 		} else {
1937 			struct server *s;
1938 
1939 			for (s = head; s->next != head; s = s->next)
1940 				;
1941 			s->next = server;
1942 			server->next = head;
1943 		}
1944 		lnk->server = server;
1945 		res = 0;
1946 	} else
1947 		res = -1;
1948 
1949 	LIBALIAS_UNLOCK(la);
1950 	return (res);
1951 }
1952 
1953 /* Redirect packets of a given IP protocol from a specific
1954    public address to a private address */
1955 struct alias_link *
1956 LibAliasRedirectProto(struct libalias *la, struct in_addr src_addr,
1957     struct in_addr dst_addr,
1958     struct in_addr alias_addr,
1959     u_char proto)
1960 {
1961 	struct alias_link *lnk;
1962 
1963 	LIBALIAS_LOCK(la);
1964 	lnk = AddLink(la, src_addr, dst_addr, alias_addr,
1965 	    NO_SRC_PORT, NO_DEST_PORT, 0,
1966 	    proto);
1967 
1968 	if (lnk != NULL) {
1969 		lnk->flags |= LINK_PERMANENT;
1970 	}
1971 #ifdef LIBALIAS_DEBUG
1972 	else {
1973 		fprintf(stderr, "PacketAliasRedirectProto(): "
1974 		    "call to AddLink() failed\n");
1975 	}
1976 #endif
1977 
1978 	LIBALIAS_UNLOCK(la);
1979 	return (lnk);
1980 }
1981 
1982 /* Static address translation */
1983 struct alias_link *
1984 LibAliasRedirectAddr(struct libalias *la, struct in_addr src_addr,
1985     struct in_addr alias_addr)
1986 {
1987 	struct alias_link *lnk;
1988 
1989 	LIBALIAS_LOCK(la);
1990 	lnk = AddLink(la, src_addr, ANY_ADDR, alias_addr,
1991 	    0, 0, 0,
1992 	    LINK_ADDR);
1993 
1994 	if (lnk != NULL) {
1995 		lnk->flags |= LINK_PERMANENT;
1996 	}
1997 #ifdef LIBALIAS_DEBUG
1998 	else {
1999 		fprintf(stderr, "PacketAliasRedirectAddr(): "
2000 		    "call to AddLink() failed\n");
2001 	}
2002 #endif
2003 
2004 	LIBALIAS_UNLOCK(la);
2005 	return (lnk);
2006 }
2007 
2008 /* Mark the aliasing link dynamic */
2009 int
2010 LibAliasRedirectDynamic(struct libalias *la, struct alias_link *lnk)
2011 {
2012 	int res;
2013 
2014 	LIBALIAS_LOCK(la);
2015 	(void)la;
2016 
2017 	if (lnk->flags & LINK_PARTIALLY_SPECIFIED)
2018 		res = -1;
2019 	else {
2020 		lnk->flags &= ~LINK_PERMANENT;
2021 		res = 0;
2022 	}
2023 	LIBALIAS_UNLOCK(la);
2024 	return (res);
2025 }
2026 
2027 /* This is a dangerous function to put in the API,
2028    because an invalid pointer can crash the program. */
2029 void
2030 LibAliasRedirectDelete(struct libalias *la, struct alias_link *lnk)
2031 {
2032 	LIBALIAS_LOCK(la);
2033 	(void)la;
2034 	DeleteLink(&lnk, 1);
2035 	LIBALIAS_UNLOCK(la);
2036 }
2037 
2038 void
2039 LibAliasSetAddress(struct libalias *la, struct in_addr addr)
2040 {
2041 	LIBALIAS_LOCK(la);
2042 	if (la->packetAliasMode & PKT_ALIAS_RESET_ON_ADDR_CHANGE
2043 	    && la->aliasAddress.s_addr != addr.s_addr)
2044 		CleanupAliasData(la, 0);
2045 
2046 	la->aliasAddress = addr;
2047 	LIBALIAS_UNLOCK(la);
2048 }
2049 
2050 void
2051 LibAliasSetAliasPortRange(struct libalias *la, u_short port_low,
2052     u_short port_high)
2053 {
2054 	LIBALIAS_LOCK(la);
2055 	if (port_low) {
2056 		la->aliasPortLower = port_low;
2057 		/* Add 1 to the aliasPortLength as modulo has range of 1 to n-1 */
2058 		la->aliasPortLength = port_high - port_low + 1;
2059 	} else {
2060 		/* Set default values */
2061 		la->aliasPortLower = 0x8000;
2062 		la->aliasPortLength = 0x8000;
2063 	}
2064 	LIBALIAS_UNLOCK(la);
2065 }
2066 
2067 void
2068 LibAliasSetTarget(struct libalias *la, struct in_addr target_addr)
2069 {
2070 	LIBALIAS_LOCK(la);
2071 	la->targetAddress = target_addr;
2072 	LIBALIAS_UNLOCK(la);
2073 }
2074 
2075 static void
2076 finishoff(void)
2077 {
2078 	while (!LIST_EMPTY(&instancehead))
2079 		LibAliasUninit(LIST_FIRST(&instancehead));
2080 }
2081 
2082 struct libalias *
2083 LibAliasInit(struct libalias *la)
2084 {
2085 	if (la == NULL) {
2086 #ifdef _KERNEL
2087 #undef malloc	/* XXX: ugly */
2088 		la = malloc(sizeof *la, M_ALIAS, M_WAITOK | M_ZERO);
2089 #else
2090 		la = calloc(sizeof *la, 1);
2091 		if (la == NULL)
2092 			return (la);
2093 #endif
2094 
2095 #ifndef _KERNEL
2096 		/* kernel cleans up on module unload */
2097 		if (LIST_EMPTY(&instancehead))
2098 			atexit(finishoff);
2099 #endif
2100 		LIST_INSERT_HEAD(&instancehead, la, instancelist);
2101 
2102 #ifdef _KERNEL
2103 		LibAliasTime = time_uptime;
2104 #else
2105 		LibAliasTime = time(NULL);
2106 #endif
2107 
2108 		SPLAY_INIT(&la->linkSplayIn);
2109 		SPLAY_INIT(&la->linkSplayOut);
2110 		LIST_INIT(&la->pptpList);
2111 		TAILQ_INIT(&la->checkExpire);
2112 #ifdef _KERNEL
2113 		AliasSctpInit(la);
2114 #endif
2115 		LIBALIAS_LOCK_INIT(la);
2116 		LIBALIAS_LOCK(la);
2117 	} else {
2118 		LIBALIAS_LOCK(la);
2119 		CleanupAliasData(la, 1);
2120 #ifdef _KERNEL
2121 		AliasSctpTerm(la);
2122 		AliasSctpInit(la);
2123 #endif
2124 	}
2125 
2126 	la->aliasAddress.s_addr = INADDR_ANY;
2127 	la->targetAddress.s_addr = INADDR_ANY;
2128 	la->aliasPortLower = 0x8000;
2129 	la->aliasPortLength = 0x8000;
2130 
2131 	la->icmpLinkCount = 0;
2132 	la->udpLinkCount = 0;
2133 	la->tcpLinkCount = 0;
2134 	la->sctpLinkCount = 0;
2135 	la->pptpLinkCount = 0;
2136 	la->protoLinkCount = 0;
2137 	la->fragmentIdLinkCount = 0;
2138 	la->fragmentPtrLinkCount = 0;
2139 	la->sockCount = 0;
2140 
2141 	la->packetAliasMode = PKT_ALIAS_SAME_PORTS
2142 #ifndef NO_USE_SOCKETS
2143 	    | PKT_ALIAS_USE_SOCKETS
2144 #endif
2145 	    | PKT_ALIAS_RESET_ON_ADDR_CHANGE;
2146 #ifndef NO_FW_PUNCH
2147 	la->fireWallFD = -1;
2148 #endif
2149 #ifndef _KERNEL
2150 	LibAliasRefreshModules();
2151 #endif
2152 	LIBALIAS_UNLOCK(la);
2153 	return (la);
2154 }
2155 
2156 void
2157 LibAliasUninit(struct libalias *la)
2158 {
2159 	LIBALIAS_LOCK(la);
2160 #ifdef _KERNEL
2161 	AliasSctpTerm(la);
2162 #endif
2163 	CleanupAliasData(la, 1);
2164 	UninitPacketAliasLog(la);
2165 #ifndef NO_FW_PUNCH
2166 	UninitPunchFW(la);
2167 #endif
2168 	LIST_REMOVE(la, instancelist);
2169 	LIBALIAS_UNLOCK(la);
2170 	LIBALIAS_LOCK_DESTROY(la);
2171 	free(la);
2172 }
2173 
2174 /* Change mode for some operations */
2175 unsigned int
2176 LibAliasSetMode(
2177     struct libalias *la,
2178     unsigned int flags,		/* Which state to bring flags to */
2179     unsigned int mask		/* Mask of which flags to affect (use 0 to
2180 				 * do a probe for flag values) */
2181 )
2182 {
2183 	int res = -1;
2184 
2185 	LIBALIAS_LOCK(la);
2186 	if (flags & mask & PKT_ALIAS_LOG) {
2187 		/* Enable logging */
2188 		if (InitPacketAliasLog(la) == ENOMEM)
2189 			goto getout;
2190 	} else if (~flags & mask & PKT_ALIAS_LOG)
2191 		/* _Disable_ logging */
2192 		UninitPacketAliasLog(la);
2193 
2194 #ifndef NO_FW_PUNCH
2195 	if (flags & mask & PKT_ALIAS_PUNCH_FW)
2196 		/* Start punching holes in the firewall? */
2197 		InitPunchFW(la);
2198 	else if (~flags & mask & PKT_ALIAS_PUNCH_FW)
2199 		/* Stop punching holes in the firewall? */
2200 		UninitPunchFW(la);
2201 #endif
2202 
2203 	/* Other flags can be set/cleared without special action */
2204 	la->packetAliasMode = (flags & mask) | (la->packetAliasMode & ~mask);
2205 	res = la->packetAliasMode;
2206 getout:
2207 	LIBALIAS_UNLOCK(la);
2208 	return (res);
2209 }
2210 
2211 #ifndef NO_FW_PUNCH
2212 
2213 /*****************
2214   Code to support firewall punching.  This shouldn't really be in this
2215   file, but making variables global is evil too.
2216   ****************/
2217 
2218 /* Firewall include files */
2219 #include <net/if.h>
2220 #include <netinet/ip_fw.h>
2221 #include <string.h>
2222 #include <err.h>
2223 
2224 /*
2225  * helper function, updates the pointer to cmd with the length
2226  * of the current command, and also cleans up the first word of
2227  * the new command in case it has been clobbered before.
2228  */
2229 static ipfw_insn *
2230 next_cmd(ipfw_insn * cmd)
2231 {
2232 	cmd += F_LEN(cmd);
2233 	bzero(cmd, sizeof(*cmd));
2234 	return (cmd);
2235 }
2236 
2237 /*
2238  * A function to fill simple commands of size 1.
2239  * Existing flags are preserved.
2240  */
2241 static ipfw_insn *
2242 fill_cmd(ipfw_insn * cmd, enum ipfw_opcodes opcode, int size,
2243     int flags, u_int16_t arg)
2244 {
2245 	cmd->opcode = opcode;
2246 	cmd->len = ((cmd->len | flags) & (F_NOT | F_OR)) | (size & F_LEN_MASK);
2247 	cmd->arg1 = arg;
2248 	return next_cmd(cmd);
2249 }
2250 
2251 static ipfw_insn *
2252 fill_ip(ipfw_insn * cmd1, enum ipfw_opcodes opcode, u_int32_t addr)
2253 {
2254 	ipfw_insn_ip *cmd = (ipfw_insn_ip *)cmd1;
2255 
2256 	cmd->addr.s_addr = addr;
2257 	return fill_cmd(cmd1, opcode, F_INSN_SIZE(ipfw_insn_u32), 0, 0);
2258 }
2259 
2260 static ipfw_insn *
2261 fill_one_port(ipfw_insn * cmd1, enum ipfw_opcodes opcode, u_int16_t port)
2262 {
2263 	ipfw_insn_u16 *cmd = (ipfw_insn_u16 *)cmd1;
2264 
2265 	cmd->ports[0] = cmd->ports[1] = port;
2266 	return fill_cmd(cmd1, opcode, F_INSN_SIZE(ipfw_insn_u16), 0, 0);
2267 }
2268 
2269 static int
2270 fill_rule(void *buf, int bufsize, int rulenum,
2271     enum ipfw_opcodes action, int proto,
2272     struct in_addr sa, u_int16_t sp, struct in_addr da, u_int16_t dp)
2273 {
2274 	struct ip_fw *rule = (struct ip_fw *)buf;
2275 	ipfw_insn *cmd = (ipfw_insn *)rule->cmd;
2276 
2277 	bzero(buf, bufsize);
2278 	rule->rulenum = rulenum;
2279 
2280 	cmd = fill_cmd(cmd, O_PROTO, F_INSN_SIZE(ipfw_insn), 0, proto);
2281 	cmd = fill_ip(cmd, O_IP_SRC, sa.s_addr);
2282 	cmd = fill_one_port(cmd, O_IP_SRCPORT, sp);
2283 	cmd = fill_ip(cmd, O_IP_DST, da.s_addr);
2284 	cmd = fill_one_port(cmd, O_IP_DSTPORT, dp);
2285 
2286 	rule->act_ofs = (u_int32_t *)cmd - (u_int32_t *)rule->cmd;
2287 	cmd = fill_cmd(cmd, action, F_INSN_SIZE(ipfw_insn), 0, 0);
2288 
2289 	rule->cmd_len = (u_int32_t *)cmd - (u_int32_t *)rule->cmd;
2290 
2291 	return ((char *)cmd - (char *)buf);
2292 }
2293 
2294 static void
2295 InitPunchFW(struct libalias *la)
2296 {
2297 	la->fireWallField = malloc(la->fireWallNumNums);
2298 	if (la->fireWallField) {
2299 		memset(la->fireWallField, 0, la->fireWallNumNums);
2300 		if (la->fireWallFD < 0) {
2301 			la->fireWallFD = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
2302 		}
2303 		ClearAllFWHoles(la);
2304 		la->fireWallActiveNum = la->fireWallBaseNum;
2305 	}
2306 }
2307 
2308 static void
2309 UninitPunchFW(struct libalias *la)
2310 {
2311 	ClearAllFWHoles(la);
2312 	if (la->fireWallFD >= 0)
2313 		close(la->fireWallFD);
2314 	la->fireWallFD = -1;
2315 	if (la->fireWallField)
2316 		free(la->fireWallField);
2317 	la->fireWallField = NULL;
2318 	la->packetAliasMode &= ~PKT_ALIAS_PUNCH_FW;
2319 }
2320 
2321 /* Make a certain link go through the firewall */
2322 void
2323 PunchFWHole(struct alias_link *lnk)
2324 {
2325 	struct libalias *la;
2326 	int r;			/* Result code */
2327 	struct ip_fw rule;	/* On-the-fly built rule */
2328 	int fwhole;		/* Where to punch hole */
2329 
2330 	la = lnk->la;
2331 
2332 	/* Don't do anything unless we are asked to */
2333 	if (!(la->packetAliasMode & PKT_ALIAS_PUNCH_FW) ||
2334 	    la->fireWallFD < 0 ||
2335 	    lnk->link_type != LINK_TCP)
2336 		return;
2337 
2338 	memset(&rule, 0, sizeof rule);
2339 
2340 	/** Build rule **/
2341 
2342 	/* Find empty slot */
2343 	for (fwhole = la->fireWallActiveNum;
2344 	    fwhole < la->fireWallBaseNum + la->fireWallNumNums &&
2345 	    fw_tstfield(la, la->fireWallField, fwhole);
2346 	    fwhole++);
2347 	if (fwhole == la->fireWallBaseNum + la->fireWallNumNums) {
2348 		for (fwhole = la->fireWallBaseNum;
2349 		    fwhole < la->fireWallActiveNum &&
2350 		    fw_tstfield(la, la->fireWallField, fwhole);
2351 		    fwhole++);
2352 		if (fwhole == la->fireWallActiveNum) {
2353 			/* No rule point empty - we can't punch more holes. */
2354 			la->fireWallActiveNum = la->fireWallBaseNum;
2355 #ifdef LIBALIAS_DEBUG
2356 			fprintf(stderr, "libalias: Unable to create firewall hole!\n");
2357 #endif
2358 			return;
2359 		}
2360 	}
2361 	/* Start next search at next position */
2362 	la->fireWallActiveNum = fwhole + 1;
2363 
2364 	/*
2365 	 * generate two rules of the form
2366 	 *
2367 	 * add fwhole accept tcp from OAddr OPort to DAddr DPort add fwhole
2368 	 * accept tcp from DAddr DPort to OAddr OPort
2369 	 */
2370 	if (GetOriginalPort(lnk) != 0 && GetDestPort(lnk) != 0) {
2371 		u_int32_t rulebuf[255];
2372 		int i;
2373 
2374 		i = fill_rule(rulebuf, sizeof(rulebuf), fwhole,
2375 		    O_ACCEPT, IPPROTO_TCP,
2376 		    GetOriginalAddress(lnk), ntohs(GetOriginalPort(lnk)),
2377 		    GetDestAddress(lnk), ntohs(GetDestPort(lnk)));
2378 		r = setsockopt(la->fireWallFD, IPPROTO_IP, IP_FW_ADD, rulebuf, i);
2379 		if (r)
2380 			err(1, "alias punch inbound(1) setsockopt(IP_FW_ADD)");
2381 
2382 		i = fill_rule(rulebuf, sizeof(rulebuf), fwhole,
2383 		    O_ACCEPT, IPPROTO_TCP,
2384 		    GetDestAddress(lnk), ntohs(GetDestPort(lnk)),
2385 		    GetOriginalAddress(lnk), ntohs(GetOriginalPort(lnk)));
2386 		r = setsockopt(la->fireWallFD, IPPROTO_IP, IP_FW_ADD, rulebuf, i);
2387 		if (r)
2388 			err(1, "alias punch inbound(2) setsockopt(IP_FW_ADD)");
2389 	}
2390 
2391 	/* Indicate hole applied */
2392 	lnk->data.tcp->fwhole = fwhole;
2393 	fw_setfield(la, la->fireWallField, fwhole);
2394 }
2395 
2396 /* Remove a hole in a firewall associated with a particular alias
2397    lnk.  Calling this too often is harmless. */
2398 static void
2399 ClearFWHole(struct alias_link *lnk)
2400 {
2401 	struct libalias *la;
2402 
2403 	la = lnk->la;
2404 	if (lnk->link_type == LINK_TCP) {
2405 		int fwhole = lnk->data.tcp->fwhole;  /* Where is the firewall hole? */
2406 		struct ip_fw rule;
2407 
2408 		if (fwhole < 0)
2409 			return;
2410 
2411 		memset(&rule, 0, sizeof rule);	/* useless for ipfw2 */
2412 		while (!setsockopt(la->fireWallFD, IPPROTO_IP, IP_FW_DEL,
2413 		    &fwhole, sizeof fwhole));
2414 		fw_clrfield(la, la->fireWallField, fwhole);
2415 		lnk->data.tcp->fwhole = -1;
2416 	}
2417 }
2418 
2419 /* Clear out the entire range dedicated to firewall holes. */
2420 static void
2421 ClearAllFWHoles(struct libalias *la)
2422 {
2423 	struct ip_fw rule;	/* On-the-fly built rule */
2424 	int i;
2425 
2426 	if (la->fireWallFD < 0)
2427 		return;
2428 
2429 	memset(&rule, 0, sizeof rule);
2430 	for (i = la->fireWallBaseNum; i < la->fireWallBaseNum + la->fireWallNumNums; i++) {
2431 		int r = i;
2432 
2433 		while (!setsockopt(la->fireWallFD, IPPROTO_IP, IP_FW_DEL, &r, sizeof r));
2434 	}
2435 	/* XXX: third arg correct here ? /phk */
2436 	memset(la->fireWallField, 0, la->fireWallNumNums);
2437 }
2438 
2439 #endif /* !NO_FW_PUNCH */
2440 
2441 void
2442 LibAliasSetFWBase(struct libalias *la, unsigned int base, unsigned int num)
2443 {
2444 	LIBALIAS_LOCK(la);
2445 #ifndef NO_FW_PUNCH
2446 	la->fireWallBaseNum = base;
2447 	la->fireWallNumNums = num;
2448 #endif
2449 	LIBALIAS_UNLOCK(la);
2450 }
2451 
2452 void
2453 LibAliasSetSkinnyPort(struct libalias *la, unsigned int port)
2454 {
2455 	LIBALIAS_LOCK(la);
2456 	la->skinnyPort = port;
2457 	LIBALIAS_UNLOCK(la);
2458 }
2459 
2460 /*
2461  * Find the address to redirect incoming packets
2462  */
2463 struct in_addr
2464 FindSctpRedirectAddress(struct libalias *la,  struct sctp_nat_msg *sm)
2465 {
2466 	struct alias_link *lnk;
2467 	struct in_addr redir;
2468 
2469 	LIBALIAS_LOCK_ASSERT(la);
2470 	lnk = FindLinkIn(la, sm->ip_hdr->ip_src, sm->ip_hdr->ip_dst,
2471 	    sm->sctp_hdr->dest_port,sm->sctp_hdr->dest_port, LINK_SCTP, 1);
2472 	if (lnk != NULL) {
2473 		/* port redirect */
2474 		return (lnk->src_addr);
2475 	} else {
2476 		redir = FindOriginalAddress(la,sm->ip_hdr->ip_dst);
2477 		if (redir.s_addr == la->aliasAddress.s_addr ||
2478 		    redir.s_addr == la->targetAddress.s_addr) {
2479 			/* No address found */
2480 			lnk = FindLinkIn(la, sm->ip_hdr->ip_src, sm->ip_hdr->ip_dst,
2481 			    NO_DEST_PORT, 0, LINK_SCTP, 1);
2482 			if (lnk != NULL)
2483 				/* redirect proto */
2484 				return (lnk->src_addr);
2485 		}
2486 		return (redir); /* address redirect */
2487 	}
2488 }
2489