Hi, this is my first submission to the list and I hope that I'm doing in the right way. :-)
Well, the features added to Network Address Translator are: 1) Allow the user to use the string "localhost" on the client-nat network configuration in a way that is not necessary to inform the IP address beforehand. Openvpn will set the dynamic received IP from DHCP. Example: client-nat snat localhost 255.255.255.255 172.20.1.15 # replaces the 'localhost' string with the DHCP address received from openvpn server. 2) Allow the user to enable the FTP NAT support through the --enable-nat-ftp-support option. This is useful for systems that don't have conntrack-tools support, for example on Windows systems. On windows this feature is enabled by default. enable-nat-ftp-support (yes | no) The following patch was written based on release/2.3 branch. -------------------------------------------------------------------------------------------------------------------------------------- >From d11957a025dc6c113874e7b04ce4c8e9513c3b02 Mon Sep 17 00:00:00 2001 From: venturus <venturus@venturus-virtual-machine.(none)> List-Post: openvpn-devel@lists.sourceforge.net Date: Thu, 13 Aug 2015 08:26:36 -0300 Subject: [PATCH] Replaces the client-nat network string 'localhost' with the dynamic IP set to the tun/tap interface by the openvpn server. Example: --client-nat snat localhost 255.255.255.255 172.20.1.15 # replaces the 'localhost' string with the DHCP address received from openvpn server. Signed-off-by: Rafael Gava <rafael.olive...@venturus.org.br> --- src/openvpn/clinat.c | 57 ++++++++++++++++++++++++++++++++++++++++++-------- src/openvpn/clinat.h | 2 ++ src/openvpn/init.c | 4 ++++ 3 files changed, 54 insertions(+), 9 deletions(-) mode change 100644 => 100755 src/openvpn/clinat.c mode change 100644 => 100755 src/openvpn/clinat.h mode change 100644 => 100755 src/openvpn/init.c diff --git a/src/openvpn/clinat.c b/src/openvpn/clinat.c old mode 100644 new mode 100755 index af75fc9..2460185 --- a/src/openvpn/clinat.c +++ b/src/openvpn/clinat.c @@ -107,11 +107,11 @@ copy_client_nat_option_list (struct client_nat_option_list *dest, void add_client_nat_to_option_list (struct client_nat_option_list *dest, - const char *type, - const char *network, - const char *netmask, - const char *foreign_network, - int msglevel) + const char *type, + const char *network, + const char *netmask, + const char *foreign_network, + int msglevel) { struct client_nat_entry e; bool ok; @@ -126,12 +126,19 @@ add_client_nat_to_option_list (struct client_nat_option_list *dest, return; } - e.network = getaddr(0, network, 0, &ok, NULL); - if (!ok) + if (network && !strcmp(network, "localhost")) { - msg(msglevel, "client-nat: bad network: %s", network); - return; + msg (M_INFO, "*** client-nat localhost detected..."); + e.network = 0xFFFFFFFF; + } else { + e.network = getaddr(0, network, 0, &ok, NULL); + if (!ok) + { + msg(msglevel, "client-nat: bad network: %s", network); + return; + } } + e.netmask = getaddr(0, netmask, 0, &ok, NULL); if (!ok) { @@ -148,6 +155,7 @@ add_client_nat_to_option_list (struct client_nat_option_list *dest, add_entry(dest, &e); } + #if 0 static void print_checksum (struct openvpn_iphdr *iph, const char *prefix) @@ -266,4 +274,35 @@ client_nat_transform (const struct client_nat_option_list *list, } } +/* +* Replaces the localhost token with the IP received from OpenVPN +*/ +bool +update_localhost_nat(struct client_nat_option_list *dest, in_addr_t local_ip) +{ + int i; + bool ret = false; + + if (!dest) + return ret; + + for (i=0; i <= dest->n; i++) + { + struct client_nat_entry *nat_entry = &dest->entries[i]; + if (nat_entry && nat_entry->network == 0xFFFFFFFF) + { + struct in_addr addr; + + nat_entry->network = ntohl(local_ip); + addr.s_addr = nat_entry->network; + char *dot_ip = inet_ntoa(addr); + + msg (M_INFO, "Updating NAT table from localhost to: %s", dot_ip); + ret = true; + } + } + + return ret; +} + #endif diff --git a/src/openvpn/clinat.h b/src/openvpn/clinat.h old mode 100644 new mode 100755 index d55a727..ce995d9 --- a/src/openvpn/clinat.h +++ b/src/openvpn/clinat.h @@ -62,4 +62,6 @@ void client_nat_transform (const struct client_nat_option_list *list, struct buffer *ipbuf, const int direction); +bool update_localhost_nat(struct client_nat_option_list *dest, in_addr_t local_ip); + #endif diff --git a/src/openvpn/init.c b/src/openvpn/init.c old mode 100644 new mode 100755 index 71c91a2..4a63ee8 --- a/src/openvpn/init.c +++ b/src/openvpn/init.c @@ -1476,6 +1476,10 @@ do_open_tun (struct context *c) c->c1.tuntap->post_open_mtu, SET_MTU_TUN | SET_MTU_UPPER_BOUND); +#ifdef ENABLE_CLIENT_NAT + update_localhost_nat(c->options.client_nat, c->c1.tuntap->local); +#endif + ret = true; static_context = c; } -- 1.7.9.5 >From 18b1411689df67feafeb15630c3519ab2cbd42d4 Mon Sep 17 00:00:00 2001 From: Rafael Gava <rafael.olive...@venturus.org.br> List-Post: openvpn-devel@lists.sourceforge.net Date: Fri, 14 Aug 2015 15:27:50 -0300 Subject: [PATCH] Added FTP NAT support. This feature can be enabled via --enable-nat-ftp-support option. Signed-off-by: Rafael Gava <rafael.olive...@venturus.org.br> --- src/openvpn/clinat.c | 773 ++++++++++++++++++++++++++++++++++++++++++++++--- src/openvpn/clinat.h | 15 +- src/openvpn/forward.c | 75 ++--- src/openvpn/options.c | 12 + src/openvpn/options.h | 1 + 5 files changed, 787 insertions(+), 89 deletions(-) mode change 100644 => 100755 src/openvpn/forward.c mode change 100644 => 100755 src/openvpn/options.c mode change 100644 => 100755 src/openvpn/options.h diff --git a/src/openvpn/clinat.c b/src/openvpn/clinat.c index 2460185..1d213f5 100755 --- a/src/openvpn/clinat.c +++ b/src/openvpn/clinat.c @@ -36,6 +36,159 @@ #include "proto.h" #include "socket.h" #include "memdbg.h" +#include <stdio.h> +#include <ctype.h> +#include <string.h> + +/* Delta Table Types */ + +typedef struct delta_table_key { + uint32_t src_addr; + uint32_t dest_addr; + uint16_t src_port; + uint16_t dest_port; +} delta_table_key_t; + +typedef struct delta_table_entry { + delta_table_key_t key; + uint16_t delta_out; + uint16_t delta_in; + uint8_t marked_to_remove; + uint8_t reserved[3]; + uint32_t timestamp; + struct delta_table_entry *_next; +} delta_table_entry_t; + +typedef struct delta_table { + struct delta_table_entry *head; +} delta_table_t; + + +// Global delta table +//#define DEBUG_DELTA 1 + +delta_table_t *table = NULL; + +#define TIME_TO_RESPOND 60 * 5 // 5 Minutes +#define TIME_TO_LIVE 24 * 60 * 60 + +/* Delta Table Functions */ + +// In seconds, since epoch +static uint32_t +get_timestamp() { + return (uint32_t)time(NULL); +} + +static void +print_delta_table_entries(delta_table_t *table) { + delta_table_entry_t *current = table->head; + + if (current == NULL) { + return; + } + + while (current != NULL) { + printf("from %d -> to %d: removed: %s, timestamp: %d, delta_out: %d, delta_in: %d\n", + current->key.src_addr, current->key.dest_addr, current->marked_to_remove ? "true" : "false", + current->timestamp, current->delta_out, current->delta_in); + current = current->_next; + } +} + +static struct delta_table * +init_delta_table() { + delta_table_t *table = malloc(sizeof(delta_table_t)); + table->head = NULL; + return table; +} + +static struct delta_table_entry * +add_delta_entry(delta_table_t *table, delta_table_entry_t *new_delta_entry) { + if (table->head == NULL) { + table->head = new_delta_entry; + table->head->_next = NULL; + } else { + // Add element to HEAD, for performance gain + new_delta_entry->_next = table->head; + table->head = new_delta_entry; + } + return new_delta_entry; +} + +static int +remove_delta_entry(delta_table_t *table, delta_table_entry_t *delta_entry) { + delta_table_entry_t *current = table->head; + delta_table_entry_t *before = NULL; + uint8_t found = 0; + + //msg (M_INFO, "On remove_delta_entry - entry: %p", &delta_entry); + + while (current != NULL) { + if(current == delta_entry) { + found = 1; + break; + } + before = current; + current = current->_next; + } + + //Element not found + if (!found) { + return 0; + } + + if(before==NULL){ + table->head = table->head->_next; + } else { + before->_next = delta_entry->_next; + } + + //msg (M_INFO, "On remove_delta_entry - entry: %p - freed", &delta_entry); + + free(delta_entry); + return 1; +} + +static struct delta_table_entry * +get_delta_entry(delta_table_t *table, delta_table_key_t key) { + delta_table_entry_t *current = table->head; + delta_table_entry_t *aux = NULL; + delta_table_entry_t *el = NULL; + + while (current != NULL) { + // Check TTL and Timeout + if ((current->marked_to_remove && get_timestamp() - current->timestamp >= TIME_TO_RESPOND) /* || get_timestamp() - current->timestamp >= TIME_TO_LIVE */){ + aux = current; + //If the CURRENT is to be removed, then dont return it + if (aux == el) { + el = NULL; + } + current = current->_next; + remove_delta_entry(table, aux); + } else { + if (( + // Regular connection + (current->key.src_addr == key.src_addr && + current->key.src_port == key.src_port && + current->key.dest_addr == key.dest_addr && + current->key.dest_port == key.dest_port) || + //Check if it is a returned connection + (current->key.dest_addr == key.src_addr && + current->key.dest_port == key.src_port && + current->key.src_addr == key.dest_addr && + current->key.src_port == key.dest_port))) { + el = current; + } + current = current->_next; + } + } + + return el; +} + +/* Delta Table Functions End */ + static bool add_entry(struct client_nat_option_list *dest, @@ -192,19 +345,533 @@ print_pkt (struct openvpn_iphdr *iph, const char *prefix, const int direction, c gc_free (&gc); } +#if DEBUG_DELTA + +#ifndef HEXDUMP_COLS +#define HEXDUMP_COLS 8 +#endif + +static void hexdump(void *mem, unsigned int len) +{ + unsigned int i, j; + + for(i = 0; i < len + ((len % HEXDUMP_COLS) ? (HEXDUMP_COLS - len % HEXDUMP_COLS) : 0); i++) + { + /* print offset */ + if(i % HEXDUMP_COLS == 0) + { + printf("0x%06x: ", i); + } + + /* print hex data */ + if(i < len) + { + printf("%02x ", 0xFF & ((char*)mem)[i]); + } + else /* end of block, just aligning for ASCII dump */ + { + printf(" "); + } + + /* print ASCII dump */ + if(i % HEXDUMP_COLS == (HEXDUMP_COLS - 1)) + { + for(j = i - (HEXDUMP_COLS - 1); j <= i; j++) + { + unsigned char ch = 0xFF & ((char*)mem)[j]; + + if(j >= len) /* end of block, not really printing */ + { + putchar(' '); + } + else if (ch >= 0x80) + { + putchar('.'); + } + else if(isprint(((char*)mem)[j])) /* printable char */ + { + putchar(0xFF & ((char*)mem)[j]); + } + else /* other char */ + { + putchar('.'); + } + } + putchar('\n'); + } + } +} +#endif + +static int try_number(const char *data, size_t dlen, uint32_t array[], + int array_size, char sep, char term) +{ + uint32_t i, len; + + memset(array, 0, sizeof(array[0])*array_size); + + /* Keep data pointing at next char. */ + for (i = 0, len = 0; len < dlen && i < array_size; len++, data++) { + if (*data >= '0' && *data <= '9') { + array[i] = array[i]*10 + *data - '0'; + } + else if (*data == sep) + i++; + else { + /* Unexpected character; true if it's the + terminator and we're finished. */ + if (*data == term && i == array_size - 1) + return len; + + msg (M_ERRNO, "CNAT - try_number - Char %u (got %u nums) `%u' unexpected\n", len, i, *data); + return 0; + } + } + + msg (M_ERRNO, "CNAT - ERROR: Failed to fill %u numbers separated by %c\n", array_size, sep); + + return 0; +} + +static int try_rfc959(const char *, size_t, u_int32_t [], char); + +static struct ftp_search { + int direction; + const char *pattern; + size_t plen; + char skip; + char term; + int (*getnum)(const char *, size_t, u_int32_t[], char); +} search[] = { + { + CN_OUTGOING, + "PORT", sizeof("PORT") - 1, ' ', '\r', + try_rfc959, + }, + { + CN_INCOMING, + "227 ", sizeof("227 ") - 1, '(', ')', + try_rfc959, + }, +}; + + +/* Returns 0, or length of numbers: 192,168,1,1,5,6 */ +static int +try_rfc959(const char *data, size_t dlen, u_int32_t array[6], + char term) +{ + return try_number(data, dlen, array, 6, ',', term); +} + +/* Grab port: number up to delimiter */ +static int +get_port(const char *data, int start, size_t dlen, char delim, + u_int32_t array[2]) +{ + u_int16_t port = 0; + int i; + + for (i = start; i < dlen; i++) { + /* Finished? */ + if (data[i] == delim) { + if (port == 0) + break; + array[0] = port >> 8; + array[1] = port; + return i + 1; + } + else if (data[i] >= '0' && data[i] <= '9') + port = port*10 + data[i] - '0'; + else /* Some other crap */ + break; + } + return 0; +} + +/* Return 1 for match, 0 for accept, -1 for partial. */ +static int +find_pattern(const char *data, size_t dlen, + const char *pattern, size_t plen, + char skip, char term, + unsigned int *numoff, + unsigned int *numlen, + u_int32_t array[6], + int (*getnum)(const char *, size_t, u_int32_t[], char)) +{ + size_t i; + + if (check_debug_level (D_CLIENT_NAT)) + msg (M_INFO, "CNAT - find_pattern %s: dlen = %u\n", pattern, dlen); + + if (dlen == 0) + return 0; + + if (dlen <= plen) { + /* Short packet: try for partial? */ + if (strncasecmp(data, pattern, dlen) == 0) + return -1; + else return 0; + } + + if (strncasecmp(data, pattern, plen) != 0) { + return 0; + } + + if (check_debug_level (D_CLIENT_NAT)) + msg (M_INFO, "CNAT - Pattern matches!\n"); + + /* Now we've found the constant string, try to skip + to the 'skip' character */ + for (i = plen; data[i] != skip; i++) + if (i == dlen - 1) return -1; + + /* Skip over the last character */ + i++; + + if (check_debug_level (D_CLIENT_NAT)) + msg (M_INFO, "CNAT - Skipped up to `%c'!\n", skip); + + *numoff = i; + *numlen = getnum(data + i, dlen - i, array, term); + if (!*numlen) + return -1; + + if (check_debug_level (D_CLIENT_NAT)) + msg (M_INFO, "CNAT - Match succeeded!\n"); + + return 1; +} + +static uint16_t +tcp_checksum (const uint8_t *buf, + const int len_tcp, + const uint32_t src_addr, + const uint32_t dest_addr) +{ + uint16_t word16; + uint32_t sum = 0; + int i; + uint8_t * psrc_addr = (uint8_t *) &src_addr; + uint8_t * pdest_addr = (uint8_t *) &dest_addr; + + /* make 16 bit words out of every two adjacent 8 bit words and */ + /* calculate the sum of all 16 bit words */ + for (i = 0; i < len_tcp; i += 2){ + word16 = ((buf[i] << 8) & 0xFF00) + ((i + 1 < len_tcp) ? (buf[i+1] & 0xFF) : 0); + sum += word16; + } + + /* add the TCP pseudo header which contains the IP source and destination addresses */ + for (i = 0; i < 4; i += 2){ + word16 =((psrc_addr[i] << 8) & 0xFF00) + (psrc_addr[i+1] & 0xFF); + sum += word16; + } + for (i = 0; i < 4; i += 2){ + word16 =((pdest_addr[i] << 8) & 0xFF00) + (pdest_addr[i+1] & 0xFF); + sum += word16; + } + + /* the protocol number and the length of the TCP packet */ + sum += (uint16_t) OPENVPN_IPPROTO_TCP + (uint16_t) len_tcp; + + /* keep only the last 16 bits of the 32 bit calculated sum and add the carries */ + while (sum >> 16) + sum = (sum & 0xFFFF) + (sum >> 16); + + /* Take the one's complement of sum */ + return ((uint16_t) ~sum); +} + +static uint16_t +ip_checksum(const void *buf, uint16_t hdr_len) { + unsigned long sum = 0; + const uint16_t *ip1; + + ip1 = buf; + while (hdr_len > 1) { + sum += *ip1++; + if (sum & 0x80000000) + sum = (sum & 0xFFFF) + (sum >> 16); + hdr_len -= 2; + } + + while (sum >> 16) + sum = (sum & 0xFFFF) + (sum >> 16); + + return(~sum); +} + +static int +client_nat_ftp_transform(struct buffer *ipbuf, + const int direction, const uint32_t from_address, uint32_t replace_address) { + + uint32_t array[6] = {0}; + int ret = 0; + int data_len = 0; + + struct ip_tcp_udp_hdr *hdr = (struct ip_tcp_udp_hdr *) BPTR (ipbuf); + + if (hdr->ip.protocol != OPENVPN_IPPROTO_TCP) + return ret; + + if (table == NULL) + table = init_delta_table(); + + delta_table_entry_t *entry = NULL; + delta_table_key_t delta_key; + + // Because the address already was NATed, probably on OpenVPN Server + if (direction == CN_OUTGOING) + delta_key.src_addr = from_address; + else if (direction == CN_INCOMING) + delta_key.src_addr = hdr->ip.saddr; + + delta_key.src_port = hdr->u.tcp.source; + delta_key.dest_addr = hdr->ip.daddr; + delta_key.dest_port = hdr->u.tcp.dest; + +#if DEBUG_DELTA + msg (M_INFO, "delta_key - saddr: %d, sport: %d, daddr: %d, dport: %d", + delta_key.src_addr, delta_key.src_port, delta_key.dest_addr, delta_key.dest_port); + + msg (M_INFO, "seq: %x, ack: %x", + hdr->u.tcp.seq, hdr->u.tcp.ack_seq); +#endif + + int tcp_data_offset = OPENVPN_TCPH_GET_DOFF (hdr->u.tcp.doff_res); + + struct buffer tcpbuf = *ipbuf; + if (!buf_advance (&tcpbuf, sizeof(struct openvpn_iphdr) + tcp_data_offset)) + return ret; + + uint8_t * tcp_data = (uint8_t *) BPTR (&tcpbuf); + +#if DEBUG_DELTA + msg (M_INFO, "TCP DATA BEFORE NAT:"); + hexdump(tcp_data, BLEN(&tcpbuf)); +#endif + + u_int32_t matchlen, matchoff; + int found_pattern = -1; + int i = 0; + + for (i = 0; i < sizeof(search) / sizeof(search[0]); i++) { + if (search[i].direction != direction) continue; + + found_pattern = find_pattern(tcp_data, BLEN(&tcpbuf), + search[i].pattern, + search[i].plen, + search[i].skip, + search[i].term, + &matchoff, &matchlen, + array, + search[i].getnum); + if (found_pattern) break; + } + + if (found_pattern > 0) + { + + if (check_debug_level (D_CLIENT_NAT)) + { + msg (M_INFO, "client-nat FTP match: %.*s - data: %.*s\n", + matchoff, tcp_data, matchlen, &tcp_data[matchoff]); + } + + data_len = BLEN(&tcpbuf) - matchoff; + + uint32_t ip = htonl((array[0] << 24) | (array[1] << 16) + | (array[2] << 8) | array[3]); + uint16_t port = htons(array[4] << 8 | array[5]); + uint8_t * addr_tmp = (uint8_t *) &replace_address; + + uint8_t new_tcp_data[32]; + memset(&new_tcp_data[0], 0, sizeof(new_tcp_data)); + + int new_len = sprintf((char *) &new_tcp_data[0], "%d,%d,%d,%d,%d,%d%.*s", + addr_tmp[0], addr_tmp[1], addr_tmp[2], addr_tmp[3], array[4], array[5], + data_len - matchlen, (char *)&tcp_data[matchoff + matchlen]); + + if (check_debug_level (D_CLIENT_NAT)) + { + msg (M_INFO, "client-nat replaced address from: %.*s to: %.*s\n", + data_len, &tcp_data[matchoff], new_len, &new_tcp_data[0]); + } + + //If the new len is greater than the old, there will be there an adjustment + if (new_len > data_len) + { + //If the entry already exists + if (entry = get_delta_entry(table, delta_key)) + { + if (direction == CN_OUTGOING) + { + hdr->u.tcp.seq = htonl(ntohl(hdr->u.tcp.seq) + (entry->delta_out)); // Need to update seq and ack + hdr->u.tcp.ack_seq = htonl(ntohl(hdr->u.tcp.ack_seq) - entry->delta_in); + entry->delta_out += new_len - data_len; + } + else + { + hdr->u.tcp.seq = htonl(ntohl(hdr->u.tcp.seq) + (entry->delta_in)); // Need to update seq and ack + hdr->u.tcp.ack_seq = htonl(ntohl(hdr->u.tcp.ack_seq) - entry->delta_out); + entry->delta_in += new_len - data_len; + } + //Update timestamp entry on delta entry, to keep this delta alive + entry->timestamp = get_timestamp(); + } + else + { + // No entry found. Add a new one. + entry = malloc(sizeof(delta_table_entry_t)); + entry->key = delta_key; + + if (direction == CN_OUTGOING) + { + entry->delta_out = new_len - data_len; + entry->delta_in = 0; + } + else + { + entry->delta_in = new_len - data_len; + entry->delta_out = 0; + } + entry->marked_to_remove = 0; + entry->timestamp = get_timestamp(); + + //Add it to delta table + add_delta_entry(table, entry); + } + +#if DEBUG_DELTA + print_delta_table_entries(table); +#endif + + //Increase the tcpbuf and ipbuf reflecting the delta chars added to the segment. + ASSERT(buf_inc_len(&tcpbuf, new_len - data_len)); + ASSERT(buf_inc_len(ipbuf, new_len - data_len)); + + //Update tot_len + hdr->ip.tot_len = htons(ntohs(hdr->ip.tot_len) + (new_len - data_len)); + + // Readjust IP Checksum + uint16_t head_len = OPENVPN_IPH_GET_LEN(hdr->ip.version_len); + hdr->ip.check = 0; + uint16_t check = ip_checksum( BPTR (ipbuf), head_len); + hdr->ip.check = check; + + //Use new_len here! + memcpy(&tcp_data[matchoff], new_tcp_data, new_len); + } + else + { + // The replace size is the lesser or iqual the original? Pad with 0 if necessary. + memcpy(&tcp_data[matchoff], new_tcp_data, data_len); + + //If the entry already exists + if (entry = get_delta_entry(table, delta_key)) + { + if (direction == CN_OUTGOING) + { + hdr->u.tcp.seq = htonl(ntohl(hdr->u.tcp.seq) + (entry->delta_out)); // Need to update seq and ack + hdr->u.tcp.ack_seq = htonl(ntohl(hdr->u.tcp.ack_seq) - entry->delta_in); + } + else + { + hdr->u.tcp.seq = htonl(ntohl(hdr->u.tcp.seq) + (entry->delta_in)); // Need to update seq and ack + hdr->u.tcp.ack_seq = htonl(ntohl(hdr->u.tcp.ack_seq) - entry->delta_out); + } + //Update timestamp entry on delta entry, to keep this delta alive + entry->timestamp = get_timestamp(); + } + + } + + //Update TCP checksum + hdr->u.tcp.check = 0; + + uint16_t tot_len = ntohs(hdr->ip.tot_len); + uint16_t head_len = OPENVPN_IPH_GET_LEN(hdr->ip.version_len); + + uint16_t check = tcp_checksum(BPTR (ipbuf) + head_len, + tot_len - head_len, hdr->ip.saddr, hdr->ip.daddr); + + hdr->u.tcp.check = htons(check); + ret = 1; + } + else + { + //No pattern found. Check if there is a delta entry for this connection + if(entry = get_delta_entry(table, delta_key)) + { + if (direction == CN_OUTGOING) + { + hdr->u.tcp.seq = htonl(ntohl(hdr->u.tcp.seq) + entry->delta_out); + hdr->u.tcp.ack_seq = htonl(ntohl(hdr->u.tcp.ack_seq) - entry->delta_in); + } + else + { + hdr->u.tcp.seq = htonl(ntohl(hdr->u.tcp.seq) + entry->delta_in); + hdr->u.tcp.ack_seq = htonl(ntohl(hdr->u.tcp.ack_seq) - entry->delta_out); + } + + //Update TCP checksum + hdr->u.tcp.check = 0; + + uint16_t tot_len = ntohs(hdr->ip.tot_len); + uint16_t head_len = OPENVPN_IPH_GET_LEN(hdr->ip.version_len); + + uint16_t check = tcp_checksum(BPTR (ipbuf) + head_len, + tot_len - head_len, hdr->ip.saddr, hdr->ip.daddr); + + hdr->u.tcp.check = htons(check); + + //Update timestamp entry on delta entry + entry->timestamp = get_timestamp(); + + // If it is a FIN package, then remove entry from delta table + if (OPENVPN_TCPH_FIN_MASK & hdr->u.tcp.flags) + { + entry->marked_to_remove = 1; + msg (M_INFO, "Delta marked to be removed!"); + } + + ret = 1; + } + else + { +#if DEBUG_DELTA + // Regular package! + msg (M_INFO, "Regular package!: tot_len %d, seq %x, ack %x\n", ntohs(hdr->ip.tot_len), ntohl(hdr->u.tcp.seq), ntohl(hdr->u.tcp.ack_seq)); +#endif + } + } + +#if DEBUG_DELTA + msg (M_INFO, "TCP DATA AFTER NAT:"); + hexdump(tcp_data, BLEN(&tcpbuf)); +#endif + + return ret; +} + void client_nat_transform (const struct client_nat_option_list *list, - struct buffer *ipbuf, - const int direction) + struct buffer *ipbuf, + const int direction, + const bool enable_nat_ftp_support) { struct ip_tcp_udp_hdr *h = (struct ip_tcp_udp_hdr *) BPTR (ipbuf); int i; - uint32_t addr, *addr_ptr; + uint32_t addr, *addr_ptr, from_addr; const uint32_t *from, *to; int accumulate = 0; unsigned int amask; unsigned int alog = 0; + uint32_t orig_saddr = h->ip.saddr; + uint32_t orig_daddr = h->ip.daddr; + if (check_debug_level (D_CLIENT_NAT)) print_pkt (&h->ip, "BEFORE", direction, D_CLIENT_NAT); @@ -212,65 +879,81 @@ client_nat_transform (const struct client_nat_option_list *list, { const struct client_nat_entry *e = &list->entries[i]; /* current NAT rule */ if (e->type ^ direction) - { - addr = *(addr_ptr = &h->ip.daddr); - amask = 2; - } + { + addr = *(addr_ptr = &h->ip.daddr); + amask = 2; + } else - { - addr = *(addr_ptr = &h->ip.saddr); - amask = 1; - } + { + addr = *(addr_ptr = &h->ip.saddr); + amask = 1; + } if (direction) - { - from = &e->foreign_network; - to = &e->network; - } + { + from = &e->foreign_network; + to = &e->network; + } else - { - from = &e->network; - to = &e->foreign_network; - } + { + from = &e->network; + to = &e->foreign_network; + } if (((addr & e->netmask) == *from) && !(amask & alog)) - { - /* pre-adjust IP checksum */ - ADD_CHECKSUM_32(accumulate, addr); + { + from_addr = *from; + + /* pre-adjust IP checksum */ + ADD_CHECKSUM_32(accumulate, addr); - /* do NAT transform */ - addr = (addr & ~e->netmask) | *to; + /* do NAT transform */ + addr = (addr & ~e->netmask) | *to; - /* post-adjust IP checksum */ - SUB_CHECKSUM_32(accumulate, addr); + /* post-adjust IP checksum */ + SUB_CHECKSUM_32(accumulate, addr); - /* write the modified address to packet */ - *addr_ptr = addr; + /* write the modified address to packet */ + *addr_ptr = addr; - /* mark as modified */ - alog |= amask; - } + /* mark as modified */ + alog |= amask; + } } if (alog) { if (check_debug_level (D_CLIENT_NAT)) - print_pkt (&h->ip, "AFTER", direction, D_CLIENT_NAT); + print_pkt (&h->ip, "AFTER", direction, D_CLIENT_NAT); ADJUST_CHECKSUM(accumulate, h->ip.check); if (h->ip.protocol == OPENVPN_IPPROTO_TCP) - { - if (BLEN(ipbuf) >= sizeof(struct openvpn_iphdr) + sizeof(struct openvpn_tcphdr)) - { - ADJUST_CHECKSUM(accumulate, h->u.tcp.check); - } - } + { + if (BLEN(ipbuf) >= sizeof(struct openvpn_iphdr) + sizeof(struct openvpn_tcphdr)) + { + ADJUST_CHECKSUM(accumulate, h->u.tcp.check); + } + + uint32_t repl_addr = addr; + + if (amask == 2) + { + if (direction) + repl_addr = orig_saddr; + else + repl_addr = orig_daddr; + } + + if (enable_nat_ftp_support) + client_nat_ftp_transform(ipbuf, direction, from_addr, repl_addr); + + } else if (h->ip.protocol == OPENVPN_IPPROTO_UDP) - { - if (BLEN(ipbuf) >= sizeof(struct openvpn_iphdr) + sizeof(struct openvpn_udphdr)) - { - ADJUST_CHECKSUM(accumulate, h->u.udp.check); - } - } + { + if (BLEN(ipbuf) >= sizeof(struct openvpn_iphdr) + sizeof(struct openvpn_udphdr)) + { + ADJUST_CHECKSUM(accumulate, h->u.udp.check); + } + } } } @@ -297,7 +980,7 @@ update_localhost_nat(struct client_nat_option_list *dest, in_addr_t local_ip) addr.s_addr = nat_entry->network; char *dot_ip = inet_ntoa(addr); - msg (M_INFO, "Updating NAT table from localhost to: %s", dot_ip); + msg (M_INFO, "CNAT - Updating NAT table from localhost to: %s", dot_ip); ret = true; } } diff --git a/src/openvpn/clinat.h b/src/openvpn/clinat.h index ce995d9..a58da5d 100755 --- a/src/openvpn/clinat.h +++ b/src/openvpn/clinat.h @@ -52,15 +52,16 @@ void copy_client_nat_option_list (struct client_nat_option_list *dest, const str void print_client_nat_list(const struct client_nat_option_list *list, int msglevel); void add_client_nat_to_option_list (struct client_nat_option_list *dest, - const char *type, - const char *network, - const char *netmask, - const char *foreign_network, - int msglevel); + const char *type, + const char *network, + const char *netmask, + const char *foreign_network, + int msglevel); void client_nat_transform (const struct client_nat_option_list *list, - struct buffer *ipbuf, - const int direction); + struct buffer *ipbuf, + const int direction, + const bool enable_nat_ftp_support); bool update_localhost_nat(struct client_nat_option_list *dest, in_addr_t local_ip); diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c old mode 100644 new mode 100755 index 217fbb3..615db7a --- a/src/openvpn/forward.c +++ b/src/openvpn/forward.c @@ -980,9 +980,6 @@ process_incoming_tun (struct context *c) perf_push (PERF_PROC_IN_TUN); - if (c->c2.buf.len > 0) - c->c2.tun_read_bytes += c->c2.buf.len; - #ifdef LOG_RW if (c->c2.log_rw && c->c2.buf.len > 0) fprintf (stderr, "r"); @@ -999,6 +996,9 @@ process_incoming_tun (struct context *c) */ process_ip_header (c, PIPV4_PASSTOS|PIP_MSSFIX|PIPV4_CLIENT_NAT, &c->c2.buf); + if (c->c2.buf.len > 0) + c->c2.tun_read_bytes += c->c2.buf.len; + #ifdef PACKET_TRUNCATION_CHECK /* if (c->c2.buf.len > 1) --c->c2.buf.len; */ ipv4_packet_size_verify (BPTR (&c->c2.buf), @@ -1041,43 +1041,44 @@ process_ip_header (struct context *c, unsigned int flags, struct buffer *buf) #else if (flags & PIP_MSSFIX) #endif - { - struct buffer ipbuf = *buf; - if (is_ipv4 (TUNNEL_TYPE (c->c1.tuntap), &ipbuf)) - { -#if PASSTOS_CAPABILITY - /* extract TOS from IP header */ - if (flags & PIPV4_PASSTOS) - link_socket_extract_tos (c->c2.link_socket, &ipbuf); -#endif - - /* possibly alter the TCP MSS */ - if (flags & PIP_MSSFIX) - mss_fixup_ipv4 (&ipbuf, MTU_TO_MSS (TUN_MTU_SIZE_DYNAMIC (&c->c2.frame))); + { + if (is_ipv4 (TUNNEL_TYPE (c->c1.tuntap), buf)) + { #ifdef ENABLE_CLIENT_NAT - /* possibly do NAT on packet */ - if ((flags & PIPV4_CLIENT_NAT) && c->options.client_nat) - { - const int direction = (flags & PIPV4_OUTGOING) ? CN_INCOMING : CN_OUTGOING; - client_nat_transform (c->options.client_nat, &ipbuf, direction); - } + /* possibly do NAT on packet */ + if ((flags & PIPV4_CLIENT_NAT) && c->options.client_nat) + { + const int direction = (flags & PIPV4_OUTGOING) ? CN_INCOMING : CN_OUTGOING; + client_nat_transform(c->options.client_nat, buf, direction, c->options.enable_nat_ftp_support); + } #endif - /* possibly extract a DHCP router message */ - if (flags & PIPV4_EXTRACT_DHCP_ROUTER) - { - const in_addr_t dhcp_router = dhcp_extract_router_msg (&ipbuf); - if (dhcp_router) - route_list_add_vpn_gateway (c->c1.route_list, c->c2.es, dhcp_router); - } - } - else if (is_ipv6 (TUNNEL_TYPE (c->c1.tuntap), &ipbuf)) - { - /* possibly alter the TCP MSS */ - if (flags & PIP_MSSFIX) - mss_fixup_ipv6 (&ipbuf, MTU_TO_MSS (TUN_MTU_SIZE_DYNAMIC (&c->c2.frame))); - } - } + +#if PASSTOS_CAPABILITY + /* extract TOS from IP header */ + if (flags & PIPV4_PASSTOS) + link_socket_extract_tos (c->c2.link_socket, buf); +#endif + + /* possibly alter the TCP MSS */ + if (flags & PIP_MSSFIX) + mss_fixup_ipv4 (buf, MTU_TO_MSS (TUN_MTU_SIZE_DYNAMIC (&c->c2.frame))); + + /* possibly extract a DHCP router message */ + if (flags & PIPV4_EXTRACT_DHCP_ROUTER) + { + const in_addr_t dhcp_router = dhcp_extract_router_msg (buf); + if (dhcp_router) + route_list_add_vpn_gateway (c->c1.route_list, c->c2.es, dhcp_router); + } + } + else if (is_ipv6 (TUNNEL_TYPE (c->c1.tuntap), buf)) + { + /* possibly alter the TCP MSS */ + if (flags & PIP_MSSFIX) + mss_fixup_ipv6 (buf, MTU_TO_MSS (TUN_MTU_SIZE_DYNAMIC (&c->c2.frame))); + } + } } } diff --git a/src/openvpn/options.c b/src/openvpn/options.c old mode 100644 new mode 100755 index 007bd8c..73b615a --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -236,6 +236,7 @@ static const char usage_message[] = " the default gateway. Useful when pushing private subnets.\n" #ifdef ENABLE_CLIENT_NAT "--client-nat snat|dnat network netmask alias : on client add 1-to-1 NAT rule.\n" + "--enable-nat-ftp-support : Enable NAT FTP Support, updating the IP address on FTP PORT commands or PASV responses\n" #endif #ifdef ENABLE_PUSH_PEER_INFO "--push-peer-info : (client only) push client info to server.\n" @@ -789,6 +790,11 @@ init_options (struct options *o, const bool init_gc) o->max_routes = MAX_ROUTES_DEFAULT; o->resolve_retry_seconds = RESOLV_RETRY_INFINITE; o->proto_force = -1; + +#ifdef ENABLE_CLIENT_NAT + o->enable_nat_ftp_support = false; +#endif + #ifdef ENABLE_OCC o->occ = true; #endif @@ -1535,6 +1541,8 @@ show_settings (const struct options *o) #ifdef ENABLE_CLIENT_NAT if (o->client_nat) print_client_nat_list(o->client_nat, D_SHOW_PARMS); + + SHOW_BOOL(enable_nat_ftp_support); #endif #ifdef ENABLE_MANAGEMENT @@ -5287,6 +5295,10 @@ add_option (struct options *options, cnol_check_alloc (options); add_client_nat_to_option_list(options->client_nat, p[1], p[2], p[3], p[4], msglevel); } + else if (streq (p[0], "enable_nat_ftp_support")) + { + options->enable_nat_ftp_support = true; + } #endif else if (streq (p[0], "route") && p[1]) { diff --git a/src/openvpn/options.h b/src/openvpn/options.h old mode 100644 new mode 100755 index af9a47f..2b27368 --- a/src/openvpn/options.h +++ b/src/openvpn/options.h @@ -347,6 +347,7 @@ struct options bool allow_pull_fqdn; /* as a client, allow server to push a FQDN for certain parameters */ #ifdef ENABLE_CLIENT_NAT + bool enable_nat_ftp_support; struct client_nat_option_list *client_nat; #endif -- 1.7.9.5 >From 0aa64f57ecf653cd067dbef2b2378bdfc4f9a754 Mon Sep 17 00:00:00 2001 From: Rafael Gava <rafael.olive...@venturus.org.br> List-Post: openvpn-devel@lists.sourceforge.net Date: Fri, 14 Aug 2015 16:46:13 -0300 Subject: [PATCH] Removed debug messages. Signed-off-by: Rafael Gava <rafael.olive...@venturus.org.br> --- src/openvpn/clinat.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/openvpn/clinat.c b/src/openvpn/clinat.c index 1d213f5..991ea75 100755 --- a/src/openvpn/clinat.c +++ b/src/openvpn/clinat.c @@ -833,7 +833,9 @@ client_nat_ftp_transform(struct buffer *ipbuf, if (OPENVPN_TCPH_FIN_MASK & hdr->u.tcp.flags) { entry->marked_to_remove = 1; +#if DEBUG_DELTA msg (M_INFO, "Delta marked to be removed!"); +#endif } ret = 1; -- 1.7.9.5 >From cf7af4e4784c83a64b8c1e12601d33bdc5b14d59 Mon Sep 17 00:00:00 2001 From: Rafael Gava <rafael.olive...@venturus.org.br> List-Post: openvpn-devel@lists.sourceforge.net Date: Thu, 20 Aug 2015 08:47:08 -0300 Subject: [PATCH] Fixed FTP NAT IP address. Signed-off-by: Rafael Gava <rafael.olive...@venturus.org.br> --- src/openvpn/clinat.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/openvpn/clinat.c b/src/openvpn/clinat.c index 991ea75..ef400de 100755 --- a/src/openvpn/clinat.c +++ b/src/openvpn/clinat.c @@ -423,7 +423,7 @@ static int try_number(const char *data, size_t dlen, uint32_t array[], if (*data == term && i == array_size - 1) return len; - msg (M_ERRNO, "CNAT - try_number - Char %u (got %u nums) `%u' unexpected\n", len, i, *data); + msg (M_ERRNO, "CNAT - try_number - Char %u (got %u nums) '%u' unexpected\n", len, i, *data); return 0; } } @@ -433,7 +433,7 @@ static int try_number(const char *data, size_t dlen, uint32_t array[], return 0; } -static int try_rfc959(const char *, size_t, u_int32_t [], char); +static int try_rfc959(const char *, size_t, uint32_t [], char); static struct ftp_search { int direction; @@ -441,7 +441,7 @@ static struct ftp_search { size_t plen; char skip; char term; - int (*getnum)(const char *, size_t, u_int32_t[], char); + int (*getnum)(const char *, size_t, uint32_t[], char); } search[] = { { CN_OUTGOING, @@ -458,7 +458,7 @@ static struct ftp_search { /* Returns 0, or length of numbers: 192,168,1,1,5,6 */ static int -try_rfc959(const char *data, size_t dlen, u_int32_t array[6], +try_rfc959(const char *data, size_t dlen, uint32_t array[6], char term) { return try_number(data, dlen, array, 6, ',', term); @@ -467,9 +467,9 @@ try_rfc959(const char *data, size_t dlen, u_int32_t array[6], /* Grab port: number up to delimiter */ static int get_port(const char *data, int start, size_t dlen, char delim, - u_int32_t array[2]) + uint32_t array[2]) { - u_int16_t port = 0; + uint16_t port = 0; int i; for (i = start; i < dlen; i++) { @@ -496,13 +496,13 @@ find_pattern(const char *data, size_t dlen, char skip, char term, unsigned int *numoff, unsigned int *numlen, - u_int32_t array[6], - int (*getnum)(const char *, size_t, u_int32_t[], char)) + uint32_t array[6], + int (*getnum)(const char *, size_t, uint32_t[], char)) { size_t i; if (check_debug_level (D_CLIENT_NAT)) - msg (M_INFO, "CNAT - find_pattern %s: dlen = %u\n", pattern, dlen); + msg (M_INFO, "CNAT - find_pattern %s: dlen = %u\n", pattern, (uint32_t) dlen); if (dlen == 0) return 0; @@ -530,7 +530,7 @@ find_pattern(const char *data, size_t dlen, i++; if (check_debug_level (D_CLIENT_NAT)) - msg (M_INFO, "CNAT - Skipped up to `%c'!\n", skip); + msg (M_INFO, "CNAT - Skipped up to '%c' (%d)!\n", skip, skip); *numoff = i; *numlen = getnum(data + i, dlen - i, array, term); @@ -652,7 +652,7 @@ client_nat_ftp_transform(struct buffer *ipbuf, hexdump(tcp_data, BLEN(&tcpbuf)); #endif - u_int32_t matchlen, matchoff; + uint32_t matchlen, matchoff; int found_pattern = -1; int i = 0; @@ -689,7 +689,7 @@ client_nat_ftp_transform(struct buffer *ipbuf, uint8_t new_tcp_data[32]; memset(&new_tcp_data[0], 0, sizeof(new_tcp_data)); - int new_len = sprintf((char *) &new_tcp_data[0], "%d,%d,%d,%d,%d,%d%.*s", + int new_len = sprintf((char *) &new_tcp_data[0], "%03d,%03d,%03d,%03d,%03d,%03d%.*s", addr_tmp[0], addr_tmp[1], addr_tmp[2], addr_tmp[3], array[4], array[5], data_len - matchlen, (char *)&tcp_data[matchoff + matchlen]); @@ -700,7 +700,7 @@ client_nat_ftp_transform(struct buffer *ipbuf, } //If the new len is greater than the old, there will be there an adjustment - if (new_len > data_len) + if (replace_address != ip && new_len > data_len) { //If the entry already exists if (entry = get_delta_entry(table, delta_key)) @@ -765,8 +765,8 @@ client_nat_ftp_transform(struct buffer *ipbuf, } else { - // The replace size is the lesser or iqual the original? Pad with 0 if necessary. - memcpy(&tcp_data[matchoff], new_tcp_data, data_len); + if (replace_address != ip) + memcpy(&tcp_data[matchoff], new_tcp_data, data_len); //If the entry already exists if (entry = get_delta_entry(table, delta_key)) -- 1.7.9.5 >From 63b1ddad1628eb1a179c2bf5010b781284d2bc4f Mon Sep 17 00:00:00 2001 From: Rafael Gava <rafael.olive...@venturus.org.br> List-Post: openvpn-devel@lists.sourceforge.net Date: Sat, 15 Aug 2015 16:58:45 -0300 Subject: [PATCH] Fixed options parameter name enable-nat-ftp-support. Signed-off-by: Rafael Gava <rafael.olive...@venturus.org.br> --- src/openvpn/options.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openvpn/options.c b/src/openvpn/options.c index 73b615a..1a012e4 100755 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -5295,7 +5295,7 @@ add_option (struct options *options, cnol_check_alloc (options); add_client_nat_to_option_list(options->client_nat, p[1], p[2], p[3], p[4], msglevel); } - else if (streq (p[0], "enable_nat_ftp_support")) + else if (streq (p[0], "enable-nat-ftp-support")) { options->enable_nat_ftp_support = true; } -- 1.7.9.5 >From 2f5ac96c0c2c5d9d06a11e0b08ae958e09c2e9b5 Mon Sep 17 00:00:00 2001 From: Rafael Gava <rafael.olive...@venturus.org.br> List-Post: openvpn-devel@lists.sourceforge.net Date: Mon, 24 Aug 2015 09:15:34 -0300 Subject: [PATCH] Allows set yes/no option to enable-nat-ftp-support feature. Signed-off-by: Rafael Gava <rafael.olive...@venturus.org.br> --- src/openvpn/options.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/openvpn/options.c b/src/openvpn/options.c index 1a012e4..4d73d87 100755 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -236,7 +236,9 @@ static const char usage_message[] = " the default gateway. Useful when pushing private subnets.\n" #ifdef ENABLE_CLIENT_NAT "--client-nat snat|dnat network netmask alias : on client add 1-to-1 NAT rule.\n" - "--enable-nat-ftp-support : Enable NAT FTP Support, updating the IP address on FTP PORT commands or PASV responses\n" + "--enable-nat-ftp-support : Enable NAT FTP Support, replacing the IP address on FTP PORT commands or PASV responses?\n" + " 'no' -- No, disable this feature.\n" + " 'yes' -- Yes, enable this feature (Enabled by default on Windows).\n" #endif #ifdef ENABLE_PUSH_PEER_INFO "--push-peer-info : (client only) push client info to server.\n" @@ -792,8 +794,12 @@ init_options (struct options *o, const bool init_gc) o->proto_force = -1; #ifdef ENABLE_CLIENT_NAT +#ifdef WIN32 + o->enable_nat_ftp_support = true; +#else o->enable_nat_ftp_support = false; #endif +#endif #ifdef ENABLE_OCC o->occ = true; @@ -5297,7 +5303,18 @@ add_option (struct options *options, } else if (streq (p[0], "enable-nat-ftp-support")) { - options->enable_nat_ftp_support = true; + if (p[1]) + { + if (streq (p[1], "yes")) + options->enable_nat_ftp_support = true; + else if (streq (p[1], "no")) + options->enable_nat_ftp_support = false; + else + { + msg (msglevel, "bad enable-nat-ftp-support option: %s -- must be 'yes' or 'no'", p[1]); + goto err; + } + } } #endif else if (streq (p[0], "route") && p[1]) -- 1.7.9.5