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