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