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