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