Index: head/lib/libalias/alias_db.c =================================================================== --- head/lib/libalias/alias_db.c (revision 44615) +++ head/lib/libalias/alias_db.c (revision 44616) @@ -1,2302 +1,2330 @@ /* -*- mode: c; tab-width: 8; c-basic-indent: 4; -*- Alias_db.c encapsulates all data structures used for storing packet aliasing data. Other parts of the aliasing software access data through functions provided in this file. Data storage is based on the notion of a "link", which is established for ICMP echo/reply packets, UDP datagrams and TCP stream connections. A link stores the original source and destination addresses. For UDP and TCP, it also stores source and destination port numbers, as well as an alias port number. Links are also used to store information about fragments. There is a facility for sweeping through and deleting old links as new packets are sent through. A simple timeout is used for ICMP and UDP links. TCP links are left alone unless there is an incomplete connection, in which case the link can be deleted after a certain amount of time. This software is placed into the public domain with no restrictions on its distribution. Initial version: August, 1996 (cjm) Version 1.4: September 16, 1996 (cjm) Facility for handling incoming links added. Version 1.6: September 18, 1996 (cjm) ICMP data handling simplified. Version 1.7: January 9, 1997 (cjm) Fragment handling simplified. Saves pointers for unresolved fragments. Permits links for unspecied remote ports or unspecified remote addresses. Fixed bug which did not properly zero port table entries after a link was deleted. Cleaned up some obsolete comments. Version 1.8: January 14, 1997 (cjm) Fixed data type error in StartPoint(). (This error did not exist prior to v1.7 and was discovered and fixed by Ari Suutari) Version 1.9: February 1, 1997 Optionally, connections initiated from packet aliasing host machine will will not have their port number aliased unless it conflicts with an aliasing port already being used. (cjm) All options earlier being #ifdef'ed now are available through a new interface, SetPacketAliasMode(). This allow run time control (which is now available in PPP+pktAlias through the 'alias' keyword). (ee) Added ability to create an alias port without either destination address or port specified. port type = ALIAS_PORT_UNKNOWN_DEST_ALL (ee) Removed K&R style function headers and general cleanup. (ee) Added packetAliasMode to replace compiler #defines's (ee) Allocates sockets for partially specified ports if ALIAS_USE_SOCKETS defined. (cjm) Version 2.0: March, 1997 SetAliasAddress() will now clean up alias links if the aliasing address is changed. (cjm) PacketAliasPermanentLink() function added to support permanent links. (J. Fortes suggested the need for this.) Examples: (192.168.0.1, port 23) <-> alias port 6002, unknown dest addr/port (192.168.0.2, port 21) <-> alias port 3604, known dest addr unknown dest port These permament links allow for incoming connections to machines on the local network. They can be given with a user-chosen amount of specificity, with increasing specificity meaning more security. (cjm) Quite a bit of rework to the basic engine. The portTable[] array, which kept track of which ports were in use was replaced by a table/linked list structure. (cjm) SetExpire() function added. (cjm) DeleteLink() no longer frees memory association with a pointer to a fragment (this bug was first recognized by E. Eklund in v1.9). Version 2.1: May, 1997 (cjm) Packet aliasing engine reworked so that it can handle multiple external addresses rather than just a single host address. PacketAliasRedirectPort() and PacketAliasRedirectAddr() added to the API. The first function is a more generalized version of PacketAliasPermanentLink(). The second function implements static network address translation. See HISTORY file for additional revisions. */ /* System include files */ #include #include #include #include #include #include #include /* BSD network include files */ #include #include #include #include #include #include "alias.h" #include "alias_local.h" /* Constants (note: constants are also defined near relevant functions or structs) */ /* Sizes of input and output link tables */ #define LINK_TABLE_OUT_SIZE 101 #define LINK_TABLE_IN_SIZE 4001 /* Parameters used for cleanup of expired links */ #define ALIAS_CLEANUP_INTERVAL_SECS 60 #define ALIAS_CLEANUP_MAX_SPOKES 30 /* Timouts (in seconds) for different link types) */ #define ICMP_EXPIRE_TIME 60 #define UDP_EXPIRE_TIME 60 #define FRAGMENT_ID_EXPIRE_TIME 10 #define FRAGMENT_PTR_EXPIRE_TIME 30 /* TCP link expire time for different cases */ /* When the link has been used and closed - minimal grace time to allow ACKs and potential re-connect in FTP (XXX - is this allowed?) */ #ifndef TCP_EXPIRE_DEAD # define TCP_EXPIRE_DEAD 10 #endif /* When the link has been used and closed on one side - the other side is allowed to still send data */ #ifndef TCP_EXPIRE_SINGLEDEAD # define TCP_EXPIRE_SINGLEDEAD 90 #endif /* When the link isn't yet up */ #ifndef TCP_EXPIRE_INITIAL # define TCP_EXPIRE_INITIAL 300 #endif /* When the link is up */ #ifndef TCP_EXPIRE_CONNECTED # define TCP_EXPIRE_CONNECTED 86400 #endif /* Dummy port number codes used for FindLinkIn/Out() and AddLink(). These constants can be anything except zero, which indicates an unknown port number. */ #define NO_DEST_PORT 1 #define NO_SRC_PORT 1 /* Data Structures The fundamental data structure used in this program is "struct alias_link". Whenever a TCP connection is made, a UDP datagram is sent out, or an ICMP echo request is made, a link record is made (if it has not already been created). The link record is identified by the source address/port and the destination address/port. In the case of an ICMP echo request, the source port is treated as being equivalent with the 16-bit id number of the ICMP packet. The link record also can store some auxiliary data. For TCP connections that have had sequence and acknowledgment modifications, data space is available to track these changes. A state field is used to keep track in changes to the tcp connection state. Id numbers of fragments can also be stored in the auxiliary space. Pointers to unresolved framgents can also be stored. The link records support two independent chainings. Lookup tables for input and out tables hold the initial pointers the link chains. On input, the lookup table indexes on alias port and link type. On output, the lookup table indexes on source addreess, destination address, source port, destination port and link type. */ struct ack_data_record /* used to save changes to ack/seq numbers */ { u_long ack_old; u_long ack_new; int delta; int active; }; struct tcp_state /* Information about tcp connection */ { int in; /* State for outside -> inside */ int out; /* State for inside -> outside */ int index; /* Index to ack data array */ int ack_modified; /* Indicates whether ack and seq numbers */ /* been modified */ }; #define N_LINK_TCP_DATA 3 /* Number of distinct ack number changes saved for a modified TCP stream */ struct tcp_dat { struct tcp_state state; struct ack_data_record ack[N_LINK_TCP_DATA]; int fwhole; /* Which firewall record is used for this hole? */ }; struct alias_link /* Main data structure */ { struct in_addr src_addr; /* Address and port information */ struct in_addr dst_addr; struct in_addr alias_addr; struct in_addr proxy_addr; u_short src_port; u_short dst_port; u_short alias_port; u_short proxy_port; int link_type; /* Type of link: tcp, udp, icmp, frag */ /* values for link_type */ #define LINK_ICMP 1 #define LINK_UDP 2 #define LINK_TCP 3 #define LINK_FRAGMENT_ID 4 #define LINK_FRAGMENT_PTR 5 #define LINK_ADDR 6 int flags; /* indicates special characteristics */ /* flag bits */ #define LINK_UNKNOWN_DEST_PORT 0x01 #define LINK_UNKNOWN_DEST_ADDR 0x02 #define LINK_PERMANENT 0x04 #define LINK_PARTIALLY_SPECIFIED 0x03 /* logical-or of first two bits */ #define LINK_UNFIREWALLED 0x08 int timestamp; /* Time link was last accessed */ int expire_time; /* Expire time for link */ int sockfd; /* socket descriptor */ u_int start_point_out; /* Index number in output lookup table */ u_int start_point_in; struct alias_link *next_out; /* Linked list pointers for input and */ struct alias_link *last_out; /* output tables */ struct alias_link *next_in; /* . */ struct alias_link *last_in; /* . */ union /* Auxiliary data */ { char *frag_ptr; struct in_addr frag_addr; struct tcp_dat *tcp; } data; }; /* Global Variables The global variables listed here are only accessed from within alias_db.c and so are prefixed with the static designation. */ int packetAliasMode; /* Mode flags */ /* - documented in alias.h */ static struct in_addr aliasAddress; /* Address written onto source */ /* field of IP packet. */ static struct in_addr targetAddress; /* IP address incoming packets */ /* are sent to if no aliasing */ /* link already exists */ static struct in_addr nullAddress; /* Used as a dummy parameter for */ /* some function calls */ static struct alias_link * linkTableOut[LINK_TABLE_OUT_SIZE]; /* Lookup table of pointers to */ /* chains of link records. Each */ static struct alias_link * /* link record is doubly indexed */ linkTableIn[LINK_TABLE_IN_SIZE]; /* into input and output lookup */ /* tables. */ static int icmpLinkCount; /* Link statistics */ static int udpLinkCount; static int tcpLinkCount; static int fragmentIdLinkCount; static int fragmentPtrLinkCount; static int sockCount; static int cleanupIndex; /* Index to chain of link table */ /* being inspected for old links */ static int timeStamp; /* System time in seconds for */ /* current packet */ static int lastCleanupTime; /* Last time IncrementalCleanup() */ /* was called */ static int houseKeepingResidual; /* used by HouseKeeping() */ static int deleteAllLinks; /* If equal to zero, DeleteLink() */ /* will not remove permanent links */ static FILE *monitorFile; /* File descriptor for link */ /* statistics monitoring file */ static int newDefaultLink; /* Indicates if a new aliasing */ /* link has been created after a */ /* call to PacketAliasIn/Out(). */ #ifndef NO_FW_PUNCH static int fireWallFD = -1; /* File descriptor to be able to */ /* control firewall. Opened by */ /* PacketAliasSetMode on first */ /* setting the PKT_ALIAS_PUNCH_FW */ /* flag. */ #endif static int pptpAliasFlag; /* Indicates if PPTP aliasing is */ /* on or off */ static struct in_addr pptpAliasAddr; /* Address of source of PPTP */ /* packets. */ /* Internal utility routines (used only in alias_db.c) Lookup table starting points: StartPointIn() -- link table initial search point for outgoing packets StartPointOut() -- port table initial search point for incoming packets Miscellaneous: SeqDiff() -- difference between two TCP sequences ShowAliasStats() -- send alias statistics to a monitor file */ /* Local prototypes */ static u_int StartPointIn(struct in_addr, u_short, int); static u_int StartPointOut(struct in_addr, struct in_addr, u_short, u_short, int); static int SeqDiff(u_long, u_long); static void ShowAliasStats(void); #ifndef NO_FW_PUNCH /* Firewall control */ static void InitPunchFW(void); static void UninitPunchFW(void); static void ClearFWHole(struct alias_link *link); #endif /* Log file control */ static void InitPacketAliasLog(void); static void UninitPacketAliasLog(void); static u_int StartPointIn(struct in_addr alias_addr, u_short alias_port, int link_type) { u_int n; n = alias_addr.s_addr; n += alias_port; n += link_type; return(n % LINK_TABLE_IN_SIZE); } static u_int StartPointOut(struct in_addr src_addr, struct in_addr dst_addr, u_short src_port, u_short dst_port, int link_type) { u_int n; n = src_addr.s_addr; n += dst_addr.s_addr; n += src_port; n += dst_port; n += link_type; return(n % LINK_TABLE_OUT_SIZE); } static int SeqDiff(u_long x, u_long y) { /* Return the difference between two TCP sequence numbers */ /* This function is encapsulated in case there are any unusual arithmetic conditions that need to be considered. */ return (ntohl(y) - ntohl(x)); } static void ShowAliasStats(void) { /* Used for debugging */ if (monitorFile) { fprintf(monitorFile, "icmp=%d, udp=%d, tcp=%d, frag_id=%d frag_ptr=%d", icmpLinkCount, udpLinkCount, tcpLinkCount, fragmentIdLinkCount, fragmentPtrLinkCount); fprintf(monitorFile, " / tot=%d (sock=%d)\n", icmpLinkCount + udpLinkCount + tcpLinkCount + fragmentIdLinkCount + fragmentPtrLinkCount, sockCount); fflush(monitorFile); } } /* Internal routines for finding, deleting and adding links Port Allocation: GetNewPort() -- find and reserve new alias port number GetSocket() -- try to allocate a socket for a given port Link creation and deletion: CleanupAliasData() - remove all link chains from lookup table IncrementalCleanup() - look for stale links in a single chain DeleteLink() - remove link AddLink() - add link ReLink() - change link Link search: FindLinkOut() - find link for outgoing packets FindLinkIn() - find link for incoming packets */ /* Local prototypes */ static int GetNewPort(struct alias_link *, int); static u_short GetSocket(u_short, int *, int); static void CleanupAliasData(void); static void IncrementalCleanup(void); static void DeleteLink(struct alias_link *); static struct alias_link * AddLink(struct in_addr, struct in_addr, struct in_addr, u_short, u_short, int, int); static struct alias_link * ReLink(struct alias_link *, struct in_addr, struct in_addr, struct in_addr, u_short, u_short, int, int); static struct alias_link * FindLinkOut(struct in_addr, struct in_addr, u_short, u_short, int); static struct alias_link * FindLinkIn(struct in_addr, struct in_addr, u_short, u_short, int, int); #define ALIAS_PORT_BASE 0x08000 #define ALIAS_PORT_MASK 0x07fff #define GET_NEW_PORT_MAX_ATTEMPTS 20 #define GET_ALIAS_PORT -1 #define GET_ALIAS_ID GET_ALIAS_PORT /* GetNewPort() allocates port numbers. Note that if a port number is already in use, that does not mean that it cannot be used by another link concurrently. This is because GetNewPort() looks for unused triplets: (dest addr, dest port, alias port). */ static int GetNewPort(struct alias_link *link, int alias_port_param) { int i; int max_trials; u_short port_sys; u_short port_net; /* Description of alias_port_param for GetNewPort(). When this parameter is zero or positive, it precisely specifies the port number. GetNewPort() will return this number without check that it is in use. Whis this parameter is -1, it indicates to get a randomly selected port number. */ if (alias_port_param == GET_ALIAS_PORT) { /* * The aliasing port is automatically selected * by one of two methods below: */ max_trials = GET_NEW_PORT_MAX_ATTEMPTS; if (packetAliasMode & PKT_ALIAS_SAME_PORTS) { /* * When the ALIAS_SAME_PORTS option is * chosen, the first try will be the * actual source port. If this is already * in use, the remainder of the trials * will be random. */ port_net = link->src_port; port_sys = ntohs(port_net); } else { /* First trial and all subsequent are random. */ port_sys = random() & ALIAS_PORT_MASK; port_sys += ALIAS_PORT_BASE; port_net = htons(port_sys); } } else if (alias_port_param >= 0 && alias_port_param < 0x10000) { link->alias_port = (u_short) alias_port_param; return(0); } else { +#ifdef DEBUG fprintf(stderr, "PacketAlias/GetNewPort(): "); fprintf(stderr, "input parameter error\n"); +#endif return(-1); } /* Port number search */ for (i=0; idst_addr, link->alias_addr, link->dst_port, port_net, link->link_type, 0); if (search_result == NULL) go_ahead = 1; else if (!(link->flags & LINK_PARTIALLY_SPECIFIED) && (search_result->flags & LINK_PARTIALLY_SPECIFIED)) go_ahead = 1; else go_ahead = 0; if (go_ahead) { if ((packetAliasMode && PKT_ALIAS_USE_SOCKETS) && (link->flags & LINK_PARTIALLY_SPECIFIED)) { if (GetSocket(port_net, &link->sockfd, link->link_type)) { link->alias_port = port_net; return(0); } } else { link->alias_port = port_net; return(0); } } port_sys = random() & ALIAS_PORT_MASK; port_sys += ALIAS_PORT_BASE; port_net = htons(port_sys); } +#ifdef DEBUG fprintf(stderr, "PacketAlias/GetnewPort(): "); fprintf(stderr, "could not find free port\n"); +#endif return(-1); } static u_short GetSocket(u_short port_net, int *sockfd, int link_type) { int err; int sock; struct sockaddr_in sock_addr; if (link_type == LINK_TCP) sock = socket(AF_INET, SOCK_STREAM, 0); else if (link_type == LINK_UDP) sock = socket(AF_INET, SOCK_DGRAM, 0); else { +#ifdef DEBUG fprintf(stderr, "PacketAlias/GetSocket(): "); fprintf(stderr, "incorrect link type\n"); +#endif return(0); } if (sock < 0) { +#ifdef DEBUG fprintf(stderr, "PacketAlias/GetSocket(): "); fprintf(stderr, "socket() error %d\n", *sockfd); +#endif return(0); } sock_addr.sin_family = AF_INET; sock_addr.sin_addr.s_addr = htonl(INADDR_ANY); sock_addr.sin_port = port_net; err = bind(sock, (struct sockaddr *) &sock_addr, sizeof(sock_addr)); if (err == 0) { sockCount++; *sockfd = sock; return(1); } else { close(sock); return(0); } } static void CleanupAliasData(void) { struct alias_link *link; int i, icount; icount = 0; for (i=0; inext_out; icount++; DeleteLink(link); link = link_next; } } cleanupIndex =0; } static void IncrementalCleanup(void) { int icount; struct alias_link *link; icount = 0; link = linkTableOut[cleanupIndex++]; while (link != NULL) { int idelta; struct alias_link *link_next; link_next = link->next_out; idelta = timeStamp - link->timestamp; switch (link->link_type) { case LINK_ICMP: case LINK_UDP: case LINK_FRAGMENT_ID: case LINK_FRAGMENT_PTR: if (idelta > link->expire_time) { DeleteLink(link); icount++; } break; case LINK_TCP: if (idelta > link->expire_time) { struct tcp_dat *tcp_aux; tcp_aux = link->data.tcp; if (tcp_aux->state.in != ALIAS_TCP_STATE_CONNECTED || tcp_aux->state.out != ALIAS_TCP_STATE_CONNECTED) { DeleteLink(link); icount++; } } break; } link = link_next; } if (cleanupIndex == LINK_TABLE_OUT_SIZE) cleanupIndex = 0; } void DeleteLink(struct alias_link *link) { struct alias_link *link_last; struct alias_link *link_next; /* Don't do anything if the link is marked permanent */ if (deleteAllLinks == 0 && link->flags & LINK_PERMANENT) return; #ifndef NO_FW_PUNCH /* Delete associatied firewall hole, if any */ ClearFWHole(link); #endif /* Adjust output table pointers */ link_last = link->last_out; link_next = link->next_out; if (link_last != NULL) link_last->next_out = link_next; else linkTableOut[link->start_point_out] = link_next; if (link_next != NULL) link_next->last_out = link_last; /* Adjust input table pointers */ link_last = link->last_in; link_next = link->next_in; if (link_last != NULL) link_last->next_in = link_next; else linkTableIn[link->start_point_in] = link_next; if (link_next != NULL) link_next->last_in = link_last; /* Close socket, if one has been allocated */ if (link->sockfd != -1) { sockCount--; close(link->sockfd); } /* Link-type dependent cleanup */ switch(link->link_type) { case LINK_ICMP: icmpLinkCount--; break; case LINK_UDP: udpLinkCount--; break; case LINK_TCP: tcpLinkCount--; if (link->data.tcp != NULL) free(link->data.tcp); break; case LINK_FRAGMENT_ID: fragmentIdLinkCount--; break; case LINK_FRAGMENT_PTR: fragmentPtrLinkCount--; if (link->data.frag_ptr != NULL) free(link->data.frag_ptr); break; } /* Free memory */ free(link); /* Write statistics, if logging enabled */ if (packetAliasMode & PKT_ALIAS_LOG) { ShowAliasStats(); } } static struct alias_link * AddLink(struct in_addr src_addr, struct in_addr dst_addr, struct in_addr alias_addr, u_short src_port, u_short dst_port, int alias_port_param, /* if less than zero, alias */ int link_type) /* port will be automatically */ { /* chosen. If greater than */ u_int start_point; /* zero, equal to alias port */ struct alias_link *link; struct alias_link *first_link; link = malloc(sizeof(struct alias_link)); if (link != NULL) { /* If either the aliasing address or source address are equal to the default device address (equal to the global variable aliasAddress), then set the alias address field of the link record to zero */ if (src_addr.s_addr == aliasAddress.s_addr) src_addr.s_addr = 0; if (alias_addr.s_addr == aliasAddress.s_addr) alias_addr.s_addr = 0; /* Basic initialization */ link->src_addr = src_addr; link->dst_addr = dst_addr; link->alias_addr = alias_addr; link->proxy_addr.s_addr = 0; link->src_port = src_port; link->dst_port = dst_port; link->proxy_port = 0; link->link_type = link_type; link->sockfd = -1; link->flags = 0; link->timestamp = timeStamp; /* Expiration time */ switch (link_type) { case LINK_ICMP: link->expire_time = ICMP_EXPIRE_TIME; break; case LINK_UDP: link->expire_time = UDP_EXPIRE_TIME; break; case LINK_TCP: link->expire_time = TCP_EXPIRE_INITIAL; break; case LINK_FRAGMENT_ID: link->expire_time = FRAGMENT_ID_EXPIRE_TIME; break; case LINK_FRAGMENT_PTR: link->expire_time = FRAGMENT_PTR_EXPIRE_TIME; break; } /* Determine alias flags */ if (dst_addr.s_addr == 0) link->flags |= LINK_UNKNOWN_DEST_ADDR; if (dst_port == 0) link->flags |= LINK_UNKNOWN_DEST_PORT; /* Determine alias port */ if (GetNewPort(link, alias_port_param) != 0) { free(link); return(NULL); } /* Set up pointers for output lookup table */ start_point = StartPointOut(src_addr, dst_addr, src_port, dst_port, link_type); first_link = linkTableOut[start_point]; link->last_out = NULL; link->next_out = first_link; link->start_point_out = start_point; if (first_link != NULL) first_link->last_out = link; linkTableOut[start_point] = link; /* Set up pointers for input lookup table */ start_point = StartPointIn(alias_addr, link->alias_port, link_type); first_link = linkTableIn[start_point]; link->last_in = NULL; link->next_in = first_link; link->start_point_in = start_point; if (first_link != NULL) first_link->last_in = link; linkTableIn[start_point] = link; /* Link-type dependent initialization */ switch(link_type) { struct tcp_dat *aux_tcp; case LINK_ICMP: icmpLinkCount++; break; case LINK_UDP: udpLinkCount++; break; case LINK_TCP: aux_tcp = malloc(sizeof(struct tcp_dat)); link->data.tcp = aux_tcp; if (aux_tcp != NULL) { int i; tcpLinkCount++; aux_tcp->state.in = ALIAS_TCP_STATE_NOT_CONNECTED; aux_tcp->state.out = ALIAS_TCP_STATE_NOT_CONNECTED; aux_tcp->state.index = 0; aux_tcp->state.ack_modified = 0; for (i=0; iack[i].active = 0; aux_tcp->fwhole = -1; } else { +#ifdef DEBUG fprintf(stderr, "PacketAlias/AddLink: "); fprintf(stderr, " cannot allocate auxiliary TCP data\n"); +#endif } break; case LINK_FRAGMENT_ID: fragmentIdLinkCount++; break; case LINK_FRAGMENT_PTR: fragmentPtrLinkCount++; break; } } else { +#ifdef DEBUG fprintf(stderr, "PacketAlias/AddLink(): "); fprintf(stderr, "malloc() call failed.\n"); +#endif } if (packetAliasMode & PKT_ALIAS_LOG) { ShowAliasStats(); } return(link); } static struct alias_link * ReLink(struct alias_link *old_link, struct in_addr src_addr, struct in_addr dst_addr, struct in_addr alias_addr, u_short src_port, u_short dst_port, int alias_port_param, /* if less than zero, alias */ int link_type) /* port will be automatically */ { /* chosen. If greater than */ struct alias_link *new_link; /* zero, equal to alias port */ new_link = AddLink(src_addr, dst_addr, alias_addr, src_port, dst_port, alias_port_param, link_type); #ifndef NO_FW_PUNCH if (new_link != NULL && old_link->link_type == LINK_TCP && old_link->data.tcp && old_link->data.tcp->fwhole > 0) { PunchFWHole(new_link); } #endif DeleteLink(old_link); return new_link; } static struct alias_link * FindLinkOut(struct in_addr src_addr, struct in_addr dst_addr, u_short src_port, u_short dst_port, int link_type) { u_int i; struct alias_link *link; if (src_addr.s_addr == aliasAddress.s_addr) src_addr.s_addr = 0; i = StartPointOut(src_addr, dst_addr, src_port, dst_port, link_type); link = linkTableOut[i]; while (link != NULL) { if (link->src_addr.s_addr == src_addr.s_addr && link->dst_addr.s_addr == dst_addr.s_addr && link->dst_port == dst_port && link->src_port == src_port && link->link_type == link_type) { link->timestamp = timeStamp; break; } link = link->next_out; } return(link); } struct alias_link * FindLinkIn(struct in_addr dst_addr, struct in_addr alias_addr, u_short dst_port, u_short alias_port, int link_type, int replace_partial_links) { int flags_in; u_int start_point; struct alias_link *link; struct alias_link *link_fully_specified; struct alias_link *link_unknown_all; struct alias_link *link_unknown_dst_addr; struct alias_link *link_unknown_dst_port; /* Initialize pointers */ link_fully_specified = NULL; link_unknown_all = NULL; link_unknown_dst_addr = NULL; link_unknown_dst_port = NULL; /* If either the dest addr or port is unknown, the search loop will have to know about this. */ flags_in = 0; if (dst_addr.s_addr == 0) flags_in |= LINK_UNKNOWN_DEST_ADDR; if (dst_port == 0) flags_in |= LINK_UNKNOWN_DEST_PORT; /* The following allows permanent links to be be specified as using the default aliasing address (i.e. device interface address) without knowing in advance what that address is. */ if (alias_addr.s_addr == aliasAddress.s_addr) alias_addr.s_addr = 0; /* Search loop */ start_point = StartPointIn(alias_addr, alias_port, link_type); link = linkTableIn[start_point]; while (link != NULL) { int flags; flags = flags_in | link->flags; if (!(flags & LINK_PARTIALLY_SPECIFIED)) { if (link->alias_addr.s_addr == alias_addr.s_addr && link->alias_port == alias_port && link->dst_addr.s_addr == dst_addr.s_addr && link->dst_port == dst_port && link->link_type == link_type) { link_fully_specified = link; break; } } else if ((flags & LINK_UNKNOWN_DEST_ADDR) && (flags & LINK_UNKNOWN_DEST_PORT)) { if (link->alias_addr.s_addr == alias_addr.s_addr && link->alias_port == alias_port && link->link_type == link_type) { if (link_unknown_all == NULL) link_unknown_all = link; } } else if (flags & LINK_UNKNOWN_DEST_ADDR) { if (link->alias_addr.s_addr == alias_addr.s_addr && link->alias_port == alias_port && link->link_type == link_type && link->dst_port == dst_port) { if (link_unknown_dst_addr == NULL) link_unknown_dst_addr = link; } } else if (flags & LINK_UNKNOWN_DEST_PORT) { if (link->alias_addr.s_addr == alias_addr.s_addr && link->alias_port == alias_port && link->link_type == link_type && link->dst_addr.s_addr == dst_addr.s_addr) { if (link_unknown_dst_port == NULL) link_unknown_dst_port = link; } } link = link->next_in; } if (link_fully_specified != NULL) { return link_fully_specified; } else if (link_unknown_dst_port != NULL) { return replace_partial_links ? ReLink(link_unknown_dst_port, link_unknown_dst_port->src_addr, dst_addr, alias_addr, link_unknown_dst_port->src_port, dst_port, alias_port, link_type) : link_unknown_dst_port; } else if (link_unknown_dst_addr != NULL) { return replace_partial_links ? ReLink(link_unknown_dst_addr, link_unknown_dst_addr->src_addr, dst_addr, alias_addr, link_unknown_dst_addr->src_port, dst_port, alias_port, link_type) : link_unknown_dst_addr; } else if (link_unknown_all != NULL) { return replace_partial_links ? ReLink(link_unknown_all, link_unknown_all->src_addr, dst_addr, alias_addr, link_unknown_all->src_port, dst_port, alias_port, link_type) : link_unknown_all; } else { return(NULL); } } /* External routines for finding/adding links -- "external" means outside alias_db.c, but within alias*.c -- FindIcmpIn(), FindIcmpOut() FindFragmentIn1(), FindFragmentIn2() AddFragmentPtrLink(), FindFragmentPtr() FindUdpTcpIn(), FindUdpTcpOut() FindOriginalAddress(), FindAliasAddress() (prototypes in alias_local.h) */ struct alias_link * FindIcmpIn(struct in_addr dst_addr, struct in_addr alias_addr, u_short id_alias) { return FindLinkIn(dst_addr, alias_addr, NO_DEST_PORT, id_alias, LINK_ICMP, 0); } struct alias_link * FindIcmpOut(struct in_addr src_addr, struct in_addr dst_addr, u_short id) { struct alias_link * link; link = FindLinkOut(src_addr, dst_addr, id, NO_DEST_PORT, LINK_ICMP); if (link == NULL) { struct in_addr alias_addr; alias_addr = FindAliasAddress(src_addr); link = AddLink(src_addr, dst_addr, alias_addr, id, NO_DEST_PORT, GET_ALIAS_ID, LINK_ICMP); } return(link); } struct alias_link * FindFragmentIn1(struct in_addr dst_addr, struct in_addr alias_addr, u_short ip_id) { struct alias_link *link; link = FindLinkIn(dst_addr, alias_addr, NO_DEST_PORT, ip_id, LINK_FRAGMENT_ID, 0); if (link == NULL) { link = AddLink(nullAddress, dst_addr, alias_addr, NO_SRC_PORT, NO_DEST_PORT, ip_id, LINK_FRAGMENT_ID); } return(link); } struct alias_link * FindFragmentIn2(struct in_addr dst_addr, /* Doesn't add a link if one */ struct in_addr alias_addr, /* is not found. */ u_short ip_id) { return FindLinkIn(dst_addr, alias_addr, NO_DEST_PORT, ip_id, LINK_FRAGMENT_ID, 0); } struct alias_link * AddFragmentPtrLink(struct in_addr dst_addr, u_short ip_id) { return AddLink(nullAddress, dst_addr, nullAddress, NO_SRC_PORT, NO_DEST_PORT, ip_id, LINK_FRAGMENT_PTR); } struct alias_link * FindFragmentPtr(struct in_addr dst_addr, u_short ip_id) { return FindLinkIn(dst_addr, nullAddress, NO_DEST_PORT, ip_id, LINK_FRAGMENT_PTR, 0); } struct alias_link * FindUdpTcpIn(struct in_addr dst_addr, struct in_addr alias_addr, u_short dst_port, u_short alias_port, u_char proto) { int link_type; struct alias_link *link; switch (proto) { case IPPROTO_UDP: link_type = LINK_UDP; break; case IPPROTO_TCP: link_type = LINK_TCP; break; default: return NULL; break; } link = FindLinkIn(dst_addr, alias_addr, dst_port, alias_port, link_type, 1); if (!(packetAliasMode & PKT_ALIAS_DENY_INCOMING) && !(packetAliasMode & PKT_ALIAS_PROXY_ONLY) && link == NULL) { struct in_addr target_addr; target_addr = FindOriginalAddress(alias_addr); link = AddLink(target_addr, dst_addr, alias_addr, alias_port, dst_port, alias_port, link_type); } return(link); } struct alias_link * FindUdpTcpOut(struct in_addr src_addr, struct in_addr dst_addr, u_short src_port, u_short dst_port, u_char proto) { int link_type; struct alias_link *link; switch (proto) { case IPPROTO_UDP: link_type = LINK_UDP; break; case IPPROTO_TCP: link_type = LINK_TCP; break; default: return NULL; break; } link = FindLinkOut(src_addr, dst_addr, src_port, dst_port, link_type); if (link == NULL) { struct in_addr alias_addr; alias_addr = FindAliasAddress(src_addr); link = AddLink(src_addr, dst_addr, alias_addr, src_port, dst_port, GET_ALIAS_PORT, link_type); } return(link); } struct in_addr FindOriginalAddress(struct in_addr alias_addr) { struct alias_link *link; link = FindLinkIn(nullAddress, alias_addr, 0, 0, LINK_ADDR, 0); if (link == NULL) { newDefaultLink = 1; if (targetAddress.s_addr != 0) return targetAddress; else return alias_addr; } else { if (link->src_addr.s_addr == 0) return aliasAddress; else return link->src_addr; } } struct in_addr FindAliasAddress(struct in_addr original_addr) { struct alias_link *link; link = FindLinkOut(original_addr, nullAddress, 0, 0, LINK_ADDR); if (link == NULL) { return aliasAddress; } else { if (link->alias_addr.s_addr == 0) return aliasAddress; else return link->alias_addr; } } /* External routines for getting or changing link data (external to alias_db.c, but internal to alias*.c) SetFragmentData(), GetFragmentData() SetFragmentPtr(), GetFragmentPtr() SetStateIn(), SetStateOut(), GetStateIn(), GetStateOut() GetOriginalAddress(), GetDestAddress(), GetAliasAddress() GetOriginalPort(), GetAliasPort() SetAckModified(), GetAckModified() GetDeltaAckIn(), GetDeltaSeqOut(), AddSeq() */ void SetFragmentAddr(struct alias_link *link, struct in_addr src_addr) { link->data.frag_addr = src_addr; } void GetFragmentAddr(struct alias_link *link, struct in_addr *src_addr) { *src_addr = link->data.frag_addr; } void SetFragmentPtr(struct alias_link *link, char *fptr) { link->data.frag_ptr = fptr; } void GetFragmentPtr(struct alias_link *link, char **fptr) { *fptr = link->data.frag_ptr; } void SetStateIn(struct alias_link *link, int state) { /* TCP input state */ switch (state) { case ALIAS_TCP_STATE_DISCONNECTED: if (link->data.tcp->state.out != ALIAS_TCP_STATE_CONNECTED) { link->expire_time = TCP_EXPIRE_DEAD; } else { link->expire_time = TCP_EXPIRE_SINGLEDEAD; } link->data.tcp->state.in = state; break; case ALIAS_TCP_STATE_CONNECTED: link->expire_time = TCP_EXPIRE_CONNECTED; /*FALLTHROUGH*/ case ALIAS_TCP_STATE_NOT_CONNECTED: link->data.tcp->state.in = state; break; default: abort(); } } void SetStateOut(struct alias_link *link, int state) { /* TCP output state */ switch (state) { case ALIAS_TCP_STATE_DISCONNECTED: if (link->data.tcp->state.in != ALIAS_TCP_STATE_CONNECTED) { link->expire_time = TCP_EXPIRE_DEAD; } else { link->expire_time = TCP_EXPIRE_SINGLEDEAD; } link->data.tcp->state.out = state; break; case ALIAS_TCP_STATE_CONNECTED: link->expire_time = TCP_EXPIRE_CONNECTED; /*FALLTHROUGH*/ case ALIAS_TCP_STATE_NOT_CONNECTED: link->data.tcp->state.out = state; break; default: abort(); } } int GetStateIn(struct alias_link *link) { /* TCP input state */ return link->data.tcp->state.in; } int GetStateOut(struct alias_link *link) { /* TCP output state */ return link->data.tcp->state.out; } struct in_addr GetOriginalAddress(struct alias_link *link) { if (link->src_addr.s_addr == 0) return aliasAddress; else return(link->src_addr); } struct in_addr GetDestAddress(struct alias_link *link) { return(link->dst_addr); } struct in_addr GetAliasAddress(struct alias_link *link) { if (link->alias_addr.s_addr == 0) return aliasAddress; else return link->alias_addr; } struct in_addr GetDefaultAliasAddress() { return aliasAddress; } void SetDefaultAliasAddress(struct in_addr alias_addr) { aliasAddress = alias_addr; } u_short GetOriginalPort(struct alias_link *link) { return(link->src_port); } u_short GetAliasPort(struct alias_link *link) { return(link->alias_port); } u_short GetDestPort(struct alias_link *link) { return(link->dst_port); } void SetAckModified(struct alias_link *link) { /* Indicate that ack numbers have been modified in a TCP connection */ link->data.tcp->state.ack_modified = 1; } struct in_addr GetProxyAddress(struct alias_link *link) { return link->proxy_addr; } void SetProxyAddress(struct alias_link *link, struct in_addr addr) { link->proxy_addr = addr; } u_short GetProxyPort(struct alias_link *link) { return link->proxy_port; } void SetProxyPort(struct alias_link *link, u_short port) { link->proxy_port = port; } int GetAckModified(struct alias_link *link) { /* See if ack numbers have been modified */ return link->data.tcp->state.ack_modified; } int GetDeltaAckIn(struct ip *pip, struct alias_link *link) { /* Find out how much the ack number has been altered for an incoming TCP packet. To do this, a circular list is ack numbers where the TCP packet size was altered is searched. */ int i; struct tcphdr *tc; int delta, ack_diff_min; u_long ack; tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); ack = tc->th_ack; delta = 0; ack_diff_min = -1; for (i=0; idata.tcp->ack[i]; if (x.active == 1) { int ack_diff; ack_diff = SeqDiff(x.ack_new, ack); if (ack_diff >= 0) { if (ack_diff_min >= 0) { if (ack_diff < ack_diff_min) { delta = x.delta; ack_diff_min = ack_diff; } } else { delta = x.delta; ack_diff_min = ack_diff; } } } } return (delta); } int GetDeltaSeqOut(struct ip *pip, struct alias_link *link) { /* Find out how much the seq number has been altered for an outgoing TCP packet. To do this, a circular list is ack numbers where the TCP packet size was altered is searched. */ int i; struct tcphdr *tc; int delta, seq_diff_min; u_long seq; tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); seq = tc->th_seq; delta = 0; seq_diff_min = -1; for (i=0; idata.tcp->ack[i]; if (x.active == 1) { int seq_diff; seq_diff = SeqDiff(x.ack_old, seq); if (seq_diff >= 0) { if (seq_diff_min >= 0) { if (seq_diff < seq_diff_min) { delta = x.delta; seq_diff_min = seq_diff; } } else { delta = x.delta; seq_diff_min = seq_diff; } } } } return (delta); } void AddSeq(struct ip *pip, struct alias_link *link, int delta) { /* When a TCP packet has been altered in length, save this information in a circular list. If enough packets have been altered, then this list will begin to overwrite itself. */ struct tcphdr *tc; struct ack_data_record x; int hlen, tlen, dlen; int i; tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); hlen = (pip->ip_hl + tc->th_off) << 2; tlen = ntohs(pip->ip_len); dlen = tlen - hlen; x.ack_old = htonl(ntohl(tc->th_seq) + dlen); x.ack_new = htonl(ntohl(tc->th_seq) + dlen + delta); x.delta = delta; x.active = 1; i = link->data.tcp->state.index; link->data.tcp->ack[i] = x; i++; if (i == N_LINK_TCP_DATA) link->data.tcp->state.index = 0; else link->data.tcp->state.index = i; } void SetExpire(struct alias_link *link, int expire) { if (expire == 0) { link->flags &= ~LINK_PERMANENT; DeleteLink(link); } else if (expire == -1) { link->flags |= LINK_PERMANENT; } else if (expire > 0) { link->expire_time = expire; } else { +#ifdef DEBUG fprintf(stderr, "PacketAlias/SetExpire(): "); fprintf(stderr, "error in expire parameter\n"); +#endif } } void ClearCheckNewLink(void) { newDefaultLink = 0; } /* Miscellaneous Functions HouseKeeping() InitPacketAliasLog() UninitPacketAliasLog() */ /* Whenever an outgoing or incoming packet is handled, HouseKeeping() is called to find and remove timed-out aliasing links. Logic exists to sweep through the entire table and linked list structure every 60 seconds. (prototype in alias_local.h) */ void HouseKeeping(void) { int i, n, n100; struct timeval tv; struct timezone tz; /* * Save system time (seconds) in global variable timeStamp for * use by other functions. This is done so as not to unnecessarily * waste timeline by making system calls. */ gettimeofday(&tv, &tz); timeStamp = tv.tv_sec; /* Compute number of spokes (output table link chains) to cover */ n100 = LINK_TABLE_OUT_SIZE * 100 + houseKeepingResidual; n100 *= timeStamp - lastCleanupTime; n100 /= ALIAS_CLEANUP_INTERVAL_SECS; n = n100/100; /* Handle different cases */ if (n > ALIAS_CLEANUP_MAX_SPOKES) { n = ALIAS_CLEANUP_MAX_SPOKES; lastCleanupTime = timeStamp; houseKeepingResidual = 0; for (i=0; i 0) { lastCleanupTime = timeStamp; houseKeepingResidual = n100 - 100*n; for (i=0; iflags |= LINK_PERMANENT; } +#ifdef DEBUG else { fprintf(stderr, "PacketAliasRedirectPort(): " "call to AddLink() failed\n"); } +#endif return link; } /* Translate PPTP packets to a machine on the inside */ int PacketAliasPptp(struct in_addr src_addr) { pptpAliasAddr = src_addr; /* Address of the inside PPTP machine */ pptpAliasFlag = src_addr.s_addr != INADDR_NONE; return 1; } int GetPptpAlias (struct in_addr* alias_addr) { if (pptpAliasFlag) *alias_addr = pptpAliasAddr; return pptpAliasFlag; } /* Static address translation */ struct alias_link * PacketAliasRedirectAddr(struct in_addr src_addr, struct in_addr alias_addr) { struct alias_link *link; link = AddLink(src_addr, nullAddress, alias_addr, 0, 0, 0, LINK_ADDR); if (link != NULL) { link->flags |= LINK_PERMANENT; } +#ifdef DEBUG else { fprintf(stderr, "PacketAliasRedirectAddr(): " "call to AddLink() failed\n"); } +#endif return link; } void PacketAliasRedirectDelete(struct alias_link *link) { /* This is a dangerous function to put in the API, because an invalid pointer can crash the program. */ deleteAllLinks = 1; DeleteLink(link); deleteAllLinks = 0; } void PacketAliasSetAddress(struct in_addr addr) { if (packetAliasMode & PKT_ALIAS_RESET_ON_ADDR_CHANGE && aliasAddress.s_addr != addr.s_addr) CleanupAliasData(); aliasAddress = addr; } void PacketAliasSetTarget(struct in_addr target_addr) { targetAddress = target_addr; } void PacketAliasInit(void) { int i; struct timeval tv; struct timezone tz; static int firstCall = 1; if (firstCall == 1) { gettimeofday(&tv, &tz); timeStamp = tv.tv_sec; lastCleanupTime = tv.tv_sec; houseKeepingResidual = 0; for (i=0; i #include #include #include #include static void ClearAllFWHoles(void); static int fireWallBaseNum; /* The first firewall entry free for our use */ static int fireWallNumNums; /* How many entries can we use? */ static int fireWallActiveNum; /* Which entry did we last use? */ static char *fireWallField; /* bool array for entries */ #define fw_setfield(field, num) \ do { \ (field)[num] = 1; \ } /*lint -save -e717 */ while(0) /*lint -restore */ #define fw_clrfield(field, num) \ do { \ (field)[num] = 0; \ } /*lint -save -e717 */ while(0) /*lint -restore */ #define fw_tstfield(field, num) ((field)[num]) void PacketAliasSetFWBase(unsigned int base, unsigned int num) { fireWallBaseNum = base; fireWallNumNums = num; } static void InitPunchFW(void) { fireWallField = malloc(fireWallNumNums); if (fireWallField) { memset(fireWallField, 0, fireWallNumNums); if (fireWallFD < 0) { fireWallFD = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); } ClearAllFWHoles(); fireWallActiveNum = fireWallBaseNum; } } static void UninitPunchFW(void) { ClearAllFWHoles(); if (fireWallFD >= 0) close(fireWallFD); fireWallFD = -1; if (fireWallField) free(fireWallField); fireWallField = NULL; packetAliasMode &= ~PKT_ALIAS_PUNCH_FW; } /* Make a certain link go through the firewall */ void PunchFWHole(struct alias_link *link) { int r; /* Result code */ struct ip_fw rule; /* On-the-fly built rule */ int fwhole; /* Where to punch hole */ /* Don't do anything unless we are asked to */ if ( !(packetAliasMode & PKT_ALIAS_PUNCH_FW) || fireWallFD < 0 || link->link_type != LINK_TCP || !link->data.tcp) return; memset(&rule, 0, sizeof rule); /** Build rule **/ /* Find empty slot */ for (fwhole = fireWallActiveNum; fwhole < fireWallBaseNum + fireWallNumNums && fw_tstfield(fireWallField, fwhole); fwhole++) ; if (fwhole >= fireWallBaseNum + fireWallNumNums || fw_tstfield(fireWallField, fwhole)) { for (fwhole = fireWallBaseNum; fwhole < fireWallActiveNum && fw_tstfield(fireWallField, fwhole); fwhole++) ; if (fwhole == fireWallActiveNum) { /* No rule point empty - we can't punch more holes. */ fireWallActiveNum = fireWallBaseNum; +#ifdef DEBUG fprintf(stderr, "libalias: Unable to create firewall hole!\n"); +#endif return; } } /* Start next search at next position */ fireWallActiveNum = fwhole+1; /* Build generic part of the two rules */ rule.fw_number = fwhole; rule.fw_nports = 1; /* Number of source ports; dest ports follow */ rule.fw_flg = IP_FW_F_ACCEPT; rule.fw_prot = IPPROTO_TCP; rule.fw_smsk.s_addr = INADDR_BROADCAST; rule.fw_dmsk.s_addr = INADDR_BROADCAST; /* Build and apply specific part of the rules */ rule.fw_src = GetOriginalAddress(link); rule.fw_dst = GetDestAddress(link); rule.fw_uar.fw_pts[0] = ntohs(GetOriginalPort(link)); rule.fw_uar.fw_pts[1] = ntohs(GetDestPort(link)); /* Skip non-bound links - XXX should not be strictly necessary, but seems to leave hole if not done. Leak of non-bound links? (Code should be left even if the problem is fixed - it is a clear optimization) */ if (rule.fw_uar.fw_pts[0] != 0 && rule.fw_uar.fw_pts[1] != 0) { r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, &rule, sizeof rule); +#ifdef DEBUG if (r) err(1, "alias punch inbound(1) setsockopt(IP_FW_ADD)"); +#endif rule.fw_src = GetDestAddress(link); rule.fw_dst = GetOriginalAddress(link); rule.fw_uar.fw_pts[0] = ntohs(GetDestPort(link)); rule.fw_uar.fw_pts[1] = ntohs(GetOriginalPort(link)); r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, &rule, sizeof rule); +#ifdef DEBUG if (r) err(1, "alias punch inbound(2) setsockopt(IP_FW_ADD)"); +#endif } /* Indicate hole applied */ link->data.tcp->fwhole = fwhole; fw_setfield(fireWallField, fwhole); } /* Remove a hole in a firewall associated with a particular alias link. Calling this too often is harmless. */ static void ClearFWHole(struct alias_link *link) { if (link->link_type == LINK_TCP && link->data.tcp) { int fwhole = link->data.tcp->fwhole; /* Where is the firewall hole? */ struct ip_fw rule; if (fwhole < 0) return; memset(&rule, 0, sizeof rule); rule.fw_number = fwhole; while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule)) ; fw_clrfield(fireWallField, fwhole); link->data.tcp->fwhole = -1; } } /* Clear out the entire range dedicated to firewall holes. */ static void ClearAllFWHoles(void) { struct ip_fw rule; /* On-the-fly built rule */ int i; if (fireWallFD < 0) return; memset(&rule, 0, sizeof rule); for (i = fireWallBaseNum; i < fireWallBaseNum + fireWallNumNums; i++) { rule.fw_number = i; while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule)) ; } memset(fireWallField, 0, fireWallNumNums); } #endif Index: head/lib/libalias/alias_ftp.c =================================================================== --- head/lib/libalias/alias_ftp.c (revision 44615) +++ head/lib/libalias/alias_ftp.c (revision 44616) @@ -1,227 +1,229 @@ /* Alias_ftp.c performs special processing for FTP sessions under TCP. Specifically, when a PORT command from the client side is sent, it is intercepted and modified. The address is changed to the gateway machine and an aliasing port is used. For this routine to work, the PORT command must fit entirely into a single TCP packet. This is typically the case, but exceptions can easily be envisioned under the actual specifications. Probably the most troubling aspect of the approach taken here is that the new PORT command will typically be a different length, and this causes a certain amount of bookkeeping to keep track of the changes of sequence and acknowledgment numbers, since the client machine is totally unaware of the modification to the TCP stream. This software is placed into the public domain with no restrictions on its distribution. Initial version: August, 1996 (cjm) Version 1.6 Brian Somers and Martin Renters identified an IP checksum error for modified IP packets. Version 1.7: January 9, 1996 (cjm) Differental checksum computation for change in IP packet length. Version 2.1: May, 1997 (cjm) Very minor changes to conform with local/global/function naming conventions withing the packet alising module. See HISTORY file for record of revisions. */ /* Includes */ #include #include #include #include #include #include #include #include #include "alias_local.h" static void NewFtpPortCommand(struct ip *, struct alias_link *, struct in_addr, u_short, int); void AliasHandleFtpOut( struct ip *pip, /* IP packet to examine/patch */ struct alias_link *link, /* The link to go through (aliased port) */ int maxpacketsize /* The maximum size this packet can grow to (including headers) */) { int hlen, tlen, dlen; struct in_addr true_addr; u_short true_port; char *sptr; struct tcphdr *tc; /* Calculate data length of TCP packet */ tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); hlen = (pip->ip_hl + tc->th_off) << 2; tlen = ntohs(pip->ip_len); dlen = tlen - hlen; /* Return is data length is too long or too short */ if (dlen<10 || dlen>80) return; /* Place string pointer and beginning of data */ sptr = (char *) pip; sptr += hlen; /* Parse through string using state diagram method */ { char ch, zero; int i, state; u_long a1, a2, a3, a4; u_short p1, p2; a1=0; a2=0; a3=0; a4=0; p1=0; p2=0; zero = '0'; state=-4; for (i=0; iip_hl << 2)); hlen = (pip->ip_hl + tc->th_off) << 2; tlen = ntohs(pip->ip_len); dlen = tlen - hlen; /* Create new PORT command */ { char stemp[80]; char *sptr; u_short alias_port; u_char *ptr; int a1, a2, a3, a4, p1, p2; struct in_addr alias_address; /* Decompose alias address into quad format */ alias_address = GetAliasAddress(link); ptr = (u_char *) &alias_address.s_addr; a1 = *ptr++; a2=*ptr++; a3=*ptr++; a4=*ptr; /* Decompose alias port into pair format */ alias_port = GetAliasPort(ftp_link); ptr = (char *) &alias_port; p1 = *ptr++; p2=*ptr; /* Generate command string */ sprintf(stemp, "PORT %d,%d,%d,%d,%d,%d\r\n", a1,a2,a3,a4,p1,p2); /* Save string length for IP header modification */ slen = strlen(stemp); /* Copy into IP packet */ sptr = (char *) pip; sptr += hlen; strncpy(sptr, stemp, maxpacketsize-hlen); } /* Save information regarding modified seq and ack numbers */ { int delta; SetAckModified(link); delta = GetDeltaSeqOut(pip, link); AddSeq(pip, link, delta+slen-dlen); } /* Revise IP header */ { u_short new_len; new_len = htons(hlen + slen); DifferentialChecksum(&pip->ip_sum, &new_len, &pip->ip_len, 1); pip->ip_len = new_len; } /* Compute TCP checksum for revised packet */ tc->th_sum = 0; tc->th_sum = TcpChecksum(pip); } else { +#ifdef DEBUG fprintf(stderr, "PacketAlias/HandleFtpOut: Cannot allocate FTP data port\n"); +#endif } } Index: head/lib/libalias/alias_nbt.c =================================================================== --- head/lib/libalias/alias_nbt.c (revision 44615) +++ head/lib/libalias/alias_nbt.c (revision 44616) @@ -1,713 +1,711 @@ /* * Written by Atsushi Murai * * Copyright (C) 1998, System Planning and Engineering Co. All rights reserverd. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by the System Planning and Engineering Co. The name of the * SPEC may not be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $Id: alias_nbt.c,v 1.1 1998/05/24 03:03:10 amurai Exp $ + * $Id: alias_nbt.c,v 1.2 1998/12/14 02:25:32 dillon Exp $ * * TODO: * oClean up. * oConsidering for word alignment for other platform. */ /* alias_nbt.c performs special processing for NetBios over TCP/IP sessions by UDP. Initial version: May, 1998 (Atsushi Murai ) See HISTORY file for record of revisions. */ /* Includes */ #include #include #include #include #include #include #include #include #include #include #include "alias_local.h" #define ADJUST_CHECKSUM(acc, cksum) { \ acc += cksum; \ if (acc < 0) \ { \ acc = -acc; \ acc = (acc >> 16) + (acc & 0xffff); \ acc += acc >> 16; \ cksum = (u_short) ~acc; \ } \ else \ { \ acc = (acc >> 16) + (acc & 0xffff); \ acc += acc >> 16; \ cksum = (u_short) acc; \ } \ } typedef struct { struct in_addr oldaddr; u_short oldport; struct in_addr newaddr; u_short newport; u_short *uh_sum; } NBTArguments; typedef struct { unsigned char type; unsigned char flags; u_short id; struct in_addr source_ip; u_short source_port; u_short len; u_short offset; } NbtDataHeader; #define OpQuery 0 #define OpUnknown 4 #define OpRegist 5 #define OpRelease 6 #define OpWACK 7 #define OpRefresh 8 typedef struct { u_short nametrid; u_short dir:1, opcode:4, nmflags:7, rcode:4; u_short qdcount; u_short ancount; u_short nscount; u_short arcount; } NbtNSHeader; #define FMT_ERR 0x1 #define SRV_ERR 0x2 #define IMP_ERR 0x4 #define RFS_ERR 0x5 #define ACT_ERR 0x6 #define CFT_ERR 0x7 -/******************************************************************* - * copy an IP address from one buffer to another * - *******************************************************************/ -void putip(void *dest,void *src) -{ - memcpy(dest,src,4); -} -void PrintRcode( u_char rcode ) { +#ifdef DEBUG +static void PrintRcode( u_char rcode ) { switch (rcode) { case FMT_ERR: printf("\nFormat Error."); case SRV_ERR: printf("\nSever failure."); case IMP_ERR: printf("\nUnsupported request error.\n"); case RFS_ERR: printf("\nRefused error.\n"); case ACT_ERR: printf("\nActive error.\n"); case CFT_ERR: printf("\nName in conflict error.\n"); default: printf("\n???=%0x\n", rcode ); } } +#endif /* Handling Name field */ -u_char *AliasHandleName ( u_char *p, char *pmax ) { +static u_char *AliasHandleName ( u_char *p, char *pmax ) { u_char *s; u_char c; int compress; /* Following length field */ if (p == NULL || (char *)p >= pmax) return(NULL); if (*p & 0xc0 ) { p = p + 2; if ((char *)p > pmax) return(NULL); return ((u_char *)p); } while ( ( *p & 0x3f) != 0x00 ) { s = p + 1; if ( *p == 0x20 ) compress = 1; else compress = 0; /* Get next length field */ p = (u_char *)(p + (*p & 0x3f) + 1); if ((char *)p > pmax) { p = NULL; break; } #ifdef DEBUG printf(":"); #endif while (s < p) { if ( compress == 1 ) { c = (u_char )(((((*s & 0x0f) << 4) | (*(s+1) & 0x0f)) - 0x11)); #ifdef DEBUG if (isprint( c ) ) printf("%c", c ); else printf("<0x%02x>", c ); #endif s +=2; } else { #ifdef DEBUG printf("%c", *s); #endif s++; } } #ifdef DEBUG printf(":"); #endif fflush(stdout); } /* Set up to out of Name field */ if (p == NULL || (char *)p >= pmax) p = NULL; else p++; return ((u_char *)p); } /* * NetBios Datagram Handler (IP/UDP) */ #define DGM_DIRECT_UNIQ 0x10 #define DGM_DIRECT_GROUP 0x11 #define DGM_BROADCAST 0x12 #define DGM_ERROR 0x13 #define DGM_QUERY 0x14 #define DGM_POSITIVE_RES 0x15 #define DGM_NEGATIVE_RES 0x16 int AliasHandleUdpNbt( struct ip *pip, /* IP packet to examine/patch */ struct alias_link *link, struct in_addr *alias_address, u_short alias_port ) { struct udphdr * uh; NbtDataHeader *ndh; u_char *p = NULL; char *pmax; /* Calculate data length of UDP packet */ uh = (struct udphdr *) ((char *) pip + (pip->ip_hl << 2)); pmax = (char *)uh + ntohs( uh->uh_ulen ); ndh = (NbtDataHeader *)((char *)uh + (sizeof (struct udphdr))); if ((char *)(ndh + 1) > pmax) return(-1); #ifdef DEBUG printf("\nType=%02x,", ndh->type ); #endif switch ( ndh->type ) { case DGM_DIRECT_UNIQ: case DGM_DIRECT_GROUP: case DGM_BROADCAST: p = (u_char *)ndh + 14; p = AliasHandleName ( p, pmax ); /* Source Name */ p = AliasHandleName ( p, pmax ); /* Destination Name */ break; case DGM_ERROR: p = (u_char *)ndh + 11; break; case DGM_QUERY: case DGM_POSITIVE_RES: case DGM_NEGATIVE_RES: p = (u_char *)ndh + 10; p = AliasHandleName ( p, pmax ); /* Destination Name */ break; } if (p == NULL || (char *)p > pmax) p = NULL; #ifdef DEBUG printf("%s:%d-->", inet_ntoa(ndh->source_ip), ntohs(ndh->source_port) ); #endif /* Doing a IP address and Port number Translation */ if ( uh->uh_sum != 0 ) { int acc; u_short *sptr; acc = ndh->source_port; acc -= alias_port; sptr = (u_short *) &(ndh->source_ip); acc += *sptr++; acc += *sptr; sptr = (u_short *) alias_address; acc -= *sptr++; acc -= *sptr; ADJUST_CHECKSUM(acc, uh->uh_sum) } ndh->source_ip = *alias_address; ndh->source_port = alias_port; #ifdef DEBUG printf("%s:%d\n", inet_ntoa(ndh->source_ip), ntohs(ndh->source_port) ); fflush(stdout); #endif return((p == NULL) ? -1 : 0); } /* Question Section */ #define QS_TYPE_NB 0x0020 #define QS_TYPE_NBSTAT 0x0021 #define QS_CLAS_IN 0x0001 typedef struct { u_short type; /* The type of Request */ u_short class; /* The class of Request */ } NBTNsQuestion; -u_char * +static u_char * AliasHandleQuestion( u_short count, NBTNsQuestion *q, char *pmax, NBTArguments *nbtarg) { while ( count != 0 ) { /* Name Filed */ q = (NBTNsQuestion *)AliasHandleName((u_char *)q, pmax); if (q == NULL || (char *)(q + 1) > pmax) { q = NULL; break; } /* Type and Class filed */ switch ( ntohs(q->type) ) { case QS_TYPE_NB: case QS_TYPE_NBSTAT: q= q+1; break; default: +#ifdef DEBUG printf("\nUnknown Type on Question %0x\n", ntohs(q->type) ); +#endif break; } count--; } /* Set up to out of Question Section */ return ((u_char *)q); } /* Resource Record */ #define RR_TYPE_A 0x0001 #define RR_TYPE_NS 0x0002 #define RR_TYPE_NULL 0x000a #define RR_TYPE_NB 0x0020 #define RR_TYPE_NBSTAT 0x0021 #define RR_CLAS_IN 0x0001 #define SizeOfNsResource 8 typedef struct { u_short type; u_short class; unsigned int ttl; u_short rdlen; } NBTNsResource; #define SizeOfNsRNB 6 typedef struct { u_short g:1, ont:2, resv:13; struct in_addr addr; } NBTNsRNB; -u_char * +static u_char * AliasHandleResourceNB( NBTNsResource *q, char *pmax, NBTArguments *nbtarg) { NBTNsRNB *nb; u_short bcount; if (q == NULL || (char *)(q + 1) > pmax) return(NULL); /* Check out a length */ bcount = ntohs(q->rdlen); /* Forward to Resource NB position */ nb = (NBTNsRNB *)((u_char *)q + SizeOfNsResource); /* Processing all in_addr array */ #ifdef DEBUG printf("NB rec[%s", inet_ntoa(nbtarg->oldaddr)); printf("->%s, %dbytes] ",inet_ntoa(nbtarg->newaddr ), bcount); #endif while ( nb != NULL && bcount != 0 ) { if ((char *)(nb + 1) > pmax) { nb = NULL; break; } #ifdef DEBUG printf("<%s>", inet_ntoa(nb->addr) ); #endif if (!bcmp(&nbtarg->oldaddr,&nb->addr, sizeof(struct in_addr) ) ) { if ( *nbtarg->uh_sum != 0 ) { int acc; u_short *sptr; sptr = (u_short *) &(nb->addr); acc = *sptr++; acc += *sptr; sptr = (u_short *) &(nbtarg->newaddr); acc -= *sptr++; acc -= *sptr; ADJUST_CHECKSUM(acc, *nbtarg->uh_sum) } nb->addr = nbtarg->newaddr; #ifdef DEBUG printf("O"); #endif } #ifdef DEBUG else { printf("."); } #endif nb=(NBTNsRNB *)((u_char *)nb + SizeOfNsRNB); bcount -= SizeOfNsRNB; } if (nb == NULL || (char *)(nb + 1) > pmax) { nb = NULL; } return ((u_char *)nb); } #define SizeOfResourceA 6 typedef struct { struct in_addr addr; } NBTNsResourceA; -u_char * +static u_char * AliasHandleResourceA( NBTNsResource *q, char *pmax, NBTArguments *nbtarg) { NBTNsResourceA *a; u_short bcount; if (q == NULL || (char *)(q + 1) > pmax) return(NULL); /* Forward to Resource A position */ a = (NBTNsResourceA *)( (u_char *)q + sizeof(NBTNsResource) ); /* Check out of length */ bcount = ntohs(q->rdlen); /* Processing all in_addr array */ #ifdef DEBUG printf("Arec [%s", inet_ntoa(nbtarg->oldaddr)); printf("->%s]",inet_ntoa(nbtarg->newaddr )); #endif while ( bcount != 0 ) { if (a == NULL || (char *)(a + 1) > pmax) return(NULL); #ifdef DEBUG printf("..%s", inet_ntoa(a->addr) ); #endif if ( !bcmp(&nbtarg->oldaddr, &a->addr, sizeof(struct in_addr) ) ) { if ( *nbtarg->uh_sum != 0 ) { int acc; u_short *sptr; sptr = (u_short *) &(a->addr); /* Old */ acc = *sptr++; acc += *sptr; sptr = (u_short *) &nbtarg->newaddr; /* New */ acc -= *sptr++; acc -= *sptr; ADJUST_CHECKSUM(acc, *nbtarg->uh_sum) } a->addr = nbtarg->newaddr; } a++; /*XXXX*/ bcount -= SizeOfResourceA; } if (a == NULL || (char *)(a + 1) > pmax) a = NULL; return ((u_char *)a); } typedef struct { u_short opcode:4, flags:8, resv:4; } NBTNsResourceNULL; -u_char * +static u_char * AliasHandleResourceNULL( NBTNsResource *q, char *pmax, NBTArguments *nbtarg) { NBTNsResourceNULL *n; u_short bcount; if (q == NULL || (char *)(q + 1) > pmax) return(NULL); /* Forward to Resource NULL position */ n = (NBTNsResourceNULL *)( (u_char *)q + sizeof(NBTNsResource) ); /* Check out of length */ bcount = ntohs(q->rdlen); /* Processing all in_addr array */ while ( bcount != 0 ) { if ((char *)(n + 1) > pmax) { n = NULL; break; } n++; bcount -= sizeof(NBTNsResourceNULL); } if ((char *)(n + 1) > pmax) n = NULL; return ((u_char *)n); } -u_char * +static u_char * AliasHandleResourceNS( NBTNsResource *q, char *pmax, NBTArguments *nbtarg) { NBTNsResourceNULL *n; u_short bcount; if (q == NULL || (char *)(q + 1) > pmax) return(NULL); /* Forward to Resource NULL position */ n = (NBTNsResourceNULL *)( (u_char *)q + sizeof(NBTNsResource) ); /* Check out of length */ bcount = ntohs(q->rdlen); /* Resource Record Name Filed */ q = (NBTNsResource *)AliasHandleName( (u_char *)n, pmax ); /* XXX */ if (q == NULL || (char *)((u_char *)n + bcount) > pmax) return(NULL); else return ((u_char *)n + bcount); } typedef struct { u_short numnames; } NBTNsResourceNBSTAT; -u_char * +static u_char * AliasHandleResourceNBSTAT( NBTNsResource *q, char *pmax, NBTArguments *nbtarg) { NBTNsResourceNBSTAT *n; u_short bcount; if (q == NULL || (char *)(q + 1) > pmax) return(NULL); /* Forward to Resource NBSTAT position */ n = (NBTNsResourceNBSTAT *)( (u_char *)q + sizeof(NBTNsResource) ); /* Check out of length */ bcount = ntohs(q->rdlen); if (q == NULL || (char *)((u_char *)n + bcount) > pmax) return(NULL); else return ((u_char *)n + bcount); } -u_char * +static u_char * AliasHandleResource( u_short count, NBTNsResource *q, char *pmax, NBTArguments *nbtarg) { while ( count != 0 ) { /* Resource Record Name Filed */ q = (NBTNsResource *)AliasHandleName( (u_char *)q, pmax ); if (q == NULL || (char *)(q + 1) > pmax) break; #ifdef DEBUG printf("type=%02x, count=%d\n", ntohs(q->type), count ); #endif /* Type and Class filed */ switch ( ntohs(q->type) ) { case RR_TYPE_NB: q = (NBTNsResource *)AliasHandleResourceNB( q, pmax, nbtarg ); break; case RR_TYPE_A: q = (NBTNsResource *)AliasHandleResourceA( q, pmax, nbtarg ); break; case RR_TYPE_NS: q = (NBTNsResource *)AliasHandleResourceNS( q, pmax, nbtarg ); break; case RR_TYPE_NULL: q = (NBTNsResource *)AliasHandleResourceNULL( q, pmax, nbtarg ); break; case RR_TYPE_NBSTAT: q = (NBTNsResource *)AliasHandleResourceNBSTAT( q, pmax, nbtarg ); break; default: +#ifdef DEBUG printf( "\nUnknown Type of Resource %0x\n", ntohs(q->type) ); +#endif break; } count--; } fflush(stdout); return ((u_char *)q); } int AliasHandleUdpNbtNS( struct ip *pip, /* IP packet to examine/patch */ struct alias_link *link, struct in_addr *alias_address, u_short *alias_port, struct in_addr *original_address, u_short *original_port ) { struct udphdr * uh; NbtNSHeader * nsh; u_char * p; char *pmax; NBTArguments nbtarg; /* Set up Common Parameter */ nbtarg.oldaddr = *alias_address; nbtarg.oldport = *alias_port; nbtarg.newaddr = *original_address; nbtarg.newport = *original_port; /* Calculate data length of UDP packet */ uh = (struct udphdr *) ((char *) pip + (pip->ip_hl << 2)); nbtarg.uh_sum = &(uh->uh_sum); nsh = (NbtNSHeader *)((char *)uh + (sizeof(struct udphdr))); p = (u_char *)(nsh + 1); pmax = (char *)uh + ntohs( uh->uh_ulen ); if ((char *)(nsh + 1) > pmax) return(-1); #ifdef DEBUG printf(" [%s] ID=%02x, op=%01x, flag=%02x, rcode=%01x, qd=%04x" ", an=%04x, ns=%04x, ar=%04x, [%d]-->", nsh->dir ? "Response": "Request", nsh->nametrid, nsh->opcode, nsh->nmflags, nsh->rcode, ntohs(nsh->qdcount), ntohs(nsh->ancount), ntohs(nsh->nscount), ntohs(nsh->arcount), (u_char *)p -(u_char *)nsh ); #endif /* Question Entries */ if (ntohs(nsh->qdcount) !=0 ) { p = AliasHandleQuestion( ntohs(nsh->qdcount), (NBTNsQuestion *)p, pmax, &nbtarg ); } /* Answer Resource Records */ if (ntohs(nsh->ancount) !=0 ) { p = AliasHandleResource( ntohs(nsh->ancount), (NBTNsResource *)p, pmax, &nbtarg ); } /* Authority Resource Recodrs */ if (ntohs(nsh->nscount) !=0 ) { p = AliasHandleResource( ntohs(nsh->nscount), (NBTNsResource *)p, pmax, &nbtarg ); } /* Additional Resource Recodrs */ if (ntohs(nsh->arcount) !=0 ) { p = AliasHandleResource( ntohs(nsh->arcount), (NBTNsResource *)p, pmax, &nbtarg ); } #ifdef DEBUG PrintRcode(nsh->rcode); #endif return ((p == NULL) ? -1 : 0); } - Index: head/lib/libalias/alias_proxy.c =================================================================== --- head/lib/libalias/alias_proxy.c (revision 44615) +++ head/lib/libalias/alias_proxy.c (revision 44616) @@ -1,801 +1,805 @@ /* file: alias_proxy.c This file encapsulates special operations related to transparent proxy redirection. This is where packets with a particular destination, usually tcp port 80, are redirected to a proxy server. When packets are proxied, the destination address and port are modified. In certain cases, it is necessary to somehow encode the original address/port info into the packet. Two methods are presently supported: addition of a [DEST addr port] string at the beginning a of tcp stream, or inclusion of an optional field in the IP header. There is one public API function: PacketAliasProxyRule() -- Adds and deletes proxy rules. Rules are stored in a linear linked list, so lookup efficiency won't be too good for large lists. Initial development: April, 1998 (cjm) */ /* System includes */ #include #include #include #include #include #include #include /* BSD IPV4 includes */ #include #include #include #include #include #include "alias_local.h" /* Functions used by alias*.c */ #include "alias.h" /* Public API functions for libalias */ /* Data structures */ /* * A linked list of arbitrary length, based on struct proxy_entry is * used to store proxy rules. */ struct proxy_entry { #define PROXY_TYPE_ENCODE_NONE 1 #define PROXY_TYPE_ENCODE_TCPSTREAM 2 #define PROXY_TYPE_ENCODE_IPHDR 3 int rule_index; int proxy_type; u_char proto; u_short proxy_port; u_short server_port; struct in_addr server_addr; struct in_addr src_addr; struct in_addr src_mask; struct in_addr dst_addr; struct in_addr dst_mask; struct proxy_entry *next; struct proxy_entry *last; }; /* File scope variables */ static struct proxy_entry *proxyList; /* Local (static) functions: IpMask() -- Utility function for creating IP masks from integer (1-32) specification. IpAddr() -- Utility function for converting string to IP address IpPort() -- Utility function for converting string to port number RuleAdd() -- Adds an element to the rule list. RuleDelete() -- Removes an element from the rule list. RuleNumberDelete() -- Removes all elements from the rule list having a certain rule number. ProxyEncodeTcpStream() -- Adds [DEST x.x.x.x xxxx] to the beginning of a TCP stream. ProxyEncodeIpHeader() -- Adds an IP option indicating the true destination of a proxied IP packet */ static int IpMask(int, struct in_addr *); static int IpAddr(char *, struct in_addr *); static int IpPort(char *, int, int *); static void RuleAdd(struct proxy_entry *); static void RuleDelete(struct proxy_entry *); static int RuleNumberDelete(int); static void ProxyEncodeTcpStream(struct alias_link *, struct ip *, int); static void ProxyEncodeIpHeader(struct ip *, int); static int IpMask(int nbits, struct in_addr *mask) { int i; u_int imask; if (nbits < 0 || nbits > 32) return -1; imask = 0; for (i=0; i> 1) + 0x80000000; mask->s_addr = htonl(imask); return 0; } static int IpAddr(char *s, struct in_addr *addr) { if (inet_aton(s, addr) == 0) return -1; else return 0; } static int IpPort(char *s, int proto, int *port) { int n; n = sscanf(s, "%d", port); if (n != 1) { struct servent *se; if (proto == IPPROTO_TCP) se = getservbyname(s, "tcp"); else if (proto == IPPROTO_UDP) se = getservbyname(s, "udp"); else return -1; if (se == NULL) return -1; *port = (u_int) ntohs(se->s_port); } return 0; } void RuleAdd(struct proxy_entry *entry) { int rule_index; struct proxy_entry *ptr; struct proxy_entry *ptr_last; if (proxyList == NULL) { proxyList = entry; entry->last = NULL; entry->next = NULL; return; } rule_index = entry->rule_index; ptr = proxyList; ptr_last = NULL; while (ptr != NULL) { if (ptr->rule_index >= rule_index) { if (ptr_last == NULL) { entry->next = proxyList; entry->last = NULL; proxyList->last = entry; proxyList = entry; return; } ptr_last->next = entry; ptr->last = entry; entry->last = ptr->last; entry->next = ptr; return; } ptr_last = ptr; ptr = ptr->next; } ptr_last->next = entry; entry->last = ptr_last; entry->next = NULL; } static void RuleDelete(struct proxy_entry *entry) { if (entry->last != NULL) entry->last->next = entry->next; else proxyList = entry->next; if (entry->next != NULL) entry->next->last = entry->last; free(entry); } static int RuleNumberDelete(int rule_index) { int err; struct proxy_entry *ptr; err = -1; ptr = proxyList; while (ptr != NULL) { struct proxy_entry *ptr_next; ptr_next = ptr->next; if (ptr->rule_index == rule_index) { err = 0; RuleDelete(ptr); } ptr = ptr_next; } return err; } static void ProxyEncodeTcpStream(struct alias_link *link, struct ip *pip, int maxpacketsize) { int slen; char buffer[40]; struct tcphdr *tc; /* Compute pointer to tcp header */ tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); /* Don't modify if once already modified */ if (GetAckModified (link)) return; /* Translate destination address and port to string form */ snprintf(buffer, sizeof(buffer) - 2, "[DEST %s %d]", inet_ntoa(GetProxyAddress (link)), (u_int) ntohs(GetProxyPort (link))); /* Pad string out to a multiple of two in length */ slen = strlen(buffer); switch (slen % 2) { case 0: strcat(buffer, " \n"); slen += 2; break; case 1: strcat(buffer, "\n"); slen += 1; } /* Check for packet overflow */ if ((ntohs(pip->ip_len) + strlen(buffer)) > maxpacketsize) return; /* Shift existing TCP data and insert destination string */ { int dlen; int hlen; u_char *p; hlen = (pip->ip_hl + tc->th_off) << 2; dlen = ntohs (pip->ip_len) - hlen; /* Modify first packet that has data in it */ if (dlen == 0) return; p = (char *) pip; p += hlen; memmove(p + slen, p, dlen); memcpy(p, buffer, slen); } /* Save information about modfied sequence number */ { int delta; SetAckModified(link); delta = GetDeltaSeqOut(pip, link); AddSeq(pip, link, delta+slen); } /* Update IP header packet length and checksum */ { int accumulate; accumulate = pip->ip_len; pip->ip_len = htons(ntohs(pip->ip_len) + slen); accumulate -= pip->ip_len; ADJUST_CHECKSUM(accumulate, pip->ip_sum); } /* Update TCP checksum, Use TcpChecksum since so many things have already changed. */ tc->th_sum = 0; tc->th_sum = TcpChecksum (pip); } static void ProxyEncodeIpHeader(struct ip *pip, int maxpacketsize) { #define OPTION_LEN_BYTES 8 #define OPTION_LEN_INT16 4 #define OPTION_LEN_INT32 2 u_char option[OPTION_LEN_BYTES]; -fprintf(stdout, " ip cksum 1 = %x\n", (u_int) IpChecksum(pip)); -fprintf(stdout, "tcp cksum 1 = %x\n", (u_int) TcpChecksum(pip)); +#ifdef DEBUG + fprintf(stdout, " ip cksum 1 = %x\n", (u_int) IpChecksum(pip)); + fprintf(stdout, "tcp cksum 1 = %x\n", (u_int) TcpChecksum(pip)); +#endif /* Check to see that there is room to add an IP option */ if (pip->ip_hl > (0x0f - OPTION_LEN_INT32)) return; /* Build option and copy into packet */ { u_char *ptr; struct tcphdr *tc; ptr = (u_char *) pip; ptr += 20; memcpy(ptr + OPTION_LEN_BYTES, ptr, ntohs(pip->ip_len) - 20); option[0] = 0x64; /* class: 3 (reserved), option 4 */ option[1] = OPTION_LEN_BYTES; memcpy(&option[2], (u_char *) &pip->ip_dst, 4); tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); memcpy(&option[6], (u_char *) &tc->th_sport, 2); memcpy(ptr, option, 8); } /* Update checksum, header length and packet length */ { int i; int accumulate; u_short *sptr; sptr = (u_short *) option; accumulate = 0; for (i=0; iip_hl += OPTION_LEN_INT32; accumulate -= *sptr; accumulate += pip->ip_len; pip->ip_len = htons(ntohs(pip->ip_len) + OPTION_LEN_BYTES); accumulate -= pip->ip_len; ADJUST_CHECKSUM(accumulate, pip->ip_sum); } #undef OPTION_LEN_BYTES #undef OPTION_LEN_INT16 #undef OPTION_LEN_INT32 -fprintf(stdout, " ip cksum 2 = %x\n", (u_int) IpChecksum(pip)); -fprintf(stdout, "tcp cksum 2 = %x\n", (u_int) TcpChecksum(pip)); +#ifdef DEBUG + fprintf(stdout, " ip cksum 2 = %x\n", (u_int) IpChecksum(pip)); + fprintf(stdout, "tcp cksum 2 = %x\n", (u_int) TcpChecksum(pip)); +#endif } /* Functions by other packet alias source files ProxyCheck() -- Checks whether an outgoing packet should be proxied. ProxyModify() -- Encodes the original destination address/port for a packet which is to be redirected to a proxy server. */ int ProxyCheck(struct ip *pip, struct in_addr *proxy_server_addr, u_short *proxy_server_port) { u_short dst_port; struct in_addr src_addr; struct in_addr dst_addr; struct proxy_entry *ptr; src_addr = pip->ip_src; dst_addr = pip->ip_dst; dst_port = ((struct tcphdr *) ((char *) pip + (pip->ip_hl << 2))) ->th_dport; ptr = proxyList; while (ptr != NULL) { u_short proxy_port; proxy_port = ptr->proxy_port; if ((dst_port == proxy_port || proxy_port == 0) && pip->ip_p == ptr->proto && src_addr.s_addr != ptr->server_addr.s_addr) { struct in_addr src_addr_masked; struct in_addr dst_addr_masked; src_addr_masked.s_addr = src_addr.s_addr & ptr->src_mask.s_addr; dst_addr_masked.s_addr = dst_addr.s_addr & ptr->dst_mask.s_addr; if ((src_addr_masked.s_addr == ptr->src_addr.s_addr) && (dst_addr_masked.s_addr == ptr->dst_addr.s_addr)) { if ((*proxy_server_port = ptr->server_port) == 0) *proxy_server_port = dst_port; *proxy_server_addr = ptr->server_addr; return ptr->proxy_type; } } ptr = ptr->next; } return 0; } void ProxyModify(struct alias_link *link, struct ip *pip, int maxpacketsize, int proxy_type) { switch (proxy_type) { case PROXY_TYPE_ENCODE_IPHDR: ProxyEncodeIpHeader(pip, maxpacketsize); break; case PROXY_TYPE_ENCODE_TCPSTREAM: ProxyEncodeTcpStream(link, pip, maxpacketsize); break; } } /* Public API functions */ int PacketAliasProxyRule(char *cmd) { /* * This function takes command strings of the form: * * server [:] * [port ] * [rule n] * [proto tcp|udp] * [src [/n]] * [dst [/n]] * [type encode_tcp_stream|encode_ip_hdr|no_encode] * * delete * * Subfields can be in arbitrary order. Port numbers and addresses * must be in either numeric or symbolic form. An optional rule number * is used to control the order in which rules are searched. If two * rules have the same number, then search order cannot be guaranteed, * and the rules should be disjoint. If no rule number is specified, * then 0 is used, and group 0 rules are always checked before any * others. */ int i, n, len; int cmd_len; int token_count; int state; char *token; char buffer[256]; char str_port[sizeof(buffer)]; char str_server_port[sizeof(buffer)]; int rule_index; int proto; int proxy_type; int proxy_port; int server_port; struct in_addr server_addr; struct in_addr src_addr, src_mask; struct in_addr dst_addr, dst_mask; struct proxy_entry *proxy_entry; /* Copy command line into a buffer */ cmd_len = strlen(cmd); if (cmd_len > (sizeof(buffer) - 1)) return -1; strcpy(buffer, cmd); /* Convert to lower case */ len = strlen(buffer); for (i=0; iproxy_type = proxy_type; proxy_entry->rule_index = rule_index; proxy_entry->proto = proto; proxy_entry->proxy_port = htons(proxy_port); proxy_entry->server_port = htons(server_port); proxy_entry->server_addr = server_addr; proxy_entry->src_addr.s_addr = src_addr.s_addr & src_mask.s_addr; proxy_entry->dst_addr.s_addr = dst_addr.s_addr & dst_mask.s_addr; proxy_entry->src_mask = src_mask; proxy_entry->dst_mask = dst_mask; RuleAdd(proxy_entry); return 0; } Index: head/sys/netinet/libalias/alias_db.c =================================================================== --- head/sys/netinet/libalias/alias_db.c (revision 44615) +++ head/sys/netinet/libalias/alias_db.c (revision 44616) @@ -1,2302 +1,2330 @@ /* -*- mode: c; tab-width: 8; c-basic-indent: 4; -*- Alias_db.c encapsulates all data structures used for storing packet aliasing data. Other parts of the aliasing software access data through functions provided in this file. Data storage is based on the notion of a "link", which is established for ICMP echo/reply packets, UDP datagrams and TCP stream connections. A link stores the original source and destination addresses. For UDP and TCP, it also stores source and destination port numbers, as well as an alias port number. Links are also used to store information about fragments. There is a facility for sweeping through and deleting old links as new packets are sent through. A simple timeout is used for ICMP and UDP links. TCP links are left alone unless there is an incomplete connection, in which case the link can be deleted after a certain amount of time. This software is placed into the public domain with no restrictions on its distribution. Initial version: August, 1996 (cjm) Version 1.4: September 16, 1996 (cjm) Facility for handling incoming links added. Version 1.6: September 18, 1996 (cjm) ICMP data handling simplified. Version 1.7: January 9, 1997 (cjm) Fragment handling simplified. Saves pointers for unresolved fragments. Permits links for unspecied remote ports or unspecified remote addresses. Fixed bug which did not properly zero port table entries after a link was deleted. Cleaned up some obsolete comments. Version 1.8: January 14, 1997 (cjm) Fixed data type error in StartPoint(). (This error did not exist prior to v1.7 and was discovered and fixed by Ari Suutari) Version 1.9: February 1, 1997 Optionally, connections initiated from packet aliasing host machine will will not have their port number aliased unless it conflicts with an aliasing port already being used. (cjm) All options earlier being #ifdef'ed now are available through a new interface, SetPacketAliasMode(). This allow run time control (which is now available in PPP+pktAlias through the 'alias' keyword). (ee) Added ability to create an alias port without either destination address or port specified. port type = ALIAS_PORT_UNKNOWN_DEST_ALL (ee) Removed K&R style function headers and general cleanup. (ee) Added packetAliasMode to replace compiler #defines's (ee) Allocates sockets for partially specified ports if ALIAS_USE_SOCKETS defined. (cjm) Version 2.0: March, 1997 SetAliasAddress() will now clean up alias links if the aliasing address is changed. (cjm) PacketAliasPermanentLink() function added to support permanent links. (J. Fortes suggested the need for this.) Examples: (192.168.0.1, port 23) <-> alias port 6002, unknown dest addr/port (192.168.0.2, port 21) <-> alias port 3604, known dest addr unknown dest port These permament links allow for incoming connections to machines on the local network. They can be given with a user-chosen amount of specificity, with increasing specificity meaning more security. (cjm) Quite a bit of rework to the basic engine. The portTable[] array, which kept track of which ports were in use was replaced by a table/linked list structure. (cjm) SetExpire() function added. (cjm) DeleteLink() no longer frees memory association with a pointer to a fragment (this bug was first recognized by E. Eklund in v1.9). Version 2.1: May, 1997 (cjm) Packet aliasing engine reworked so that it can handle multiple external addresses rather than just a single host address. PacketAliasRedirectPort() and PacketAliasRedirectAddr() added to the API. The first function is a more generalized version of PacketAliasPermanentLink(). The second function implements static network address translation. See HISTORY file for additional revisions. */ /* System include files */ #include #include #include #include #include #include #include /* BSD network include files */ #include #include #include #include #include #include "alias.h" #include "alias_local.h" /* Constants (note: constants are also defined near relevant functions or structs) */ /* Sizes of input and output link tables */ #define LINK_TABLE_OUT_SIZE 101 #define LINK_TABLE_IN_SIZE 4001 /* Parameters used for cleanup of expired links */ #define ALIAS_CLEANUP_INTERVAL_SECS 60 #define ALIAS_CLEANUP_MAX_SPOKES 30 /* Timouts (in seconds) for different link types) */ #define ICMP_EXPIRE_TIME 60 #define UDP_EXPIRE_TIME 60 #define FRAGMENT_ID_EXPIRE_TIME 10 #define FRAGMENT_PTR_EXPIRE_TIME 30 /* TCP link expire time for different cases */ /* When the link has been used and closed - minimal grace time to allow ACKs and potential re-connect in FTP (XXX - is this allowed?) */ #ifndef TCP_EXPIRE_DEAD # define TCP_EXPIRE_DEAD 10 #endif /* When the link has been used and closed on one side - the other side is allowed to still send data */ #ifndef TCP_EXPIRE_SINGLEDEAD # define TCP_EXPIRE_SINGLEDEAD 90 #endif /* When the link isn't yet up */ #ifndef TCP_EXPIRE_INITIAL # define TCP_EXPIRE_INITIAL 300 #endif /* When the link is up */ #ifndef TCP_EXPIRE_CONNECTED # define TCP_EXPIRE_CONNECTED 86400 #endif /* Dummy port number codes used for FindLinkIn/Out() and AddLink(). These constants can be anything except zero, which indicates an unknown port number. */ #define NO_DEST_PORT 1 #define NO_SRC_PORT 1 /* Data Structures The fundamental data structure used in this program is "struct alias_link". Whenever a TCP connection is made, a UDP datagram is sent out, or an ICMP echo request is made, a link record is made (if it has not already been created). The link record is identified by the source address/port and the destination address/port. In the case of an ICMP echo request, the source port is treated as being equivalent with the 16-bit id number of the ICMP packet. The link record also can store some auxiliary data. For TCP connections that have had sequence and acknowledgment modifications, data space is available to track these changes. A state field is used to keep track in changes to the tcp connection state. Id numbers of fragments can also be stored in the auxiliary space. Pointers to unresolved framgents can also be stored. The link records support two independent chainings. Lookup tables for input and out tables hold the initial pointers the link chains. On input, the lookup table indexes on alias port and link type. On output, the lookup table indexes on source addreess, destination address, source port, destination port and link type. */ struct ack_data_record /* used to save changes to ack/seq numbers */ { u_long ack_old; u_long ack_new; int delta; int active; }; struct tcp_state /* Information about tcp connection */ { int in; /* State for outside -> inside */ int out; /* State for inside -> outside */ int index; /* Index to ack data array */ int ack_modified; /* Indicates whether ack and seq numbers */ /* been modified */ }; #define N_LINK_TCP_DATA 3 /* Number of distinct ack number changes saved for a modified TCP stream */ struct tcp_dat { struct tcp_state state; struct ack_data_record ack[N_LINK_TCP_DATA]; int fwhole; /* Which firewall record is used for this hole? */ }; struct alias_link /* Main data structure */ { struct in_addr src_addr; /* Address and port information */ struct in_addr dst_addr; struct in_addr alias_addr; struct in_addr proxy_addr; u_short src_port; u_short dst_port; u_short alias_port; u_short proxy_port; int link_type; /* Type of link: tcp, udp, icmp, frag */ /* values for link_type */ #define LINK_ICMP 1 #define LINK_UDP 2 #define LINK_TCP 3 #define LINK_FRAGMENT_ID 4 #define LINK_FRAGMENT_PTR 5 #define LINK_ADDR 6 int flags; /* indicates special characteristics */ /* flag bits */ #define LINK_UNKNOWN_DEST_PORT 0x01 #define LINK_UNKNOWN_DEST_ADDR 0x02 #define LINK_PERMANENT 0x04 #define LINK_PARTIALLY_SPECIFIED 0x03 /* logical-or of first two bits */ #define LINK_UNFIREWALLED 0x08 int timestamp; /* Time link was last accessed */ int expire_time; /* Expire time for link */ int sockfd; /* socket descriptor */ u_int start_point_out; /* Index number in output lookup table */ u_int start_point_in; struct alias_link *next_out; /* Linked list pointers for input and */ struct alias_link *last_out; /* output tables */ struct alias_link *next_in; /* . */ struct alias_link *last_in; /* . */ union /* Auxiliary data */ { char *frag_ptr; struct in_addr frag_addr; struct tcp_dat *tcp; } data; }; /* Global Variables The global variables listed here are only accessed from within alias_db.c and so are prefixed with the static designation. */ int packetAliasMode; /* Mode flags */ /* - documented in alias.h */ static struct in_addr aliasAddress; /* Address written onto source */ /* field of IP packet. */ static struct in_addr targetAddress; /* IP address incoming packets */ /* are sent to if no aliasing */ /* link already exists */ static struct in_addr nullAddress; /* Used as a dummy parameter for */ /* some function calls */ static struct alias_link * linkTableOut[LINK_TABLE_OUT_SIZE]; /* Lookup table of pointers to */ /* chains of link records. Each */ static struct alias_link * /* link record is doubly indexed */ linkTableIn[LINK_TABLE_IN_SIZE]; /* into input and output lookup */ /* tables. */ static int icmpLinkCount; /* Link statistics */ static int udpLinkCount; static int tcpLinkCount; static int fragmentIdLinkCount; static int fragmentPtrLinkCount; static int sockCount; static int cleanupIndex; /* Index to chain of link table */ /* being inspected for old links */ static int timeStamp; /* System time in seconds for */ /* current packet */ static int lastCleanupTime; /* Last time IncrementalCleanup() */ /* was called */ static int houseKeepingResidual; /* used by HouseKeeping() */ static int deleteAllLinks; /* If equal to zero, DeleteLink() */ /* will not remove permanent links */ static FILE *monitorFile; /* File descriptor for link */ /* statistics monitoring file */ static int newDefaultLink; /* Indicates if a new aliasing */ /* link has been created after a */ /* call to PacketAliasIn/Out(). */ #ifndef NO_FW_PUNCH static int fireWallFD = -1; /* File descriptor to be able to */ /* control firewall. Opened by */ /* PacketAliasSetMode on first */ /* setting the PKT_ALIAS_PUNCH_FW */ /* flag. */ #endif static int pptpAliasFlag; /* Indicates if PPTP aliasing is */ /* on or off */ static struct in_addr pptpAliasAddr; /* Address of source of PPTP */ /* packets. */ /* Internal utility routines (used only in alias_db.c) Lookup table starting points: StartPointIn() -- link table initial search point for outgoing packets StartPointOut() -- port table initial search point for incoming packets Miscellaneous: SeqDiff() -- difference between two TCP sequences ShowAliasStats() -- send alias statistics to a monitor file */ /* Local prototypes */ static u_int StartPointIn(struct in_addr, u_short, int); static u_int StartPointOut(struct in_addr, struct in_addr, u_short, u_short, int); static int SeqDiff(u_long, u_long); static void ShowAliasStats(void); #ifndef NO_FW_PUNCH /* Firewall control */ static void InitPunchFW(void); static void UninitPunchFW(void); static void ClearFWHole(struct alias_link *link); #endif /* Log file control */ static void InitPacketAliasLog(void); static void UninitPacketAliasLog(void); static u_int StartPointIn(struct in_addr alias_addr, u_short alias_port, int link_type) { u_int n; n = alias_addr.s_addr; n += alias_port; n += link_type; return(n % LINK_TABLE_IN_SIZE); } static u_int StartPointOut(struct in_addr src_addr, struct in_addr dst_addr, u_short src_port, u_short dst_port, int link_type) { u_int n; n = src_addr.s_addr; n += dst_addr.s_addr; n += src_port; n += dst_port; n += link_type; return(n % LINK_TABLE_OUT_SIZE); } static int SeqDiff(u_long x, u_long y) { /* Return the difference between two TCP sequence numbers */ /* This function is encapsulated in case there are any unusual arithmetic conditions that need to be considered. */ return (ntohl(y) - ntohl(x)); } static void ShowAliasStats(void) { /* Used for debugging */ if (monitorFile) { fprintf(monitorFile, "icmp=%d, udp=%d, tcp=%d, frag_id=%d frag_ptr=%d", icmpLinkCount, udpLinkCount, tcpLinkCount, fragmentIdLinkCount, fragmentPtrLinkCount); fprintf(monitorFile, " / tot=%d (sock=%d)\n", icmpLinkCount + udpLinkCount + tcpLinkCount + fragmentIdLinkCount + fragmentPtrLinkCount, sockCount); fflush(monitorFile); } } /* Internal routines for finding, deleting and adding links Port Allocation: GetNewPort() -- find and reserve new alias port number GetSocket() -- try to allocate a socket for a given port Link creation and deletion: CleanupAliasData() - remove all link chains from lookup table IncrementalCleanup() - look for stale links in a single chain DeleteLink() - remove link AddLink() - add link ReLink() - change link Link search: FindLinkOut() - find link for outgoing packets FindLinkIn() - find link for incoming packets */ /* Local prototypes */ static int GetNewPort(struct alias_link *, int); static u_short GetSocket(u_short, int *, int); static void CleanupAliasData(void); static void IncrementalCleanup(void); static void DeleteLink(struct alias_link *); static struct alias_link * AddLink(struct in_addr, struct in_addr, struct in_addr, u_short, u_short, int, int); static struct alias_link * ReLink(struct alias_link *, struct in_addr, struct in_addr, struct in_addr, u_short, u_short, int, int); static struct alias_link * FindLinkOut(struct in_addr, struct in_addr, u_short, u_short, int); static struct alias_link * FindLinkIn(struct in_addr, struct in_addr, u_short, u_short, int, int); #define ALIAS_PORT_BASE 0x08000 #define ALIAS_PORT_MASK 0x07fff #define GET_NEW_PORT_MAX_ATTEMPTS 20 #define GET_ALIAS_PORT -1 #define GET_ALIAS_ID GET_ALIAS_PORT /* GetNewPort() allocates port numbers. Note that if a port number is already in use, that does not mean that it cannot be used by another link concurrently. This is because GetNewPort() looks for unused triplets: (dest addr, dest port, alias port). */ static int GetNewPort(struct alias_link *link, int alias_port_param) { int i; int max_trials; u_short port_sys; u_short port_net; /* Description of alias_port_param for GetNewPort(). When this parameter is zero or positive, it precisely specifies the port number. GetNewPort() will return this number without check that it is in use. Whis this parameter is -1, it indicates to get a randomly selected port number. */ if (alias_port_param == GET_ALIAS_PORT) { /* * The aliasing port is automatically selected * by one of two methods below: */ max_trials = GET_NEW_PORT_MAX_ATTEMPTS; if (packetAliasMode & PKT_ALIAS_SAME_PORTS) { /* * When the ALIAS_SAME_PORTS option is * chosen, the first try will be the * actual source port. If this is already * in use, the remainder of the trials * will be random. */ port_net = link->src_port; port_sys = ntohs(port_net); } else { /* First trial and all subsequent are random. */ port_sys = random() & ALIAS_PORT_MASK; port_sys += ALIAS_PORT_BASE; port_net = htons(port_sys); } } else if (alias_port_param >= 0 && alias_port_param < 0x10000) { link->alias_port = (u_short) alias_port_param; return(0); } else { +#ifdef DEBUG fprintf(stderr, "PacketAlias/GetNewPort(): "); fprintf(stderr, "input parameter error\n"); +#endif return(-1); } /* Port number search */ for (i=0; idst_addr, link->alias_addr, link->dst_port, port_net, link->link_type, 0); if (search_result == NULL) go_ahead = 1; else if (!(link->flags & LINK_PARTIALLY_SPECIFIED) && (search_result->flags & LINK_PARTIALLY_SPECIFIED)) go_ahead = 1; else go_ahead = 0; if (go_ahead) { if ((packetAliasMode && PKT_ALIAS_USE_SOCKETS) && (link->flags & LINK_PARTIALLY_SPECIFIED)) { if (GetSocket(port_net, &link->sockfd, link->link_type)) { link->alias_port = port_net; return(0); } } else { link->alias_port = port_net; return(0); } } port_sys = random() & ALIAS_PORT_MASK; port_sys += ALIAS_PORT_BASE; port_net = htons(port_sys); } +#ifdef DEBUG fprintf(stderr, "PacketAlias/GetnewPort(): "); fprintf(stderr, "could not find free port\n"); +#endif return(-1); } static u_short GetSocket(u_short port_net, int *sockfd, int link_type) { int err; int sock; struct sockaddr_in sock_addr; if (link_type == LINK_TCP) sock = socket(AF_INET, SOCK_STREAM, 0); else if (link_type == LINK_UDP) sock = socket(AF_INET, SOCK_DGRAM, 0); else { +#ifdef DEBUG fprintf(stderr, "PacketAlias/GetSocket(): "); fprintf(stderr, "incorrect link type\n"); +#endif return(0); } if (sock < 0) { +#ifdef DEBUG fprintf(stderr, "PacketAlias/GetSocket(): "); fprintf(stderr, "socket() error %d\n", *sockfd); +#endif return(0); } sock_addr.sin_family = AF_INET; sock_addr.sin_addr.s_addr = htonl(INADDR_ANY); sock_addr.sin_port = port_net; err = bind(sock, (struct sockaddr *) &sock_addr, sizeof(sock_addr)); if (err == 0) { sockCount++; *sockfd = sock; return(1); } else { close(sock); return(0); } } static void CleanupAliasData(void) { struct alias_link *link; int i, icount; icount = 0; for (i=0; inext_out; icount++; DeleteLink(link); link = link_next; } } cleanupIndex =0; } static void IncrementalCleanup(void) { int icount; struct alias_link *link; icount = 0; link = linkTableOut[cleanupIndex++]; while (link != NULL) { int idelta; struct alias_link *link_next; link_next = link->next_out; idelta = timeStamp - link->timestamp; switch (link->link_type) { case LINK_ICMP: case LINK_UDP: case LINK_FRAGMENT_ID: case LINK_FRAGMENT_PTR: if (idelta > link->expire_time) { DeleteLink(link); icount++; } break; case LINK_TCP: if (idelta > link->expire_time) { struct tcp_dat *tcp_aux; tcp_aux = link->data.tcp; if (tcp_aux->state.in != ALIAS_TCP_STATE_CONNECTED || tcp_aux->state.out != ALIAS_TCP_STATE_CONNECTED) { DeleteLink(link); icount++; } } break; } link = link_next; } if (cleanupIndex == LINK_TABLE_OUT_SIZE) cleanupIndex = 0; } void DeleteLink(struct alias_link *link) { struct alias_link *link_last; struct alias_link *link_next; /* Don't do anything if the link is marked permanent */ if (deleteAllLinks == 0 && link->flags & LINK_PERMANENT) return; #ifndef NO_FW_PUNCH /* Delete associatied firewall hole, if any */ ClearFWHole(link); #endif /* Adjust output table pointers */ link_last = link->last_out; link_next = link->next_out; if (link_last != NULL) link_last->next_out = link_next; else linkTableOut[link->start_point_out] = link_next; if (link_next != NULL) link_next->last_out = link_last; /* Adjust input table pointers */ link_last = link->last_in; link_next = link->next_in; if (link_last != NULL) link_last->next_in = link_next; else linkTableIn[link->start_point_in] = link_next; if (link_next != NULL) link_next->last_in = link_last; /* Close socket, if one has been allocated */ if (link->sockfd != -1) { sockCount--; close(link->sockfd); } /* Link-type dependent cleanup */ switch(link->link_type) { case LINK_ICMP: icmpLinkCount--; break; case LINK_UDP: udpLinkCount--; break; case LINK_TCP: tcpLinkCount--; if (link->data.tcp != NULL) free(link->data.tcp); break; case LINK_FRAGMENT_ID: fragmentIdLinkCount--; break; case LINK_FRAGMENT_PTR: fragmentPtrLinkCount--; if (link->data.frag_ptr != NULL) free(link->data.frag_ptr); break; } /* Free memory */ free(link); /* Write statistics, if logging enabled */ if (packetAliasMode & PKT_ALIAS_LOG) { ShowAliasStats(); } } static struct alias_link * AddLink(struct in_addr src_addr, struct in_addr dst_addr, struct in_addr alias_addr, u_short src_port, u_short dst_port, int alias_port_param, /* if less than zero, alias */ int link_type) /* port will be automatically */ { /* chosen. If greater than */ u_int start_point; /* zero, equal to alias port */ struct alias_link *link; struct alias_link *first_link; link = malloc(sizeof(struct alias_link)); if (link != NULL) { /* If either the aliasing address or source address are equal to the default device address (equal to the global variable aliasAddress), then set the alias address field of the link record to zero */ if (src_addr.s_addr == aliasAddress.s_addr) src_addr.s_addr = 0; if (alias_addr.s_addr == aliasAddress.s_addr) alias_addr.s_addr = 0; /* Basic initialization */ link->src_addr = src_addr; link->dst_addr = dst_addr; link->alias_addr = alias_addr; link->proxy_addr.s_addr = 0; link->src_port = src_port; link->dst_port = dst_port; link->proxy_port = 0; link->link_type = link_type; link->sockfd = -1; link->flags = 0; link->timestamp = timeStamp; /* Expiration time */ switch (link_type) { case LINK_ICMP: link->expire_time = ICMP_EXPIRE_TIME; break; case LINK_UDP: link->expire_time = UDP_EXPIRE_TIME; break; case LINK_TCP: link->expire_time = TCP_EXPIRE_INITIAL; break; case LINK_FRAGMENT_ID: link->expire_time = FRAGMENT_ID_EXPIRE_TIME; break; case LINK_FRAGMENT_PTR: link->expire_time = FRAGMENT_PTR_EXPIRE_TIME; break; } /* Determine alias flags */ if (dst_addr.s_addr == 0) link->flags |= LINK_UNKNOWN_DEST_ADDR; if (dst_port == 0) link->flags |= LINK_UNKNOWN_DEST_PORT; /* Determine alias port */ if (GetNewPort(link, alias_port_param) != 0) { free(link); return(NULL); } /* Set up pointers for output lookup table */ start_point = StartPointOut(src_addr, dst_addr, src_port, dst_port, link_type); first_link = linkTableOut[start_point]; link->last_out = NULL; link->next_out = first_link; link->start_point_out = start_point; if (first_link != NULL) first_link->last_out = link; linkTableOut[start_point] = link; /* Set up pointers for input lookup table */ start_point = StartPointIn(alias_addr, link->alias_port, link_type); first_link = linkTableIn[start_point]; link->last_in = NULL; link->next_in = first_link; link->start_point_in = start_point; if (first_link != NULL) first_link->last_in = link; linkTableIn[start_point] = link; /* Link-type dependent initialization */ switch(link_type) { struct tcp_dat *aux_tcp; case LINK_ICMP: icmpLinkCount++; break; case LINK_UDP: udpLinkCount++; break; case LINK_TCP: aux_tcp = malloc(sizeof(struct tcp_dat)); link->data.tcp = aux_tcp; if (aux_tcp != NULL) { int i; tcpLinkCount++; aux_tcp->state.in = ALIAS_TCP_STATE_NOT_CONNECTED; aux_tcp->state.out = ALIAS_TCP_STATE_NOT_CONNECTED; aux_tcp->state.index = 0; aux_tcp->state.ack_modified = 0; for (i=0; iack[i].active = 0; aux_tcp->fwhole = -1; } else { +#ifdef DEBUG fprintf(stderr, "PacketAlias/AddLink: "); fprintf(stderr, " cannot allocate auxiliary TCP data\n"); +#endif } break; case LINK_FRAGMENT_ID: fragmentIdLinkCount++; break; case LINK_FRAGMENT_PTR: fragmentPtrLinkCount++; break; } } else { +#ifdef DEBUG fprintf(stderr, "PacketAlias/AddLink(): "); fprintf(stderr, "malloc() call failed.\n"); +#endif } if (packetAliasMode & PKT_ALIAS_LOG) { ShowAliasStats(); } return(link); } static struct alias_link * ReLink(struct alias_link *old_link, struct in_addr src_addr, struct in_addr dst_addr, struct in_addr alias_addr, u_short src_port, u_short dst_port, int alias_port_param, /* if less than zero, alias */ int link_type) /* port will be automatically */ { /* chosen. If greater than */ struct alias_link *new_link; /* zero, equal to alias port */ new_link = AddLink(src_addr, dst_addr, alias_addr, src_port, dst_port, alias_port_param, link_type); #ifndef NO_FW_PUNCH if (new_link != NULL && old_link->link_type == LINK_TCP && old_link->data.tcp && old_link->data.tcp->fwhole > 0) { PunchFWHole(new_link); } #endif DeleteLink(old_link); return new_link; } static struct alias_link * FindLinkOut(struct in_addr src_addr, struct in_addr dst_addr, u_short src_port, u_short dst_port, int link_type) { u_int i; struct alias_link *link; if (src_addr.s_addr == aliasAddress.s_addr) src_addr.s_addr = 0; i = StartPointOut(src_addr, dst_addr, src_port, dst_port, link_type); link = linkTableOut[i]; while (link != NULL) { if (link->src_addr.s_addr == src_addr.s_addr && link->dst_addr.s_addr == dst_addr.s_addr && link->dst_port == dst_port && link->src_port == src_port && link->link_type == link_type) { link->timestamp = timeStamp; break; } link = link->next_out; } return(link); } struct alias_link * FindLinkIn(struct in_addr dst_addr, struct in_addr alias_addr, u_short dst_port, u_short alias_port, int link_type, int replace_partial_links) { int flags_in; u_int start_point; struct alias_link *link; struct alias_link *link_fully_specified; struct alias_link *link_unknown_all; struct alias_link *link_unknown_dst_addr; struct alias_link *link_unknown_dst_port; /* Initialize pointers */ link_fully_specified = NULL; link_unknown_all = NULL; link_unknown_dst_addr = NULL; link_unknown_dst_port = NULL; /* If either the dest addr or port is unknown, the search loop will have to know about this. */ flags_in = 0; if (dst_addr.s_addr == 0) flags_in |= LINK_UNKNOWN_DEST_ADDR; if (dst_port == 0) flags_in |= LINK_UNKNOWN_DEST_PORT; /* The following allows permanent links to be be specified as using the default aliasing address (i.e. device interface address) without knowing in advance what that address is. */ if (alias_addr.s_addr == aliasAddress.s_addr) alias_addr.s_addr = 0; /* Search loop */ start_point = StartPointIn(alias_addr, alias_port, link_type); link = linkTableIn[start_point]; while (link != NULL) { int flags; flags = flags_in | link->flags; if (!(flags & LINK_PARTIALLY_SPECIFIED)) { if (link->alias_addr.s_addr == alias_addr.s_addr && link->alias_port == alias_port && link->dst_addr.s_addr == dst_addr.s_addr && link->dst_port == dst_port && link->link_type == link_type) { link_fully_specified = link; break; } } else if ((flags & LINK_UNKNOWN_DEST_ADDR) && (flags & LINK_UNKNOWN_DEST_PORT)) { if (link->alias_addr.s_addr == alias_addr.s_addr && link->alias_port == alias_port && link->link_type == link_type) { if (link_unknown_all == NULL) link_unknown_all = link; } } else if (flags & LINK_UNKNOWN_DEST_ADDR) { if (link->alias_addr.s_addr == alias_addr.s_addr && link->alias_port == alias_port && link->link_type == link_type && link->dst_port == dst_port) { if (link_unknown_dst_addr == NULL) link_unknown_dst_addr = link; } } else if (flags & LINK_UNKNOWN_DEST_PORT) { if (link->alias_addr.s_addr == alias_addr.s_addr && link->alias_port == alias_port && link->link_type == link_type && link->dst_addr.s_addr == dst_addr.s_addr) { if (link_unknown_dst_port == NULL) link_unknown_dst_port = link; } } link = link->next_in; } if (link_fully_specified != NULL) { return link_fully_specified; } else if (link_unknown_dst_port != NULL) { return replace_partial_links ? ReLink(link_unknown_dst_port, link_unknown_dst_port->src_addr, dst_addr, alias_addr, link_unknown_dst_port->src_port, dst_port, alias_port, link_type) : link_unknown_dst_port; } else if (link_unknown_dst_addr != NULL) { return replace_partial_links ? ReLink(link_unknown_dst_addr, link_unknown_dst_addr->src_addr, dst_addr, alias_addr, link_unknown_dst_addr->src_port, dst_port, alias_port, link_type) : link_unknown_dst_addr; } else if (link_unknown_all != NULL) { return replace_partial_links ? ReLink(link_unknown_all, link_unknown_all->src_addr, dst_addr, alias_addr, link_unknown_all->src_port, dst_port, alias_port, link_type) : link_unknown_all; } else { return(NULL); } } /* External routines for finding/adding links -- "external" means outside alias_db.c, but within alias*.c -- FindIcmpIn(), FindIcmpOut() FindFragmentIn1(), FindFragmentIn2() AddFragmentPtrLink(), FindFragmentPtr() FindUdpTcpIn(), FindUdpTcpOut() FindOriginalAddress(), FindAliasAddress() (prototypes in alias_local.h) */ struct alias_link * FindIcmpIn(struct in_addr dst_addr, struct in_addr alias_addr, u_short id_alias) { return FindLinkIn(dst_addr, alias_addr, NO_DEST_PORT, id_alias, LINK_ICMP, 0); } struct alias_link * FindIcmpOut(struct in_addr src_addr, struct in_addr dst_addr, u_short id) { struct alias_link * link; link = FindLinkOut(src_addr, dst_addr, id, NO_DEST_PORT, LINK_ICMP); if (link == NULL) { struct in_addr alias_addr; alias_addr = FindAliasAddress(src_addr); link = AddLink(src_addr, dst_addr, alias_addr, id, NO_DEST_PORT, GET_ALIAS_ID, LINK_ICMP); } return(link); } struct alias_link * FindFragmentIn1(struct in_addr dst_addr, struct in_addr alias_addr, u_short ip_id) { struct alias_link *link; link = FindLinkIn(dst_addr, alias_addr, NO_DEST_PORT, ip_id, LINK_FRAGMENT_ID, 0); if (link == NULL) { link = AddLink(nullAddress, dst_addr, alias_addr, NO_SRC_PORT, NO_DEST_PORT, ip_id, LINK_FRAGMENT_ID); } return(link); } struct alias_link * FindFragmentIn2(struct in_addr dst_addr, /* Doesn't add a link if one */ struct in_addr alias_addr, /* is not found. */ u_short ip_id) { return FindLinkIn(dst_addr, alias_addr, NO_DEST_PORT, ip_id, LINK_FRAGMENT_ID, 0); } struct alias_link * AddFragmentPtrLink(struct in_addr dst_addr, u_short ip_id) { return AddLink(nullAddress, dst_addr, nullAddress, NO_SRC_PORT, NO_DEST_PORT, ip_id, LINK_FRAGMENT_PTR); } struct alias_link * FindFragmentPtr(struct in_addr dst_addr, u_short ip_id) { return FindLinkIn(dst_addr, nullAddress, NO_DEST_PORT, ip_id, LINK_FRAGMENT_PTR, 0); } struct alias_link * FindUdpTcpIn(struct in_addr dst_addr, struct in_addr alias_addr, u_short dst_port, u_short alias_port, u_char proto) { int link_type; struct alias_link *link; switch (proto) { case IPPROTO_UDP: link_type = LINK_UDP; break; case IPPROTO_TCP: link_type = LINK_TCP; break; default: return NULL; break; } link = FindLinkIn(dst_addr, alias_addr, dst_port, alias_port, link_type, 1); if (!(packetAliasMode & PKT_ALIAS_DENY_INCOMING) && !(packetAliasMode & PKT_ALIAS_PROXY_ONLY) && link == NULL) { struct in_addr target_addr; target_addr = FindOriginalAddress(alias_addr); link = AddLink(target_addr, dst_addr, alias_addr, alias_port, dst_port, alias_port, link_type); } return(link); } struct alias_link * FindUdpTcpOut(struct in_addr src_addr, struct in_addr dst_addr, u_short src_port, u_short dst_port, u_char proto) { int link_type; struct alias_link *link; switch (proto) { case IPPROTO_UDP: link_type = LINK_UDP; break; case IPPROTO_TCP: link_type = LINK_TCP; break; default: return NULL; break; } link = FindLinkOut(src_addr, dst_addr, src_port, dst_port, link_type); if (link == NULL) { struct in_addr alias_addr; alias_addr = FindAliasAddress(src_addr); link = AddLink(src_addr, dst_addr, alias_addr, src_port, dst_port, GET_ALIAS_PORT, link_type); } return(link); } struct in_addr FindOriginalAddress(struct in_addr alias_addr) { struct alias_link *link; link = FindLinkIn(nullAddress, alias_addr, 0, 0, LINK_ADDR, 0); if (link == NULL) { newDefaultLink = 1; if (targetAddress.s_addr != 0) return targetAddress; else return alias_addr; } else { if (link->src_addr.s_addr == 0) return aliasAddress; else return link->src_addr; } } struct in_addr FindAliasAddress(struct in_addr original_addr) { struct alias_link *link; link = FindLinkOut(original_addr, nullAddress, 0, 0, LINK_ADDR); if (link == NULL) { return aliasAddress; } else { if (link->alias_addr.s_addr == 0) return aliasAddress; else return link->alias_addr; } } /* External routines for getting or changing link data (external to alias_db.c, but internal to alias*.c) SetFragmentData(), GetFragmentData() SetFragmentPtr(), GetFragmentPtr() SetStateIn(), SetStateOut(), GetStateIn(), GetStateOut() GetOriginalAddress(), GetDestAddress(), GetAliasAddress() GetOriginalPort(), GetAliasPort() SetAckModified(), GetAckModified() GetDeltaAckIn(), GetDeltaSeqOut(), AddSeq() */ void SetFragmentAddr(struct alias_link *link, struct in_addr src_addr) { link->data.frag_addr = src_addr; } void GetFragmentAddr(struct alias_link *link, struct in_addr *src_addr) { *src_addr = link->data.frag_addr; } void SetFragmentPtr(struct alias_link *link, char *fptr) { link->data.frag_ptr = fptr; } void GetFragmentPtr(struct alias_link *link, char **fptr) { *fptr = link->data.frag_ptr; } void SetStateIn(struct alias_link *link, int state) { /* TCP input state */ switch (state) { case ALIAS_TCP_STATE_DISCONNECTED: if (link->data.tcp->state.out != ALIAS_TCP_STATE_CONNECTED) { link->expire_time = TCP_EXPIRE_DEAD; } else { link->expire_time = TCP_EXPIRE_SINGLEDEAD; } link->data.tcp->state.in = state; break; case ALIAS_TCP_STATE_CONNECTED: link->expire_time = TCP_EXPIRE_CONNECTED; /*FALLTHROUGH*/ case ALIAS_TCP_STATE_NOT_CONNECTED: link->data.tcp->state.in = state; break; default: abort(); } } void SetStateOut(struct alias_link *link, int state) { /* TCP output state */ switch (state) { case ALIAS_TCP_STATE_DISCONNECTED: if (link->data.tcp->state.in != ALIAS_TCP_STATE_CONNECTED) { link->expire_time = TCP_EXPIRE_DEAD; } else { link->expire_time = TCP_EXPIRE_SINGLEDEAD; } link->data.tcp->state.out = state; break; case ALIAS_TCP_STATE_CONNECTED: link->expire_time = TCP_EXPIRE_CONNECTED; /*FALLTHROUGH*/ case ALIAS_TCP_STATE_NOT_CONNECTED: link->data.tcp->state.out = state; break; default: abort(); } } int GetStateIn(struct alias_link *link) { /* TCP input state */ return link->data.tcp->state.in; } int GetStateOut(struct alias_link *link) { /* TCP output state */ return link->data.tcp->state.out; } struct in_addr GetOriginalAddress(struct alias_link *link) { if (link->src_addr.s_addr == 0) return aliasAddress; else return(link->src_addr); } struct in_addr GetDestAddress(struct alias_link *link) { return(link->dst_addr); } struct in_addr GetAliasAddress(struct alias_link *link) { if (link->alias_addr.s_addr == 0) return aliasAddress; else return link->alias_addr; } struct in_addr GetDefaultAliasAddress() { return aliasAddress; } void SetDefaultAliasAddress(struct in_addr alias_addr) { aliasAddress = alias_addr; } u_short GetOriginalPort(struct alias_link *link) { return(link->src_port); } u_short GetAliasPort(struct alias_link *link) { return(link->alias_port); } u_short GetDestPort(struct alias_link *link) { return(link->dst_port); } void SetAckModified(struct alias_link *link) { /* Indicate that ack numbers have been modified in a TCP connection */ link->data.tcp->state.ack_modified = 1; } struct in_addr GetProxyAddress(struct alias_link *link) { return link->proxy_addr; } void SetProxyAddress(struct alias_link *link, struct in_addr addr) { link->proxy_addr = addr; } u_short GetProxyPort(struct alias_link *link) { return link->proxy_port; } void SetProxyPort(struct alias_link *link, u_short port) { link->proxy_port = port; } int GetAckModified(struct alias_link *link) { /* See if ack numbers have been modified */ return link->data.tcp->state.ack_modified; } int GetDeltaAckIn(struct ip *pip, struct alias_link *link) { /* Find out how much the ack number has been altered for an incoming TCP packet. To do this, a circular list is ack numbers where the TCP packet size was altered is searched. */ int i; struct tcphdr *tc; int delta, ack_diff_min; u_long ack; tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); ack = tc->th_ack; delta = 0; ack_diff_min = -1; for (i=0; idata.tcp->ack[i]; if (x.active == 1) { int ack_diff; ack_diff = SeqDiff(x.ack_new, ack); if (ack_diff >= 0) { if (ack_diff_min >= 0) { if (ack_diff < ack_diff_min) { delta = x.delta; ack_diff_min = ack_diff; } } else { delta = x.delta; ack_diff_min = ack_diff; } } } } return (delta); } int GetDeltaSeqOut(struct ip *pip, struct alias_link *link) { /* Find out how much the seq number has been altered for an outgoing TCP packet. To do this, a circular list is ack numbers where the TCP packet size was altered is searched. */ int i; struct tcphdr *tc; int delta, seq_diff_min; u_long seq; tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); seq = tc->th_seq; delta = 0; seq_diff_min = -1; for (i=0; idata.tcp->ack[i]; if (x.active == 1) { int seq_diff; seq_diff = SeqDiff(x.ack_old, seq); if (seq_diff >= 0) { if (seq_diff_min >= 0) { if (seq_diff < seq_diff_min) { delta = x.delta; seq_diff_min = seq_diff; } } else { delta = x.delta; seq_diff_min = seq_diff; } } } } return (delta); } void AddSeq(struct ip *pip, struct alias_link *link, int delta) { /* When a TCP packet has been altered in length, save this information in a circular list. If enough packets have been altered, then this list will begin to overwrite itself. */ struct tcphdr *tc; struct ack_data_record x; int hlen, tlen, dlen; int i; tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); hlen = (pip->ip_hl + tc->th_off) << 2; tlen = ntohs(pip->ip_len); dlen = tlen - hlen; x.ack_old = htonl(ntohl(tc->th_seq) + dlen); x.ack_new = htonl(ntohl(tc->th_seq) + dlen + delta); x.delta = delta; x.active = 1; i = link->data.tcp->state.index; link->data.tcp->ack[i] = x; i++; if (i == N_LINK_TCP_DATA) link->data.tcp->state.index = 0; else link->data.tcp->state.index = i; } void SetExpire(struct alias_link *link, int expire) { if (expire == 0) { link->flags &= ~LINK_PERMANENT; DeleteLink(link); } else if (expire == -1) { link->flags |= LINK_PERMANENT; } else if (expire > 0) { link->expire_time = expire; } else { +#ifdef DEBUG fprintf(stderr, "PacketAlias/SetExpire(): "); fprintf(stderr, "error in expire parameter\n"); +#endif } } void ClearCheckNewLink(void) { newDefaultLink = 0; } /* Miscellaneous Functions HouseKeeping() InitPacketAliasLog() UninitPacketAliasLog() */ /* Whenever an outgoing or incoming packet is handled, HouseKeeping() is called to find and remove timed-out aliasing links. Logic exists to sweep through the entire table and linked list structure every 60 seconds. (prototype in alias_local.h) */ void HouseKeeping(void) { int i, n, n100; struct timeval tv; struct timezone tz; /* * Save system time (seconds) in global variable timeStamp for * use by other functions. This is done so as not to unnecessarily * waste timeline by making system calls. */ gettimeofday(&tv, &tz); timeStamp = tv.tv_sec; /* Compute number of spokes (output table link chains) to cover */ n100 = LINK_TABLE_OUT_SIZE * 100 + houseKeepingResidual; n100 *= timeStamp - lastCleanupTime; n100 /= ALIAS_CLEANUP_INTERVAL_SECS; n = n100/100; /* Handle different cases */ if (n > ALIAS_CLEANUP_MAX_SPOKES) { n = ALIAS_CLEANUP_MAX_SPOKES; lastCleanupTime = timeStamp; houseKeepingResidual = 0; for (i=0; i 0) { lastCleanupTime = timeStamp; houseKeepingResidual = n100 - 100*n; for (i=0; iflags |= LINK_PERMANENT; } +#ifdef DEBUG else { fprintf(stderr, "PacketAliasRedirectPort(): " "call to AddLink() failed\n"); } +#endif return link; } /* Translate PPTP packets to a machine on the inside */ int PacketAliasPptp(struct in_addr src_addr) { pptpAliasAddr = src_addr; /* Address of the inside PPTP machine */ pptpAliasFlag = src_addr.s_addr != INADDR_NONE; return 1; } int GetPptpAlias (struct in_addr* alias_addr) { if (pptpAliasFlag) *alias_addr = pptpAliasAddr; return pptpAliasFlag; } /* Static address translation */ struct alias_link * PacketAliasRedirectAddr(struct in_addr src_addr, struct in_addr alias_addr) { struct alias_link *link; link = AddLink(src_addr, nullAddress, alias_addr, 0, 0, 0, LINK_ADDR); if (link != NULL) { link->flags |= LINK_PERMANENT; } +#ifdef DEBUG else { fprintf(stderr, "PacketAliasRedirectAddr(): " "call to AddLink() failed\n"); } +#endif return link; } void PacketAliasRedirectDelete(struct alias_link *link) { /* This is a dangerous function to put in the API, because an invalid pointer can crash the program. */ deleteAllLinks = 1; DeleteLink(link); deleteAllLinks = 0; } void PacketAliasSetAddress(struct in_addr addr) { if (packetAliasMode & PKT_ALIAS_RESET_ON_ADDR_CHANGE && aliasAddress.s_addr != addr.s_addr) CleanupAliasData(); aliasAddress = addr; } void PacketAliasSetTarget(struct in_addr target_addr) { targetAddress = target_addr; } void PacketAliasInit(void) { int i; struct timeval tv; struct timezone tz; static int firstCall = 1; if (firstCall == 1) { gettimeofday(&tv, &tz); timeStamp = tv.tv_sec; lastCleanupTime = tv.tv_sec; houseKeepingResidual = 0; for (i=0; i #include #include #include #include static void ClearAllFWHoles(void); static int fireWallBaseNum; /* The first firewall entry free for our use */ static int fireWallNumNums; /* How many entries can we use? */ static int fireWallActiveNum; /* Which entry did we last use? */ static char *fireWallField; /* bool array for entries */ #define fw_setfield(field, num) \ do { \ (field)[num] = 1; \ } /*lint -save -e717 */ while(0) /*lint -restore */ #define fw_clrfield(field, num) \ do { \ (field)[num] = 0; \ } /*lint -save -e717 */ while(0) /*lint -restore */ #define fw_tstfield(field, num) ((field)[num]) void PacketAliasSetFWBase(unsigned int base, unsigned int num) { fireWallBaseNum = base; fireWallNumNums = num; } static void InitPunchFW(void) { fireWallField = malloc(fireWallNumNums); if (fireWallField) { memset(fireWallField, 0, fireWallNumNums); if (fireWallFD < 0) { fireWallFD = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); } ClearAllFWHoles(); fireWallActiveNum = fireWallBaseNum; } } static void UninitPunchFW(void) { ClearAllFWHoles(); if (fireWallFD >= 0) close(fireWallFD); fireWallFD = -1; if (fireWallField) free(fireWallField); fireWallField = NULL; packetAliasMode &= ~PKT_ALIAS_PUNCH_FW; } /* Make a certain link go through the firewall */ void PunchFWHole(struct alias_link *link) { int r; /* Result code */ struct ip_fw rule; /* On-the-fly built rule */ int fwhole; /* Where to punch hole */ /* Don't do anything unless we are asked to */ if ( !(packetAliasMode & PKT_ALIAS_PUNCH_FW) || fireWallFD < 0 || link->link_type != LINK_TCP || !link->data.tcp) return; memset(&rule, 0, sizeof rule); /** Build rule **/ /* Find empty slot */ for (fwhole = fireWallActiveNum; fwhole < fireWallBaseNum + fireWallNumNums && fw_tstfield(fireWallField, fwhole); fwhole++) ; if (fwhole >= fireWallBaseNum + fireWallNumNums || fw_tstfield(fireWallField, fwhole)) { for (fwhole = fireWallBaseNum; fwhole < fireWallActiveNum && fw_tstfield(fireWallField, fwhole); fwhole++) ; if (fwhole == fireWallActiveNum) { /* No rule point empty - we can't punch more holes. */ fireWallActiveNum = fireWallBaseNum; +#ifdef DEBUG fprintf(stderr, "libalias: Unable to create firewall hole!\n"); +#endif return; } } /* Start next search at next position */ fireWallActiveNum = fwhole+1; /* Build generic part of the two rules */ rule.fw_number = fwhole; rule.fw_nports = 1; /* Number of source ports; dest ports follow */ rule.fw_flg = IP_FW_F_ACCEPT; rule.fw_prot = IPPROTO_TCP; rule.fw_smsk.s_addr = INADDR_BROADCAST; rule.fw_dmsk.s_addr = INADDR_BROADCAST; /* Build and apply specific part of the rules */ rule.fw_src = GetOriginalAddress(link); rule.fw_dst = GetDestAddress(link); rule.fw_uar.fw_pts[0] = ntohs(GetOriginalPort(link)); rule.fw_uar.fw_pts[1] = ntohs(GetDestPort(link)); /* Skip non-bound links - XXX should not be strictly necessary, but seems to leave hole if not done. Leak of non-bound links? (Code should be left even if the problem is fixed - it is a clear optimization) */ if (rule.fw_uar.fw_pts[0] != 0 && rule.fw_uar.fw_pts[1] != 0) { r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, &rule, sizeof rule); +#ifdef DEBUG if (r) err(1, "alias punch inbound(1) setsockopt(IP_FW_ADD)"); +#endif rule.fw_src = GetDestAddress(link); rule.fw_dst = GetOriginalAddress(link); rule.fw_uar.fw_pts[0] = ntohs(GetDestPort(link)); rule.fw_uar.fw_pts[1] = ntohs(GetOriginalPort(link)); r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, &rule, sizeof rule); +#ifdef DEBUG if (r) err(1, "alias punch inbound(2) setsockopt(IP_FW_ADD)"); +#endif } /* Indicate hole applied */ link->data.tcp->fwhole = fwhole; fw_setfield(fireWallField, fwhole); } /* Remove a hole in a firewall associated with a particular alias link. Calling this too often is harmless. */ static void ClearFWHole(struct alias_link *link) { if (link->link_type == LINK_TCP && link->data.tcp) { int fwhole = link->data.tcp->fwhole; /* Where is the firewall hole? */ struct ip_fw rule; if (fwhole < 0) return; memset(&rule, 0, sizeof rule); rule.fw_number = fwhole; while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule)) ; fw_clrfield(fireWallField, fwhole); link->data.tcp->fwhole = -1; } } /* Clear out the entire range dedicated to firewall holes. */ static void ClearAllFWHoles(void) { struct ip_fw rule; /* On-the-fly built rule */ int i; if (fireWallFD < 0) return; memset(&rule, 0, sizeof rule); for (i = fireWallBaseNum; i < fireWallBaseNum + fireWallNumNums; i++) { rule.fw_number = i; while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule)) ; } memset(fireWallField, 0, fireWallNumNums); } #endif Index: head/sys/netinet/libalias/alias_ftp.c =================================================================== --- head/sys/netinet/libalias/alias_ftp.c (revision 44615) +++ head/sys/netinet/libalias/alias_ftp.c (revision 44616) @@ -1,227 +1,229 @@ /* Alias_ftp.c performs special processing for FTP sessions under TCP. Specifically, when a PORT command from the client side is sent, it is intercepted and modified. The address is changed to the gateway machine and an aliasing port is used. For this routine to work, the PORT command must fit entirely into a single TCP packet. This is typically the case, but exceptions can easily be envisioned under the actual specifications. Probably the most troubling aspect of the approach taken here is that the new PORT command will typically be a different length, and this causes a certain amount of bookkeeping to keep track of the changes of sequence and acknowledgment numbers, since the client machine is totally unaware of the modification to the TCP stream. This software is placed into the public domain with no restrictions on its distribution. Initial version: August, 1996 (cjm) Version 1.6 Brian Somers and Martin Renters identified an IP checksum error for modified IP packets. Version 1.7: January 9, 1996 (cjm) Differental checksum computation for change in IP packet length. Version 2.1: May, 1997 (cjm) Very minor changes to conform with local/global/function naming conventions withing the packet alising module. See HISTORY file for record of revisions. */ /* Includes */ #include #include #include #include #include #include #include #include #include "alias_local.h" static void NewFtpPortCommand(struct ip *, struct alias_link *, struct in_addr, u_short, int); void AliasHandleFtpOut( struct ip *pip, /* IP packet to examine/patch */ struct alias_link *link, /* The link to go through (aliased port) */ int maxpacketsize /* The maximum size this packet can grow to (including headers) */) { int hlen, tlen, dlen; struct in_addr true_addr; u_short true_port; char *sptr; struct tcphdr *tc; /* Calculate data length of TCP packet */ tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); hlen = (pip->ip_hl + tc->th_off) << 2; tlen = ntohs(pip->ip_len); dlen = tlen - hlen; /* Return is data length is too long or too short */ if (dlen<10 || dlen>80) return; /* Place string pointer and beginning of data */ sptr = (char *) pip; sptr += hlen; /* Parse through string using state diagram method */ { char ch, zero; int i, state; u_long a1, a2, a3, a4; u_short p1, p2; a1=0; a2=0; a3=0; a4=0; p1=0; p2=0; zero = '0'; state=-4; for (i=0; iip_hl << 2)); hlen = (pip->ip_hl + tc->th_off) << 2; tlen = ntohs(pip->ip_len); dlen = tlen - hlen; /* Create new PORT command */ { char stemp[80]; char *sptr; u_short alias_port; u_char *ptr; int a1, a2, a3, a4, p1, p2; struct in_addr alias_address; /* Decompose alias address into quad format */ alias_address = GetAliasAddress(link); ptr = (u_char *) &alias_address.s_addr; a1 = *ptr++; a2=*ptr++; a3=*ptr++; a4=*ptr; /* Decompose alias port into pair format */ alias_port = GetAliasPort(ftp_link); ptr = (char *) &alias_port; p1 = *ptr++; p2=*ptr; /* Generate command string */ sprintf(stemp, "PORT %d,%d,%d,%d,%d,%d\r\n", a1,a2,a3,a4,p1,p2); /* Save string length for IP header modification */ slen = strlen(stemp); /* Copy into IP packet */ sptr = (char *) pip; sptr += hlen; strncpy(sptr, stemp, maxpacketsize-hlen); } /* Save information regarding modified seq and ack numbers */ { int delta; SetAckModified(link); delta = GetDeltaSeqOut(pip, link); AddSeq(pip, link, delta+slen-dlen); } /* Revise IP header */ { u_short new_len; new_len = htons(hlen + slen); DifferentialChecksum(&pip->ip_sum, &new_len, &pip->ip_len, 1); pip->ip_len = new_len; } /* Compute TCP checksum for revised packet */ tc->th_sum = 0; tc->th_sum = TcpChecksum(pip); } else { +#ifdef DEBUG fprintf(stderr, "PacketAlias/HandleFtpOut: Cannot allocate FTP data port\n"); +#endif } } Index: head/sys/netinet/libalias/alias_nbt.c =================================================================== --- head/sys/netinet/libalias/alias_nbt.c (revision 44615) +++ head/sys/netinet/libalias/alias_nbt.c (revision 44616) @@ -1,713 +1,711 @@ /* * Written by Atsushi Murai * * Copyright (C) 1998, System Planning and Engineering Co. All rights reserverd. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by the System Planning and Engineering Co. The name of the * SPEC may not be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * $Id: alias_nbt.c,v 1.1 1998/05/24 03:03:10 amurai Exp $ + * $Id: alias_nbt.c,v 1.2 1998/12/14 02:25:32 dillon Exp $ * * TODO: * oClean up. * oConsidering for word alignment for other platform. */ /* alias_nbt.c performs special processing for NetBios over TCP/IP sessions by UDP. Initial version: May, 1998 (Atsushi Murai ) See HISTORY file for record of revisions. */ /* Includes */ #include #include #include #include #include #include #include #include #include #include #include "alias_local.h" #define ADJUST_CHECKSUM(acc, cksum) { \ acc += cksum; \ if (acc < 0) \ { \ acc = -acc; \ acc = (acc >> 16) + (acc & 0xffff); \ acc += acc >> 16; \ cksum = (u_short) ~acc; \ } \ else \ { \ acc = (acc >> 16) + (acc & 0xffff); \ acc += acc >> 16; \ cksum = (u_short) acc; \ } \ } typedef struct { struct in_addr oldaddr; u_short oldport; struct in_addr newaddr; u_short newport; u_short *uh_sum; } NBTArguments; typedef struct { unsigned char type; unsigned char flags; u_short id; struct in_addr source_ip; u_short source_port; u_short len; u_short offset; } NbtDataHeader; #define OpQuery 0 #define OpUnknown 4 #define OpRegist 5 #define OpRelease 6 #define OpWACK 7 #define OpRefresh 8 typedef struct { u_short nametrid; u_short dir:1, opcode:4, nmflags:7, rcode:4; u_short qdcount; u_short ancount; u_short nscount; u_short arcount; } NbtNSHeader; #define FMT_ERR 0x1 #define SRV_ERR 0x2 #define IMP_ERR 0x4 #define RFS_ERR 0x5 #define ACT_ERR 0x6 #define CFT_ERR 0x7 -/******************************************************************* - * copy an IP address from one buffer to another * - *******************************************************************/ -void putip(void *dest,void *src) -{ - memcpy(dest,src,4); -} -void PrintRcode( u_char rcode ) { +#ifdef DEBUG +static void PrintRcode( u_char rcode ) { switch (rcode) { case FMT_ERR: printf("\nFormat Error."); case SRV_ERR: printf("\nSever failure."); case IMP_ERR: printf("\nUnsupported request error.\n"); case RFS_ERR: printf("\nRefused error.\n"); case ACT_ERR: printf("\nActive error.\n"); case CFT_ERR: printf("\nName in conflict error.\n"); default: printf("\n???=%0x\n", rcode ); } } +#endif /* Handling Name field */ -u_char *AliasHandleName ( u_char *p, char *pmax ) { +static u_char *AliasHandleName ( u_char *p, char *pmax ) { u_char *s; u_char c; int compress; /* Following length field */ if (p == NULL || (char *)p >= pmax) return(NULL); if (*p & 0xc0 ) { p = p + 2; if ((char *)p > pmax) return(NULL); return ((u_char *)p); } while ( ( *p & 0x3f) != 0x00 ) { s = p + 1; if ( *p == 0x20 ) compress = 1; else compress = 0; /* Get next length field */ p = (u_char *)(p + (*p & 0x3f) + 1); if ((char *)p > pmax) { p = NULL; break; } #ifdef DEBUG printf(":"); #endif while (s < p) { if ( compress == 1 ) { c = (u_char )(((((*s & 0x0f) << 4) | (*(s+1) & 0x0f)) - 0x11)); #ifdef DEBUG if (isprint( c ) ) printf("%c", c ); else printf("<0x%02x>", c ); #endif s +=2; } else { #ifdef DEBUG printf("%c", *s); #endif s++; } } #ifdef DEBUG printf(":"); #endif fflush(stdout); } /* Set up to out of Name field */ if (p == NULL || (char *)p >= pmax) p = NULL; else p++; return ((u_char *)p); } /* * NetBios Datagram Handler (IP/UDP) */ #define DGM_DIRECT_UNIQ 0x10 #define DGM_DIRECT_GROUP 0x11 #define DGM_BROADCAST 0x12 #define DGM_ERROR 0x13 #define DGM_QUERY 0x14 #define DGM_POSITIVE_RES 0x15 #define DGM_NEGATIVE_RES 0x16 int AliasHandleUdpNbt( struct ip *pip, /* IP packet to examine/patch */ struct alias_link *link, struct in_addr *alias_address, u_short alias_port ) { struct udphdr * uh; NbtDataHeader *ndh; u_char *p = NULL; char *pmax; /* Calculate data length of UDP packet */ uh = (struct udphdr *) ((char *) pip + (pip->ip_hl << 2)); pmax = (char *)uh + ntohs( uh->uh_ulen ); ndh = (NbtDataHeader *)((char *)uh + (sizeof (struct udphdr))); if ((char *)(ndh + 1) > pmax) return(-1); #ifdef DEBUG printf("\nType=%02x,", ndh->type ); #endif switch ( ndh->type ) { case DGM_DIRECT_UNIQ: case DGM_DIRECT_GROUP: case DGM_BROADCAST: p = (u_char *)ndh + 14; p = AliasHandleName ( p, pmax ); /* Source Name */ p = AliasHandleName ( p, pmax ); /* Destination Name */ break; case DGM_ERROR: p = (u_char *)ndh + 11; break; case DGM_QUERY: case DGM_POSITIVE_RES: case DGM_NEGATIVE_RES: p = (u_char *)ndh + 10; p = AliasHandleName ( p, pmax ); /* Destination Name */ break; } if (p == NULL || (char *)p > pmax) p = NULL; #ifdef DEBUG printf("%s:%d-->", inet_ntoa(ndh->source_ip), ntohs(ndh->source_port) ); #endif /* Doing a IP address and Port number Translation */ if ( uh->uh_sum != 0 ) { int acc; u_short *sptr; acc = ndh->source_port; acc -= alias_port; sptr = (u_short *) &(ndh->source_ip); acc += *sptr++; acc += *sptr; sptr = (u_short *) alias_address; acc -= *sptr++; acc -= *sptr; ADJUST_CHECKSUM(acc, uh->uh_sum) } ndh->source_ip = *alias_address; ndh->source_port = alias_port; #ifdef DEBUG printf("%s:%d\n", inet_ntoa(ndh->source_ip), ntohs(ndh->source_port) ); fflush(stdout); #endif return((p == NULL) ? -1 : 0); } /* Question Section */ #define QS_TYPE_NB 0x0020 #define QS_TYPE_NBSTAT 0x0021 #define QS_CLAS_IN 0x0001 typedef struct { u_short type; /* The type of Request */ u_short class; /* The class of Request */ } NBTNsQuestion; -u_char * +static u_char * AliasHandleQuestion( u_short count, NBTNsQuestion *q, char *pmax, NBTArguments *nbtarg) { while ( count != 0 ) { /* Name Filed */ q = (NBTNsQuestion *)AliasHandleName((u_char *)q, pmax); if (q == NULL || (char *)(q + 1) > pmax) { q = NULL; break; } /* Type and Class filed */ switch ( ntohs(q->type) ) { case QS_TYPE_NB: case QS_TYPE_NBSTAT: q= q+1; break; default: +#ifdef DEBUG printf("\nUnknown Type on Question %0x\n", ntohs(q->type) ); +#endif break; } count--; } /* Set up to out of Question Section */ return ((u_char *)q); } /* Resource Record */ #define RR_TYPE_A 0x0001 #define RR_TYPE_NS 0x0002 #define RR_TYPE_NULL 0x000a #define RR_TYPE_NB 0x0020 #define RR_TYPE_NBSTAT 0x0021 #define RR_CLAS_IN 0x0001 #define SizeOfNsResource 8 typedef struct { u_short type; u_short class; unsigned int ttl; u_short rdlen; } NBTNsResource; #define SizeOfNsRNB 6 typedef struct { u_short g:1, ont:2, resv:13; struct in_addr addr; } NBTNsRNB; -u_char * +static u_char * AliasHandleResourceNB( NBTNsResource *q, char *pmax, NBTArguments *nbtarg) { NBTNsRNB *nb; u_short bcount; if (q == NULL || (char *)(q + 1) > pmax) return(NULL); /* Check out a length */ bcount = ntohs(q->rdlen); /* Forward to Resource NB position */ nb = (NBTNsRNB *)((u_char *)q + SizeOfNsResource); /* Processing all in_addr array */ #ifdef DEBUG printf("NB rec[%s", inet_ntoa(nbtarg->oldaddr)); printf("->%s, %dbytes] ",inet_ntoa(nbtarg->newaddr ), bcount); #endif while ( nb != NULL && bcount != 0 ) { if ((char *)(nb + 1) > pmax) { nb = NULL; break; } #ifdef DEBUG printf("<%s>", inet_ntoa(nb->addr) ); #endif if (!bcmp(&nbtarg->oldaddr,&nb->addr, sizeof(struct in_addr) ) ) { if ( *nbtarg->uh_sum != 0 ) { int acc; u_short *sptr; sptr = (u_short *) &(nb->addr); acc = *sptr++; acc += *sptr; sptr = (u_short *) &(nbtarg->newaddr); acc -= *sptr++; acc -= *sptr; ADJUST_CHECKSUM(acc, *nbtarg->uh_sum) } nb->addr = nbtarg->newaddr; #ifdef DEBUG printf("O"); #endif } #ifdef DEBUG else { printf("."); } #endif nb=(NBTNsRNB *)((u_char *)nb + SizeOfNsRNB); bcount -= SizeOfNsRNB; } if (nb == NULL || (char *)(nb + 1) > pmax) { nb = NULL; } return ((u_char *)nb); } #define SizeOfResourceA 6 typedef struct { struct in_addr addr; } NBTNsResourceA; -u_char * +static u_char * AliasHandleResourceA( NBTNsResource *q, char *pmax, NBTArguments *nbtarg) { NBTNsResourceA *a; u_short bcount; if (q == NULL || (char *)(q + 1) > pmax) return(NULL); /* Forward to Resource A position */ a = (NBTNsResourceA *)( (u_char *)q + sizeof(NBTNsResource) ); /* Check out of length */ bcount = ntohs(q->rdlen); /* Processing all in_addr array */ #ifdef DEBUG printf("Arec [%s", inet_ntoa(nbtarg->oldaddr)); printf("->%s]",inet_ntoa(nbtarg->newaddr )); #endif while ( bcount != 0 ) { if (a == NULL || (char *)(a + 1) > pmax) return(NULL); #ifdef DEBUG printf("..%s", inet_ntoa(a->addr) ); #endif if ( !bcmp(&nbtarg->oldaddr, &a->addr, sizeof(struct in_addr) ) ) { if ( *nbtarg->uh_sum != 0 ) { int acc; u_short *sptr; sptr = (u_short *) &(a->addr); /* Old */ acc = *sptr++; acc += *sptr; sptr = (u_short *) &nbtarg->newaddr; /* New */ acc -= *sptr++; acc -= *sptr; ADJUST_CHECKSUM(acc, *nbtarg->uh_sum) } a->addr = nbtarg->newaddr; } a++; /*XXXX*/ bcount -= SizeOfResourceA; } if (a == NULL || (char *)(a + 1) > pmax) a = NULL; return ((u_char *)a); } typedef struct { u_short opcode:4, flags:8, resv:4; } NBTNsResourceNULL; -u_char * +static u_char * AliasHandleResourceNULL( NBTNsResource *q, char *pmax, NBTArguments *nbtarg) { NBTNsResourceNULL *n; u_short bcount; if (q == NULL || (char *)(q + 1) > pmax) return(NULL); /* Forward to Resource NULL position */ n = (NBTNsResourceNULL *)( (u_char *)q + sizeof(NBTNsResource) ); /* Check out of length */ bcount = ntohs(q->rdlen); /* Processing all in_addr array */ while ( bcount != 0 ) { if ((char *)(n + 1) > pmax) { n = NULL; break; } n++; bcount -= sizeof(NBTNsResourceNULL); } if ((char *)(n + 1) > pmax) n = NULL; return ((u_char *)n); } -u_char * +static u_char * AliasHandleResourceNS( NBTNsResource *q, char *pmax, NBTArguments *nbtarg) { NBTNsResourceNULL *n; u_short bcount; if (q == NULL || (char *)(q + 1) > pmax) return(NULL); /* Forward to Resource NULL position */ n = (NBTNsResourceNULL *)( (u_char *)q + sizeof(NBTNsResource) ); /* Check out of length */ bcount = ntohs(q->rdlen); /* Resource Record Name Filed */ q = (NBTNsResource *)AliasHandleName( (u_char *)n, pmax ); /* XXX */ if (q == NULL || (char *)((u_char *)n + bcount) > pmax) return(NULL); else return ((u_char *)n + bcount); } typedef struct { u_short numnames; } NBTNsResourceNBSTAT; -u_char * +static u_char * AliasHandleResourceNBSTAT( NBTNsResource *q, char *pmax, NBTArguments *nbtarg) { NBTNsResourceNBSTAT *n; u_short bcount; if (q == NULL || (char *)(q + 1) > pmax) return(NULL); /* Forward to Resource NBSTAT position */ n = (NBTNsResourceNBSTAT *)( (u_char *)q + sizeof(NBTNsResource) ); /* Check out of length */ bcount = ntohs(q->rdlen); if (q == NULL || (char *)((u_char *)n + bcount) > pmax) return(NULL); else return ((u_char *)n + bcount); } -u_char * +static u_char * AliasHandleResource( u_short count, NBTNsResource *q, char *pmax, NBTArguments *nbtarg) { while ( count != 0 ) { /* Resource Record Name Filed */ q = (NBTNsResource *)AliasHandleName( (u_char *)q, pmax ); if (q == NULL || (char *)(q + 1) > pmax) break; #ifdef DEBUG printf("type=%02x, count=%d\n", ntohs(q->type), count ); #endif /* Type and Class filed */ switch ( ntohs(q->type) ) { case RR_TYPE_NB: q = (NBTNsResource *)AliasHandleResourceNB( q, pmax, nbtarg ); break; case RR_TYPE_A: q = (NBTNsResource *)AliasHandleResourceA( q, pmax, nbtarg ); break; case RR_TYPE_NS: q = (NBTNsResource *)AliasHandleResourceNS( q, pmax, nbtarg ); break; case RR_TYPE_NULL: q = (NBTNsResource *)AliasHandleResourceNULL( q, pmax, nbtarg ); break; case RR_TYPE_NBSTAT: q = (NBTNsResource *)AliasHandleResourceNBSTAT( q, pmax, nbtarg ); break; default: +#ifdef DEBUG printf( "\nUnknown Type of Resource %0x\n", ntohs(q->type) ); +#endif break; } count--; } fflush(stdout); return ((u_char *)q); } int AliasHandleUdpNbtNS( struct ip *pip, /* IP packet to examine/patch */ struct alias_link *link, struct in_addr *alias_address, u_short *alias_port, struct in_addr *original_address, u_short *original_port ) { struct udphdr * uh; NbtNSHeader * nsh; u_char * p; char *pmax; NBTArguments nbtarg; /* Set up Common Parameter */ nbtarg.oldaddr = *alias_address; nbtarg.oldport = *alias_port; nbtarg.newaddr = *original_address; nbtarg.newport = *original_port; /* Calculate data length of UDP packet */ uh = (struct udphdr *) ((char *) pip + (pip->ip_hl << 2)); nbtarg.uh_sum = &(uh->uh_sum); nsh = (NbtNSHeader *)((char *)uh + (sizeof(struct udphdr))); p = (u_char *)(nsh + 1); pmax = (char *)uh + ntohs( uh->uh_ulen ); if ((char *)(nsh + 1) > pmax) return(-1); #ifdef DEBUG printf(" [%s] ID=%02x, op=%01x, flag=%02x, rcode=%01x, qd=%04x" ", an=%04x, ns=%04x, ar=%04x, [%d]-->", nsh->dir ? "Response": "Request", nsh->nametrid, nsh->opcode, nsh->nmflags, nsh->rcode, ntohs(nsh->qdcount), ntohs(nsh->ancount), ntohs(nsh->nscount), ntohs(nsh->arcount), (u_char *)p -(u_char *)nsh ); #endif /* Question Entries */ if (ntohs(nsh->qdcount) !=0 ) { p = AliasHandleQuestion( ntohs(nsh->qdcount), (NBTNsQuestion *)p, pmax, &nbtarg ); } /* Answer Resource Records */ if (ntohs(nsh->ancount) !=0 ) { p = AliasHandleResource( ntohs(nsh->ancount), (NBTNsResource *)p, pmax, &nbtarg ); } /* Authority Resource Recodrs */ if (ntohs(nsh->nscount) !=0 ) { p = AliasHandleResource( ntohs(nsh->nscount), (NBTNsResource *)p, pmax, &nbtarg ); } /* Additional Resource Recodrs */ if (ntohs(nsh->arcount) !=0 ) { p = AliasHandleResource( ntohs(nsh->arcount), (NBTNsResource *)p, pmax, &nbtarg ); } #ifdef DEBUG PrintRcode(nsh->rcode); #endif return ((p == NULL) ? -1 : 0); } - Index: head/sys/netinet/libalias/alias_proxy.c =================================================================== --- head/sys/netinet/libalias/alias_proxy.c (revision 44615) +++ head/sys/netinet/libalias/alias_proxy.c (revision 44616) @@ -1,801 +1,805 @@ /* file: alias_proxy.c This file encapsulates special operations related to transparent proxy redirection. This is where packets with a particular destination, usually tcp port 80, are redirected to a proxy server. When packets are proxied, the destination address and port are modified. In certain cases, it is necessary to somehow encode the original address/port info into the packet. Two methods are presently supported: addition of a [DEST addr port] string at the beginning a of tcp stream, or inclusion of an optional field in the IP header. There is one public API function: PacketAliasProxyRule() -- Adds and deletes proxy rules. Rules are stored in a linear linked list, so lookup efficiency won't be too good for large lists. Initial development: April, 1998 (cjm) */ /* System includes */ #include #include #include #include #include #include #include /* BSD IPV4 includes */ #include #include #include #include #include #include "alias_local.h" /* Functions used by alias*.c */ #include "alias.h" /* Public API functions for libalias */ /* Data structures */ /* * A linked list of arbitrary length, based on struct proxy_entry is * used to store proxy rules. */ struct proxy_entry { #define PROXY_TYPE_ENCODE_NONE 1 #define PROXY_TYPE_ENCODE_TCPSTREAM 2 #define PROXY_TYPE_ENCODE_IPHDR 3 int rule_index; int proxy_type; u_char proto; u_short proxy_port; u_short server_port; struct in_addr server_addr; struct in_addr src_addr; struct in_addr src_mask; struct in_addr dst_addr; struct in_addr dst_mask; struct proxy_entry *next; struct proxy_entry *last; }; /* File scope variables */ static struct proxy_entry *proxyList; /* Local (static) functions: IpMask() -- Utility function for creating IP masks from integer (1-32) specification. IpAddr() -- Utility function for converting string to IP address IpPort() -- Utility function for converting string to port number RuleAdd() -- Adds an element to the rule list. RuleDelete() -- Removes an element from the rule list. RuleNumberDelete() -- Removes all elements from the rule list having a certain rule number. ProxyEncodeTcpStream() -- Adds [DEST x.x.x.x xxxx] to the beginning of a TCP stream. ProxyEncodeIpHeader() -- Adds an IP option indicating the true destination of a proxied IP packet */ static int IpMask(int, struct in_addr *); static int IpAddr(char *, struct in_addr *); static int IpPort(char *, int, int *); static void RuleAdd(struct proxy_entry *); static void RuleDelete(struct proxy_entry *); static int RuleNumberDelete(int); static void ProxyEncodeTcpStream(struct alias_link *, struct ip *, int); static void ProxyEncodeIpHeader(struct ip *, int); static int IpMask(int nbits, struct in_addr *mask) { int i; u_int imask; if (nbits < 0 || nbits > 32) return -1; imask = 0; for (i=0; i> 1) + 0x80000000; mask->s_addr = htonl(imask); return 0; } static int IpAddr(char *s, struct in_addr *addr) { if (inet_aton(s, addr) == 0) return -1; else return 0; } static int IpPort(char *s, int proto, int *port) { int n; n = sscanf(s, "%d", port); if (n != 1) { struct servent *se; if (proto == IPPROTO_TCP) se = getservbyname(s, "tcp"); else if (proto == IPPROTO_UDP) se = getservbyname(s, "udp"); else return -1; if (se == NULL) return -1; *port = (u_int) ntohs(se->s_port); } return 0; } void RuleAdd(struct proxy_entry *entry) { int rule_index; struct proxy_entry *ptr; struct proxy_entry *ptr_last; if (proxyList == NULL) { proxyList = entry; entry->last = NULL; entry->next = NULL; return; } rule_index = entry->rule_index; ptr = proxyList; ptr_last = NULL; while (ptr != NULL) { if (ptr->rule_index >= rule_index) { if (ptr_last == NULL) { entry->next = proxyList; entry->last = NULL; proxyList->last = entry; proxyList = entry; return; } ptr_last->next = entry; ptr->last = entry; entry->last = ptr->last; entry->next = ptr; return; } ptr_last = ptr; ptr = ptr->next; } ptr_last->next = entry; entry->last = ptr_last; entry->next = NULL; } static void RuleDelete(struct proxy_entry *entry) { if (entry->last != NULL) entry->last->next = entry->next; else proxyList = entry->next; if (entry->next != NULL) entry->next->last = entry->last; free(entry); } static int RuleNumberDelete(int rule_index) { int err; struct proxy_entry *ptr; err = -1; ptr = proxyList; while (ptr != NULL) { struct proxy_entry *ptr_next; ptr_next = ptr->next; if (ptr->rule_index == rule_index) { err = 0; RuleDelete(ptr); } ptr = ptr_next; } return err; } static void ProxyEncodeTcpStream(struct alias_link *link, struct ip *pip, int maxpacketsize) { int slen; char buffer[40]; struct tcphdr *tc; /* Compute pointer to tcp header */ tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); /* Don't modify if once already modified */ if (GetAckModified (link)) return; /* Translate destination address and port to string form */ snprintf(buffer, sizeof(buffer) - 2, "[DEST %s %d]", inet_ntoa(GetProxyAddress (link)), (u_int) ntohs(GetProxyPort (link))); /* Pad string out to a multiple of two in length */ slen = strlen(buffer); switch (slen % 2) { case 0: strcat(buffer, " \n"); slen += 2; break; case 1: strcat(buffer, "\n"); slen += 1; } /* Check for packet overflow */ if ((ntohs(pip->ip_len) + strlen(buffer)) > maxpacketsize) return; /* Shift existing TCP data and insert destination string */ { int dlen; int hlen; u_char *p; hlen = (pip->ip_hl + tc->th_off) << 2; dlen = ntohs (pip->ip_len) - hlen; /* Modify first packet that has data in it */ if (dlen == 0) return; p = (char *) pip; p += hlen; memmove(p + slen, p, dlen); memcpy(p, buffer, slen); } /* Save information about modfied sequence number */ { int delta; SetAckModified(link); delta = GetDeltaSeqOut(pip, link); AddSeq(pip, link, delta+slen); } /* Update IP header packet length and checksum */ { int accumulate; accumulate = pip->ip_len; pip->ip_len = htons(ntohs(pip->ip_len) + slen); accumulate -= pip->ip_len; ADJUST_CHECKSUM(accumulate, pip->ip_sum); } /* Update TCP checksum, Use TcpChecksum since so many things have already changed. */ tc->th_sum = 0; tc->th_sum = TcpChecksum (pip); } static void ProxyEncodeIpHeader(struct ip *pip, int maxpacketsize) { #define OPTION_LEN_BYTES 8 #define OPTION_LEN_INT16 4 #define OPTION_LEN_INT32 2 u_char option[OPTION_LEN_BYTES]; -fprintf(stdout, " ip cksum 1 = %x\n", (u_int) IpChecksum(pip)); -fprintf(stdout, "tcp cksum 1 = %x\n", (u_int) TcpChecksum(pip)); +#ifdef DEBUG + fprintf(stdout, " ip cksum 1 = %x\n", (u_int) IpChecksum(pip)); + fprintf(stdout, "tcp cksum 1 = %x\n", (u_int) TcpChecksum(pip)); +#endif /* Check to see that there is room to add an IP option */ if (pip->ip_hl > (0x0f - OPTION_LEN_INT32)) return; /* Build option and copy into packet */ { u_char *ptr; struct tcphdr *tc; ptr = (u_char *) pip; ptr += 20; memcpy(ptr + OPTION_LEN_BYTES, ptr, ntohs(pip->ip_len) - 20); option[0] = 0x64; /* class: 3 (reserved), option 4 */ option[1] = OPTION_LEN_BYTES; memcpy(&option[2], (u_char *) &pip->ip_dst, 4); tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); memcpy(&option[6], (u_char *) &tc->th_sport, 2); memcpy(ptr, option, 8); } /* Update checksum, header length and packet length */ { int i; int accumulate; u_short *sptr; sptr = (u_short *) option; accumulate = 0; for (i=0; iip_hl += OPTION_LEN_INT32; accumulate -= *sptr; accumulate += pip->ip_len; pip->ip_len = htons(ntohs(pip->ip_len) + OPTION_LEN_BYTES); accumulate -= pip->ip_len; ADJUST_CHECKSUM(accumulate, pip->ip_sum); } #undef OPTION_LEN_BYTES #undef OPTION_LEN_INT16 #undef OPTION_LEN_INT32 -fprintf(stdout, " ip cksum 2 = %x\n", (u_int) IpChecksum(pip)); -fprintf(stdout, "tcp cksum 2 = %x\n", (u_int) TcpChecksum(pip)); +#ifdef DEBUG + fprintf(stdout, " ip cksum 2 = %x\n", (u_int) IpChecksum(pip)); + fprintf(stdout, "tcp cksum 2 = %x\n", (u_int) TcpChecksum(pip)); +#endif } /* Functions by other packet alias source files ProxyCheck() -- Checks whether an outgoing packet should be proxied. ProxyModify() -- Encodes the original destination address/port for a packet which is to be redirected to a proxy server. */ int ProxyCheck(struct ip *pip, struct in_addr *proxy_server_addr, u_short *proxy_server_port) { u_short dst_port; struct in_addr src_addr; struct in_addr dst_addr; struct proxy_entry *ptr; src_addr = pip->ip_src; dst_addr = pip->ip_dst; dst_port = ((struct tcphdr *) ((char *) pip + (pip->ip_hl << 2))) ->th_dport; ptr = proxyList; while (ptr != NULL) { u_short proxy_port; proxy_port = ptr->proxy_port; if ((dst_port == proxy_port || proxy_port == 0) && pip->ip_p == ptr->proto && src_addr.s_addr != ptr->server_addr.s_addr) { struct in_addr src_addr_masked; struct in_addr dst_addr_masked; src_addr_masked.s_addr = src_addr.s_addr & ptr->src_mask.s_addr; dst_addr_masked.s_addr = dst_addr.s_addr & ptr->dst_mask.s_addr; if ((src_addr_masked.s_addr == ptr->src_addr.s_addr) && (dst_addr_masked.s_addr == ptr->dst_addr.s_addr)) { if ((*proxy_server_port = ptr->server_port) == 0) *proxy_server_port = dst_port; *proxy_server_addr = ptr->server_addr; return ptr->proxy_type; } } ptr = ptr->next; } return 0; } void ProxyModify(struct alias_link *link, struct ip *pip, int maxpacketsize, int proxy_type) { switch (proxy_type) { case PROXY_TYPE_ENCODE_IPHDR: ProxyEncodeIpHeader(pip, maxpacketsize); break; case PROXY_TYPE_ENCODE_TCPSTREAM: ProxyEncodeTcpStream(link, pip, maxpacketsize); break; } } /* Public API functions */ int PacketAliasProxyRule(char *cmd) { /* * This function takes command strings of the form: * * server [:] * [port ] * [rule n] * [proto tcp|udp] * [src [/n]] * [dst [/n]] * [type encode_tcp_stream|encode_ip_hdr|no_encode] * * delete * * Subfields can be in arbitrary order. Port numbers and addresses * must be in either numeric or symbolic form. An optional rule number * is used to control the order in which rules are searched. If two * rules have the same number, then search order cannot be guaranteed, * and the rules should be disjoint. If no rule number is specified, * then 0 is used, and group 0 rules are always checked before any * others. */ int i, n, len; int cmd_len; int token_count; int state; char *token; char buffer[256]; char str_port[sizeof(buffer)]; char str_server_port[sizeof(buffer)]; int rule_index; int proto; int proxy_type; int proxy_port; int server_port; struct in_addr server_addr; struct in_addr src_addr, src_mask; struct in_addr dst_addr, dst_mask; struct proxy_entry *proxy_entry; /* Copy command line into a buffer */ cmd_len = strlen(cmd); if (cmd_len > (sizeof(buffer) - 1)) return -1; strcpy(buffer, cmd); /* Convert to lower case */ len = strlen(buffer); for (i=0; iproxy_type = proxy_type; proxy_entry->rule_index = rule_index; proxy_entry->proto = proto; proxy_entry->proxy_port = htons(proxy_port); proxy_entry->server_port = htons(server_port); proxy_entry->server_addr = server_addr; proxy_entry->src_addr.s_addr = src_addr.s_addr & src_mask.s_addr; proxy_entry->dst_addr.s_addr = dst_addr.s_addr & dst_mask.s_addr; proxy_entry->src_mask = src_mask; proxy_entry->dst_mask = dst_mask; RuleAdd(proxy_entry); return 0; }