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