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