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 return link_fully_specified; 1163 } 1164 else if (link_unknown_dst_port != NULL) 1165 { 1166 return replace_partial_links 1167 ? ReLink(link_unknown_dst_port, 1168 link_unknown_dst_port->src_addr, dst_addr, alias_addr, 1169 link_unknown_dst_port->src_port, dst_port, alias_port, 1170 link_type) 1171 : link_unknown_dst_port; 1172 } 1173 else if (link_unknown_dst_addr != NULL) 1174 { 1175 return replace_partial_links 1176 ? ReLink(link_unknown_dst_addr, 1177 link_unknown_dst_addr->src_addr, dst_addr, alias_addr, 1178 link_unknown_dst_addr->src_port, dst_port, alias_port, 1179 link_type) 1180 : link_unknown_dst_addr; 1181 } 1182 else if (link_unknown_all != NULL) 1183 { 1184 return replace_partial_links 1185 ? ReLink(link_unknown_all, 1186 link_unknown_all->src_addr, dst_addr, alias_addr, 1187 link_unknown_all->src_port, dst_port, alias_port, 1188 link_type) 1189 : link_unknown_all; 1190 } 1191 else 1192 { 1193 return(NULL); 1194 } 1195 } 1196 1197 1198 1199 1200 /* External routines for finding/adding links 1201 1202 -- "external" means outside alias_db.c, but within alias*.c -- 1203 1204 FindIcmpIn(), FindIcmpOut() 1205 FindFragmentIn1(), FindFragmentIn2() 1206 AddFragmentPtrLink(), FindFragmentPtr() 1207 FindUdpTcpIn(), FindUdpTcpOut() 1208 FindOriginalAddress(), FindAliasAddress() 1209 1210 (prototypes in alias_local.h) 1211 */ 1212 1213 1214 struct alias_link * 1215 FindIcmpIn(struct in_addr dst_addr, 1216 struct in_addr alias_addr, 1217 u_short id_alias) 1218 { 1219 return FindLinkIn(dst_addr, alias_addr, 1220 NO_DEST_PORT, id_alias, 1221 LINK_ICMP, 0); 1222 } 1223 1224 1225 struct alias_link * 1226 FindIcmpOut(struct in_addr src_addr, 1227 struct in_addr dst_addr, 1228 u_short id) 1229 { 1230 struct alias_link * link; 1231 1232 link = FindLinkOut(src_addr, dst_addr, 1233 id, NO_DEST_PORT, 1234 LINK_ICMP); 1235 if (link == NULL) 1236 { 1237 struct in_addr alias_addr; 1238 1239 alias_addr = FindAliasAddress(src_addr); 1240 link = AddLink(src_addr, dst_addr, alias_addr, 1241 id, NO_DEST_PORT, GET_ALIAS_ID, 1242 LINK_ICMP); 1243 } 1244 1245 return(link); 1246 } 1247 1248 1249 struct alias_link * 1250 FindFragmentIn1(struct in_addr dst_addr, 1251 struct in_addr alias_addr, 1252 u_short ip_id) 1253 { 1254 struct alias_link *link; 1255 1256 link = FindLinkIn(dst_addr, alias_addr, 1257 NO_DEST_PORT, ip_id, 1258 LINK_FRAGMENT_ID, 0); 1259 1260 if (link == NULL) 1261 { 1262 link = AddLink(nullAddress, dst_addr, alias_addr, 1263 NO_SRC_PORT, NO_DEST_PORT, ip_id, 1264 LINK_FRAGMENT_ID); 1265 } 1266 1267 return(link); 1268 } 1269 1270 1271 struct alias_link * 1272 FindFragmentIn2(struct in_addr dst_addr, /* Doesn't add a link if one */ 1273 struct in_addr alias_addr, /* is not found. */ 1274 u_short ip_id) 1275 { 1276 return FindLinkIn(dst_addr, alias_addr, 1277 NO_DEST_PORT, ip_id, 1278 LINK_FRAGMENT_ID, 0); 1279 } 1280 1281 1282 struct alias_link * 1283 AddFragmentPtrLink(struct in_addr dst_addr, 1284 u_short ip_id) 1285 { 1286 return AddLink(nullAddress, dst_addr, nullAddress, 1287 NO_SRC_PORT, NO_DEST_PORT, ip_id, 1288 LINK_FRAGMENT_PTR); 1289 } 1290 1291 1292 struct alias_link * 1293 FindFragmentPtr(struct in_addr dst_addr, 1294 u_short ip_id) 1295 { 1296 return FindLinkIn(dst_addr, nullAddress, 1297 NO_DEST_PORT, ip_id, 1298 LINK_FRAGMENT_PTR, 0); 1299 } 1300 1301 1302 struct alias_link * 1303 FindUdpTcpIn(struct in_addr dst_addr, 1304 struct in_addr alias_addr, 1305 u_short dst_port, 1306 u_short alias_port, 1307 u_char proto) 1308 { 1309 int link_type; 1310 struct alias_link *link; 1311 1312 switch (proto) 1313 { 1314 case IPPROTO_UDP: 1315 link_type = LINK_UDP; 1316 break; 1317 case IPPROTO_TCP: 1318 link_type = LINK_TCP; 1319 break; 1320 default: 1321 return NULL; 1322 break; 1323 } 1324 1325 link = FindLinkIn(dst_addr, alias_addr, 1326 dst_port, alias_port, 1327 link_type, 1); 1328 1329 if (!(packetAliasMode & PKT_ALIAS_DENY_INCOMING) 1330 && !(packetAliasMode & PKT_ALIAS_PROXY_ONLY) 1331 && link == NULL) 1332 { 1333 struct in_addr target_addr; 1334 1335 target_addr = FindOriginalAddress(alias_addr); 1336 link = AddLink(target_addr, dst_addr, alias_addr, 1337 alias_port, dst_port, alias_port, 1338 link_type); 1339 } 1340 1341 return(link); 1342 } 1343 1344 1345 struct alias_link * 1346 FindUdpTcpOut(struct in_addr src_addr, 1347 struct in_addr dst_addr, 1348 u_short src_port, 1349 u_short dst_port, 1350 u_char proto) 1351 { 1352 int link_type; 1353 struct alias_link *link; 1354 1355 switch (proto) 1356 { 1357 case IPPROTO_UDP: 1358 link_type = LINK_UDP; 1359 break; 1360 case IPPROTO_TCP: 1361 link_type = LINK_TCP; 1362 break; 1363 default: 1364 return NULL; 1365 break; 1366 } 1367 1368 link = FindLinkOut(src_addr, dst_addr, src_port, dst_port, link_type); 1369 1370 if (link == NULL) 1371 { 1372 struct in_addr alias_addr; 1373 1374 alias_addr = FindAliasAddress(src_addr); 1375 link = AddLink(src_addr, dst_addr, alias_addr, 1376 src_port, dst_port, GET_ALIAS_PORT, 1377 link_type); 1378 } 1379 1380 return(link); 1381 } 1382 1383 1384 struct in_addr 1385 FindOriginalAddress(struct in_addr alias_addr) 1386 { 1387 struct alias_link *link; 1388 1389 link = FindLinkIn(nullAddress, alias_addr, 1390 0, 0, LINK_ADDR, 0); 1391 if (link == NULL) 1392 { 1393 newDefaultLink = 1; 1394 if (targetAddress.s_addr != 0) 1395 return targetAddress; 1396 else 1397 return alias_addr; 1398 } 1399 else 1400 { 1401 if (link->src_addr.s_addr == 0) 1402 return aliasAddress; 1403 else 1404 return link->src_addr; 1405 } 1406 } 1407 1408 1409 struct in_addr 1410 FindAliasAddress(struct in_addr original_addr) 1411 { 1412 struct alias_link *link; 1413 1414 link = FindLinkOut(original_addr, nullAddress, 1415 0, 0, LINK_ADDR); 1416 if (link == NULL) 1417 { 1418 return aliasAddress; 1419 } 1420 else 1421 { 1422 if (link->alias_addr.s_addr == 0) 1423 return aliasAddress; 1424 else 1425 return link->alias_addr; 1426 } 1427 } 1428 1429 1430 /* External routines for getting or changing link data 1431 (external to alias_db.c, but internal to alias*.c) 1432 1433 SetFragmentData(), GetFragmentData() 1434 SetFragmentPtr(), GetFragmentPtr() 1435 SetStateIn(), SetStateOut(), GetStateIn(), GetStateOut() 1436 GetOriginalAddress(), GetDestAddress(), GetAliasAddress() 1437 GetOriginalPort(), GetAliasPort() 1438 SetAckModified(), GetAckModified() 1439 GetDeltaAckIn(), GetDeltaSeqOut(), AddSeq() 1440 */ 1441 1442 1443 void 1444 SetFragmentAddr(struct alias_link *link, struct in_addr src_addr) 1445 { 1446 link->data.frag_addr = src_addr; 1447 } 1448 1449 1450 void 1451 GetFragmentAddr(struct alias_link *link, struct in_addr *src_addr) 1452 { 1453 *src_addr = link->data.frag_addr; 1454 } 1455 1456 1457 void 1458 SetFragmentPtr(struct alias_link *link, char *fptr) 1459 { 1460 link->data.frag_ptr = fptr; 1461 } 1462 1463 1464 void 1465 GetFragmentPtr(struct alias_link *link, char **fptr) 1466 { 1467 *fptr = link->data.frag_ptr; 1468 } 1469 1470 1471 void 1472 SetStateIn(struct alias_link *link, int state) 1473 { 1474 /* TCP input state */ 1475 switch (state) { 1476 case ALIAS_TCP_STATE_DISCONNECTED: 1477 if (link->data.tcp->state.out != ALIAS_TCP_STATE_CONNECTED) { 1478 link->expire_time = TCP_EXPIRE_DEAD; 1479 } else { 1480 link->expire_time = TCP_EXPIRE_SINGLEDEAD; 1481 } 1482 link->data.tcp->state.in = state; 1483 break; 1484 case ALIAS_TCP_STATE_CONNECTED: 1485 link->expire_time = TCP_EXPIRE_CONNECTED; 1486 /*FALLTHROUGH*/ 1487 case ALIAS_TCP_STATE_NOT_CONNECTED: 1488 link->data.tcp->state.in = state; 1489 break; 1490 default: 1491 abort(); 1492 } 1493 } 1494 1495 1496 void 1497 SetStateOut(struct alias_link *link, int state) 1498 { 1499 /* TCP output state */ 1500 switch (state) { 1501 case ALIAS_TCP_STATE_DISCONNECTED: 1502 if (link->data.tcp->state.in != ALIAS_TCP_STATE_CONNECTED) { 1503 link->expire_time = TCP_EXPIRE_DEAD; 1504 } else { 1505 link->expire_time = TCP_EXPIRE_SINGLEDEAD; 1506 } 1507 link->data.tcp->state.out = state; 1508 break; 1509 case ALIAS_TCP_STATE_CONNECTED: 1510 link->expire_time = TCP_EXPIRE_CONNECTED; 1511 /*FALLTHROUGH*/ 1512 case ALIAS_TCP_STATE_NOT_CONNECTED: 1513 link->data.tcp->state.out = state; 1514 break; 1515 default: 1516 abort(); 1517 } 1518 } 1519 1520 1521 int 1522 GetStateIn(struct alias_link *link) 1523 { 1524 /* TCP input state */ 1525 return link->data.tcp->state.in; 1526 } 1527 1528 1529 int 1530 GetStateOut(struct alias_link *link) 1531 { 1532 /* TCP output state */ 1533 return link->data.tcp->state.out; 1534 } 1535 1536 1537 struct in_addr 1538 GetOriginalAddress(struct alias_link *link) 1539 { 1540 if (link->src_addr.s_addr == 0) 1541 return aliasAddress; 1542 else 1543 return(link->src_addr); 1544 } 1545 1546 1547 struct in_addr 1548 GetDestAddress(struct alias_link *link) 1549 { 1550 return(link->dst_addr); 1551 } 1552 1553 1554 struct in_addr 1555 GetAliasAddress(struct alias_link *link) 1556 { 1557 if (link->alias_addr.s_addr == 0) 1558 return aliasAddress; 1559 else 1560 return link->alias_addr; 1561 } 1562 1563 1564 struct in_addr 1565 GetDefaultAliasAddress() 1566 { 1567 return aliasAddress; 1568 } 1569 1570 1571 void 1572 SetDefaultAliasAddress(struct in_addr alias_addr) 1573 { 1574 aliasAddress = alias_addr; 1575 } 1576 1577 1578 u_short 1579 GetOriginalPort(struct alias_link *link) 1580 { 1581 return(link->src_port); 1582 } 1583 1584 1585 u_short 1586 GetAliasPort(struct alias_link *link) 1587 { 1588 return(link->alias_port); 1589 } 1590 1591 u_short 1592 GetDestPort(struct alias_link *link) 1593 { 1594 return(link->dst_port); 1595 } 1596 1597 void 1598 SetAckModified(struct alias_link *link) 1599 { 1600 /* Indicate that ack numbers have been modified in a TCP connection */ 1601 link->data.tcp->state.ack_modified = 1; 1602 } 1603 1604 1605 struct in_addr 1606 GetProxyAddress(struct alias_link *link) 1607 { 1608 return link->proxy_addr; 1609 } 1610 1611 1612 void 1613 SetProxyAddress(struct alias_link *link, struct in_addr addr) 1614 { 1615 link->proxy_addr = addr; 1616 } 1617 1618 1619 u_short 1620 GetProxyPort(struct alias_link *link) 1621 { 1622 return link->proxy_port; 1623 } 1624 1625 1626 void 1627 SetProxyPort(struct alias_link *link, u_short port) 1628 { 1629 link->proxy_port = port; 1630 } 1631 1632 1633 int 1634 GetAckModified(struct alias_link *link) 1635 { 1636 /* See if ack numbers have been modified */ 1637 return link->data.tcp->state.ack_modified; 1638 } 1639 1640 1641 int 1642 GetDeltaAckIn(struct ip *pip, struct alias_link *link) 1643 { 1644 /* 1645 Find out how much the ack number has been altered for an incoming 1646 TCP packet. To do this, a circular list is ack numbers where the TCP 1647 packet size was altered is searched. 1648 */ 1649 1650 int i; 1651 struct tcphdr *tc; 1652 int delta, ack_diff_min; 1653 u_long ack; 1654 1655 tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); 1656 ack = tc->th_ack; 1657 1658 delta = 0; 1659 ack_diff_min = -1; 1660 for (i=0; i<N_LINK_TCP_DATA; i++) 1661 { 1662 struct ack_data_record x; 1663 1664 x = link->data.tcp->ack[i]; 1665 if (x.active == 1) 1666 { 1667 int ack_diff; 1668 1669 ack_diff = SeqDiff(x.ack_new, ack); 1670 if (ack_diff >= 0) 1671 { 1672 if (ack_diff_min >= 0) 1673 { 1674 if (ack_diff < ack_diff_min) 1675 { 1676 delta = x.delta; 1677 ack_diff_min = ack_diff; 1678 } 1679 } 1680 else 1681 { 1682 delta = x.delta; 1683 ack_diff_min = ack_diff; 1684 } 1685 } 1686 } 1687 } 1688 return (delta); 1689 } 1690 1691 1692 int 1693 GetDeltaSeqOut(struct ip *pip, struct alias_link *link) 1694 { 1695 /* 1696 Find out how much the seq number has been altered for an outgoing 1697 TCP packet. To do this, a circular list is ack numbers where the TCP 1698 packet size was altered is searched. 1699 */ 1700 1701 int i; 1702 struct tcphdr *tc; 1703 int delta, seq_diff_min; 1704 u_long seq; 1705 1706 tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); 1707 seq = tc->th_seq; 1708 1709 delta = 0; 1710 seq_diff_min = -1; 1711 for (i=0; i<N_LINK_TCP_DATA; i++) 1712 { 1713 struct ack_data_record x; 1714 1715 x = link->data.tcp->ack[i]; 1716 if (x.active == 1) 1717 { 1718 int seq_diff; 1719 1720 seq_diff = SeqDiff(x.ack_old, seq); 1721 if (seq_diff >= 0) 1722 { 1723 if (seq_diff_min >= 0) 1724 { 1725 if (seq_diff < seq_diff_min) 1726 { 1727 delta = x.delta; 1728 seq_diff_min = seq_diff; 1729 } 1730 } 1731 else 1732 { 1733 delta = x.delta; 1734 seq_diff_min = seq_diff; 1735 } 1736 } 1737 } 1738 } 1739 return (delta); 1740 } 1741 1742 1743 void 1744 AddSeq(struct ip *pip, struct alias_link *link, int delta) 1745 { 1746 /* 1747 When a TCP packet has been altered in length, save this 1748 information in a circular list. If enough packets have 1749 been altered, then this list will begin to overwrite itself. 1750 */ 1751 1752 struct tcphdr *tc; 1753 struct ack_data_record x; 1754 int hlen, tlen, dlen; 1755 int i; 1756 1757 tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); 1758 1759 hlen = (pip->ip_hl + tc->th_off) << 2; 1760 tlen = ntohs(pip->ip_len); 1761 dlen = tlen - hlen; 1762 1763 x.ack_old = htonl(ntohl(tc->th_seq) + dlen); 1764 x.ack_new = htonl(ntohl(tc->th_seq) + dlen + delta); 1765 x.delta = delta; 1766 x.active = 1; 1767 1768 i = link->data.tcp->state.index; 1769 link->data.tcp->ack[i] = x; 1770 1771 i++; 1772 if (i == N_LINK_TCP_DATA) 1773 link->data.tcp->state.index = 0; 1774 else 1775 link->data.tcp->state.index = i; 1776 } 1777 1778 void 1779 SetExpire(struct alias_link *link, int expire) 1780 { 1781 if (expire == 0) 1782 { 1783 link->flags &= ~LINK_PERMANENT; 1784 DeleteLink(link); 1785 } 1786 else if (expire == -1) 1787 { 1788 link->flags |= LINK_PERMANENT; 1789 } 1790 else if (expire > 0) 1791 { 1792 link->expire_time = expire; 1793 } 1794 else 1795 { 1796 #ifdef DEBUG 1797 fprintf(stderr, "PacketAlias/SetExpire(): "); 1798 fprintf(stderr, "error in expire parameter\n"); 1799 #endif 1800 } 1801 } 1802 1803 void 1804 ClearCheckNewLink(void) 1805 { 1806 newDefaultLink = 0; 1807 } 1808 1809 1810 /* Miscellaneous Functions 1811 1812 HouseKeeping() 1813 InitPacketAliasLog() 1814 UninitPacketAliasLog() 1815 */ 1816 1817 /* 1818 Whenever an outgoing or incoming packet is handled, HouseKeeping() 1819 is called to find and remove timed-out aliasing links. Logic exists 1820 to sweep through the entire table and linked list structure 1821 every 60 seconds. 1822 1823 (prototype in alias_local.h) 1824 */ 1825 1826 void 1827 HouseKeeping(void) 1828 { 1829 int i, n, n100; 1830 struct timeval tv; 1831 struct timezone tz; 1832 1833 /* 1834 * Save system time (seconds) in global variable timeStamp for 1835 * use by other functions. This is done so as not to unnecessarily 1836 * waste timeline by making system calls. 1837 */ 1838 gettimeofday(&tv, &tz); 1839 timeStamp = tv.tv_sec; 1840 1841 /* Compute number of spokes (output table link chains) to cover */ 1842 n100 = LINK_TABLE_OUT_SIZE * 100 + houseKeepingResidual; 1843 n100 *= timeStamp - lastCleanupTime; 1844 n100 /= ALIAS_CLEANUP_INTERVAL_SECS; 1845 1846 n = n100/100; 1847 1848 /* Handle different cases */ 1849 if (n > ALIAS_CLEANUP_MAX_SPOKES) 1850 { 1851 n = ALIAS_CLEANUP_MAX_SPOKES; 1852 lastCleanupTime = timeStamp; 1853 houseKeepingResidual = 0; 1854 1855 for (i=0; i<n; i++) 1856 IncrementalCleanup(); 1857 } 1858 else if (n > 0) 1859 { 1860 lastCleanupTime = timeStamp; 1861 houseKeepingResidual = n100 - 100*n; 1862 1863 for (i=0; i<n; i++) 1864 IncrementalCleanup(); 1865 } 1866 else if (n < 0) 1867 { 1868 #ifdef DEBUG 1869 fprintf(stderr, "PacketAlias/HouseKeeping(): "); 1870 fprintf(stderr, "something unexpected in time values\n"); 1871 #endif 1872 lastCleanupTime = timeStamp; 1873 houseKeepingResidual = 0; 1874 } 1875 } 1876 1877 1878 /* Init the log file and enable logging */ 1879 static void 1880 InitPacketAliasLog(void) 1881 { 1882 if ((~packetAliasMode & PKT_ALIAS_LOG) 1883 && (monitorFile = fopen("/var/log/alias.log", "w"))) 1884 { 1885 packetAliasMode |= PKT_ALIAS_LOG; 1886 fprintf(monitorFile, 1887 "PacketAlias/InitPacketAliasLog: Packet alias logging enabled.\n"); 1888 } 1889 } 1890 1891 1892 /* Close the log-file and disable logging. */ 1893 static void 1894 UninitPacketAliasLog(void) 1895 { 1896 if (monitorFile) { 1897 fclose(monitorFile); 1898 monitorFile = NULL; 1899 } 1900 packetAliasMode &= ~PKT_ALIAS_LOG; 1901 } 1902 1903 1904 1905 1906 1907 1908 /* Outside world interfaces 1909 1910 -- "outside world" means other than alias*.c routines -- 1911 1912 PacketAliasRedirectPort() 1913 PacketAliasRedirectAddr() 1914 PacketAliasRedirectDelete() 1915 PacketAliasSetAddress() 1916 PacketAliasInit() 1917 PacketAliasUninit() 1918 PacketAliasSetMode() 1919 1920 (prototypes in alias.h) 1921 */ 1922 1923 /* Redirection from a specific public addr:port to a 1924 a private addr:port */ 1925 struct alias_link * 1926 PacketAliasRedirectPort(struct in_addr src_addr, u_short src_port, 1927 struct in_addr dst_addr, u_short dst_port, 1928 struct in_addr alias_addr, u_short alias_port, 1929 u_char proto) 1930 { 1931 int link_type; 1932 struct alias_link *link; 1933 1934 switch(proto) 1935 { 1936 case IPPROTO_UDP: 1937 link_type = LINK_UDP; 1938 break; 1939 case IPPROTO_TCP: 1940 link_type = LINK_TCP; 1941 break; 1942 default: 1943 #ifdef DEBUG 1944 fprintf(stderr, "PacketAliasRedirectPort(): "); 1945 fprintf(stderr, "only TCP and UDP protocols allowed\n"); 1946 #endif 1947 return NULL; 1948 } 1949 1950 link = AddLink(src_addr, dst_addr, alias_addr, 1951 src_port, dst_port, alias_port, 1952 link_type); 1953 1954 if (link != NULL) 1955 { 1956 link->flags |= LINK_PERMANENT; 1957 } 1958 #ifdef DEBUG 1959 else 1960 { 1961 fprintf(stderr, "PacketAliasRedirectPort(): " 1962 "call to AddLink() failed\n"); 1963 } 1964 #endif 1965 1966 return link; 1967 } 1968 1969 /* Translate PPTP packets to a machine on the inside 1970 */ 1971 int 1972 PacketAliasPptp(struct in_addr src_addr) 1973 { 1974 1975 pptpAliasAddr = src_addr; /* Address of the inside PPTP machine */ 1976 pptpAliasFlag = src_addr.s_addr != INADDR_NONE; 1977 1978 return 1; 1979 } 1980 1981 int GetPptpAlias (struct in_addr* alias_addr) 1982 { 1983 if (pptpAliasFlag) 1984 *alias_addr = pptpAliasAddr; 1985 1986 return pptpAliasFlag; 1987 } 1988 1989 /* Static address translation */ 1990 struct alias_link * 1991 PacketAliasRedirectAddr(struct in_addr src_addr, 1992 struct in_addr alias_addr) 1993 { 1994 struct alias_link *link; 1995 1996 link = AddLink(src_addr, nullAddress, alias_addr, 1997 0, 0, 0, 1998 LINK_ADDR); 1999 2000 if (link != NULL) 2001 { 2002 link->flags |= LINK_PERMANENT; 2003 } 2004 #ifdef DEBUG 2005 else 2006 { 2007 fprintf(stderr, "PacketAliasRedirectAddr(): " 2008 "call to AddLink() failed\n"); 2009 } 2010 #endif 2011 2012 return link; 2013 } 2014 2015 2016 void 2017 PacketAliasRedirectDelete(struct alias_link *link) 2018 { 2019 /* This is a dangerous function to put in the API, 2020 because an invalid pointer can crash the program. */ 2021 2022 deleteAllLinks = 1; 2023 DeleteLink(link); 2024 deleteAllLinks = 0; 2025 } 2026 2027 2028 void 2029 PacketAliasSetAddress(struct in_addr addr) 2030 { 2031 if (packetAliasMode & PKT_ALIAS_RESET_ON_ADDR_CHANGE 2032 && aliasAddress.s_addr != addr.s_addr) 2033 CleanupAliasData(); 2034 2035 aliasAddress = addr; 2036 } 2037 2038 2039 void 2040 PacketAliasSetTarget(struct in_addr target_addr) 2041 { 2042 targetAddress = target_addr; 2043 } 2044 2045 2046 void 2047 PacketAliasInit(void) 2048 { 2049 int i; 2050 struct timeval tv; 2051 struct timezone tz; 2052 static int firstCall = 1; 2053 2054 if (firstCall == 1) 2055 { 2056 gettimeofday(&tv, &tz); 2057 timeStamp = tv.tv_sec; 2058 lastCleanupTime = tv.tv_sec; 2059 houseKeepingResidual = 0; 2060 2061 for (i=0; i<LINK_TABLE_OUT_SIZE; i++) 2062 linkTableOut[i] = NULL; 2063 for (i=0; i<LINK_TABLE_IN_SIZE; i++) 2064 linkTableIn[i] = NULL; 2065 2066 atexit(PacketAliasUninit); 2067 firstCall = 0; 2068 } 2069 else 2070 { 2071 deleteAllLinks = 1; 2072 CleanupAliasData(); 2073 deleteAllLinks = 0; 2074 } 2075 2076 aliasAddress.s_addr = 0; 2077 targetAddress.s_addr = 0; 2078 2079 icmpLinkCount = 0; 2080 udpLinkCount = 0; 2081 tcpLinkCount = 0; 2082 fragmentIdLinkCount = 0; 2083 fragmentPtrLinkCount = 0; 2084 sockCount = 0; 2085 2086 cleanupIndex =0; 2087 2088 packetAliasMode = PKT_ALIAS_SAME_PORTS 2089 | PKT_ALIAS_USE_SOCKETS 2090 | PKT_ALIAS_RESET_ON_ADDR_CHANGE; 2091 2092 pptpAliasFlag = 0; 2093 } 2094 2095 void 2096 PacketAliasUninit(void) { 2097 deleteAllLinks = 1; 2098 CleanupAliasData(); 2099 deleteAllLinks = 0; 2100 UninitPacketAliasLog(); 2101 #ifndef NO_FW_PUNCH 2102 UninitPunchFW(); 2103 #endif 2104 } 2105 2106 2107 /* Change mode for some operations */ 2108 unsigned int 2109 PacketAliasSetMode( 2110 unsigned int flags, /* Which state to bring flags to */ 2111 unsigned int mask /* Mask of which flags to affect (use 0 to do a 2112 probe for flag values) */ 2113 ) 2114 { 2115 /* Enable logging? */ 2116 if (flags & mask & PKT_ALIAS_LOG) 2117 { 2118 InitPacketAliasLog(); /* Do the enable */ 2119 } else 2120 /* _Disable_ logging? */ 2121 if (~flags & mask & PKT_ALIAS_LOG) { 2122 UninitPacketAliasLog(); 2123 } 2124 2125 #ifndef NO_FW_PUNCH 2126 /* Start punching holes in the firewall? */ 2127 if (flags & mask & PKT_ALIAS_PUNCH_FW) { 2128 InitPunchFW(); 2129 } else 2130 /* Stop punching holes in the firewall? */ 2131 if (~flags & mask & PKT_ALIAS_PUNCH_FW) { 2132 UninitPunchFW(); 2133 } 2134 #endif 2135 2136 /* Other flags can be set/cleared without special action */ 2137 packetAliasMode = (flags & mask) | (packetAliasMode & ~mask); 2138 return packetAliasMode; 2139 } 2140 2141 2142 int 2143 PacketAliasCheckNewLink(void) 2144 { 2145 return newDefaultLink; 2146 } 2147 2148 2149 #ifndef NO_FW_PUNCH 2150 2151 /***************** 2152 Code to support firewall punching. This shouldn't really be in this 2153 file, but making variables global is evil too. 2154 ****************/ 2155 2156 /* Firewall include files */ 2157 #include <sys/queue.h> 2158 #include <net/if.h> 2159 #include <netinet/ip_fw.h> 2160 #include <string.h> 2161 #include <err.h> 2162 2163 static void ClearAllFWHoles(void); 2164 2165 static int fireWallBaseNum; /* The first firewall entry free for our use */ 2166 static int fireWallNumNums; /* How many entries can we use? */ 2167 static int fireWallActiveNum; /* Which entry did we last use? */ 2168 static char *fireWallField; /* bool array for entries */ 2169 2170 #define fw_setfield(field, num) \ 2171 do { \ 2172 (field)[num] = 1; \ 2173 } /*lint -save -e717 */ while(0) /*lint -restore */ 2174 #define fw_clrfield(field, num) \ 2175 do { \ 2176 (field)[num] = 0; \ 2177 } /*lint -save -e717 */ while(0) /*lint -restore */ 2178 #define fw_tstfield(field, num) ((field)[num]) 2179 2180 void 2181 PacketAliasSetFWBase(unsigned int base, unsigned int num) { 2182 fireWallBaseNum = base; 2183 fireWallNumNums = num; 2184 } 2185 2186 static void 2187 InitPunchFW(void) { 2188 fireWallField = malloc(fireWallNumNums); 2189 if (fireWallField) { 2190 memset(fireWallField, 0, fireWallNumNums); 2191 if (fireWallFD < 0) { 2192 fireWallFD = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); 2193 } 2194 ClearAllFWHoles(); 2195 fireWallActiveNum = fireWallBaseNum; 2196 } 2197 } 2198 2199 static void 2200 UninitPunchFW(void) { 2201 ClearAllFWHoles(); 2202 if (fireWallFD >= 0) 2203 close(fireWallFD); 2204 fireWallFD = -1; 2205 if (fireWallField) 2206 free(fireWallField); 2207 fireWallField = NULL; 2208 packetAliasMode &= ~PKT_ALIAS_PUNCH_FW; 2209 } 2210 2211 /* Make a certain link go through the firewall */ 2212 void 2213 PunchFWHole(struct alias_link *link) { 2214 int r; /* Result code */ 2215 struct ip_fw rule; /* On-the-fly built rule */ 2216 int fwhole; /* Where to punch hole */ 2217 2218 /* Don't do anything unless we are asked to */ 2219 if ( !(packetAliasMode & PKT_ALIAS_PUNCH_FW) || 2220 fireWallFD < 0 || 2221 link->link_type != LINK_TCP || 2222 !link->data.tcp) 2223 return; 2224 2225 memset(&rule, 0, sizeof rule); 2226 2227 /** Build rule **/ 2228 2229 /* Find empty slot */ 2230 for (fwhole = fireWallActiveNum; 2231 fwhole < fireWallBaseNum + fireWallNumNums && 2232 fw_tstfield(fireWallField, fwhole); 2233 fwhole++) 2234 ; 2235 if (fwhole >= fireWallBaseNum + fireWallNumNums || 2236 fw_tstfield(fireWallField, fwhole)) { 2237 for (fwhole = fireWallBaseNum; 2238 fwhole < fireWallActiveNum && 2239 fw_tstfield(fireWallField, fwhole); 2240 fwhole++) 2241 ; 2242 if (fwhole == fireWallActiveNum) { 2243 /* No rule point empty - we can't punch more holes. */ 2244 fireWallActiveNum = fireWallBaseNum; 2245 #ifdef DEBUG 2246 fprintf(stderr, "libalias: Unable to create firewall hole!\n"); 2247 #endif 2248 return; 2249 } 2250 } 2251 /* Start next search at next position */ 2252 fireWallActiveNum = fwhole+1; 2253 2254 /* Build generic part of the two rules */ 2255 rule.fw_number = fwhole; 2256 rule.fw_nports = 1; /* Number of source ports; dest ports follow */ 2257 rule.fw_flg = IP_FW_F_ACCEPT; 2258 rule.fw_prot = IPPROTO_TCP; 2259 rule.fw_smsk.s_addr = INADDR_BROADCAST; 2260 rule.fw_dmsk.s_addr = INADDR_BROADCAST; 2261 2262 /* Build and apply specific part of the rules */ 2263 rule.fw_src = GetOriginalAddress(link); 2264 rule.fw_dst = GetDestAddress(link); 2265 rule.fw_uar.fw_pts[0] = ntohs(GetOriginalPort(link)); 2266 rule.fw_uar.fw_pts[1] = ntohs(GetDestPort(link)); 2267 2268 /* Skip non-bound links - XXX should not be strictly necessary, 2269 but seems to leave hole if not done. Leak of non-bound links? 2270 (Code should be left even if the problem is fixed - it is a 2271 clear optimization) */ 2272 if (rule.fw_uar.fw_pts[0] != 0 && rule.fw_uar.fw_pts[1] != 0) { 2273 r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, &rule, sizeof rule); 2274 #ifdef DEBUG 2275 if (r) 2276 err(1, "alias punch inbound(1) setsockopt(IP_FW_ADD)"); 2277 #endif 2278 rule.fw_src = GetDestAddress(link); 2279 rule.fw_dst = GetOriginalAddress(link); 2280 rule.fw_uar.fw_pts[0] = ntohs(GetDestPort(link)); 2281 rule.fw_uar.fw_pts[1] = ntohs(GetOriginalPort(link)); 2282 r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, &rule, sizeof rule); 2283 #ifdef DEBUG 2284 if (r) 2285 err(1, "alias punch inbound(2) setsockopt(IP_FW_ADD)"); 2286 #endif 2287 } 2288 /* Indicate hole applied */ 2289 link->data.tcp->fwhole = fwhole; 2290 fw_setfield(fireWallField, fwhole); 2291 } 2292 2293 /* Remove a hole in a firewall associated with a particular alias 2294 link. Calling this too often is harmless. */ 2295 static void 2296 ClearFWHole(struct alias_link *link) { 2297 if (link->link_type == LINK_TCP && link->data.tcp) { 2298 int fwhole = link->data.tcp->fwhole; /* Where is the firewall hole? */ 2299 struct ip_fw rule; 2300 2301 if (fwhole < 0) 2302 return; 2303 2304 memset(&rule, 0, sizeof rule); 2305 rule.fw_number = fwhole; 2306 while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule)) 2307 ; 2308 fw_clrfield(fireWallField, fwhole); 2309 link->data.tcp->fwhole = -1; 2310 } 2311 } 2312 2313 /* Clear out the entire range dedicated to firewall holes. */ 2314 static void 2315 ClearAllFWHoles(void) { 2316 struct ip_fw rule; /* On-the-fly built rule */ 2317 int i; 2318 2319 if (fireWallFD < 0) 2320 return; 2321 2322 memset(&rule, 0, sizeof rule); 2323 for (i = fireWallBaseNum; i < fireWallBaseNum + fireWallNumNums; i++) { 2324 rule.fw_number = i; 2325 while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule)) 2326 ; 2327 } 2328 memset(fireWallField, 0, fireWallNumNums); 2329 } 2330 #endif 2331