You have been requested to review the proposed merge of lp:~diego-biurrun/hipl/hipfw-performance into lp:hipl. This is Christoph's branch updated to current trunk. I haven't reviewed it in detail yet, just fixed a few things I noticed right away. This still needs careful review and testing. Below is the original merge proposal text from Christoph: This can be merged as soon as the following features have been confirmed still to work. Acceleration via iptables: - "normal" ESP traffic, of course - nat via udp 10500 (supported, with all of the below) - userspace_ipsec (don't accelerate anything if enabled) - relay (excluded from iptables) - LSI (excluded from iptables) - opp-mode (supported) - midauth (supported) - esp_prot tokens (excluded from iptables) - CLOSE and CLOSE_ACK packets Timeout: Normal and accelerated connections. Keep in mind that the timout interval can be changed via -t (usage text was updated accordingly) and is very short in debug builds. -- https://code.launchpad.net/~diego-biurrun/hipl/hipfw-performance/+merge/43568 Your team HIPL core team is requested to review the proposed merge of lp:~diego-biurrun/hipl/hipfw-performance into lp:hipl.
=== modified file 'firewall/conntrack.c' --- firewall/conntrack.c 2010-12-13 19:09:27 +0000 +++ firewall/conntrack.c 2010-12-13 21:28:35 +0000 @@ -40,7 +40,9 @@ #define _BSD_SOURCE #include <errno.h> +#include <limits.h> #include <stdint.h> +#include <stdbool.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> @@ -50,6 +52,7 @@ #include <openssl/dsa.h> #include <openssl/rsa.h> #include <sys/time.h> +#include <linux/netfilter_ipv4.h> #include "lib/core/builder.h" #include "lib/core/debug.h" @@ -73,9 +76,40 @@ #include "config.h" #include "reinject.h" - -struct dlist *hip_list = NULL; -struct dlist *esp_list = NULL; +#ifdef CONFIG_HIP_DEBUG +// this improves our chances of finding bugs in the timeout code +#define DEFAULT_CONNECTION_TIMEOUT 20; // 20 seconds +#define DEFAULT_CLEANUP_INTERVAL 10; // 10 seconds +#else +#define DEFAULT_CONNECTION_TIMEOUT (60 * 5); // 5 minutes +#define DEFAULT_CLEANUP_INTERVAL (60 * 60); // 1 minute +#endif + +/** + * Interval between sweeps in hip_fw_conntrack_periodic_cleanup(), + * in Seconds. + * + * @see hip_fw_conntrack_periodic_cleanup() + */ +static const time_t cleanup_interval = DEFAULT_CLEANUP_INTERVAL; + +/** + * Connection timeout, in seconds. + * Disabled if zero. This actually specifies the minimum period of + * inactivity before a connection is considered stale. + * Thus, a connection may be inactive for at most @c connection_timeout + * plus @c cleanup_interval seconds. + * + * @see hip_fw_conntrack_periodic_cleanup() + */ +time_t connection_timeout = DEFAULT_CONNECTION_TIMEOUT; + +/** + * @todo: singly-linked may suffice. + * */ +static struct dlist *hip_list = NULL; +static struct dlist *esp_list = NULL; +static struct slist *conn_list = NULL; enum { STATE_NEW, @@ -84,8 +118,7 @@ STATE_CLOSING }; -int timeoutChecking = 0; -unsigned long timeoutValue = 0; +static unsigned int total_esp_rules_count = 0; /*------------print functions-------------*/ /** @@ -116,7 +149,7 @@ * * @param hiptuple HIP tuple */ -static void print_tuple(const struct hip_tuple *hiptuple) +static void print_tuple(DBG const struct hip_tuple *hiptuple) { HIP_DEBUG("next tuple: \n"); HIP_DEBUG("direction: %i\n", hiptuple->tuple->direction); @@ -319,23 +352,147 @@ } /** - * Insert an address into a list of addresses. If same address exists already, - * the update_id is replaced with the new value. - * - * @param addr_list the address list - * @param addr the address to be added - * @param upd_id update id - * - * @return the address list - */ -static struct slist *update_esp_address(struct slist *addr_list, - const struct in6_addr *addr, - const uint32_t *upd_id) -{ - struct esp_address *esp_addr = get_esp_address(addr_list, addr); + * Forward this spi/dest combo via iptables rather than userspace + * dispatching, or delete the corresponding rules. + * + * @param esp_tuple Extract SPI and connection info from this tuple. + * @param dest The newly added address. + * @param insert Remove rule if false, add it otherwise. + * @return 0 on success, -1 otherwise. + * + * @todo Test rules using userspace_ipsec, Relay, LSI, sys-opp, midauth, + * light-update and esp_prot configurations. + * @todo Test with different byte ordering. + * + * @see update_esp_address + * @see free_esp_tuple + */ +static int hip_fw_manage_esp_rule(const struct esp_tuple *esp_tuple, + const struct in6_addr *dest, bool insert) +{ + int err = 0; + const char *flag = insert ? "-I" : "-D"; + const char *table = NULL; + + if (hip_userspace_ipsec || prefer_userspace) { + return 0; + } + + HIP_ASSERT(esp_tuple); + HIP_ASSERT(dest); + + if (esp_tuple->esp_prot_tfm > ESP_PROT_TFM_UNUSED) { + HIP_DEBUG("ESP Transforms requested; not handled via iptables " + "since we need to inspect packets\n"); + return 0; + } + + if (esp_tuple->tuple->esp_relay) { + HIP_DEBUG("ESP Relay requested; not handled via iptables " + "since we need packet rewriting\n"); + return 0; + } + + switch (esp_tuple->tuple->hook) { + case NF_IP_LOCAL_IN: + table = "HIPFW-INPUT"; + break; + case NF_IP_FORWARD: + table = "HIPFW-FORWARD"; + break; + case NF_IP_LOCAL_OUT: + table = "HIPFW-OUT"; + break; + default: + HIP_ERROR("Packet was received via unsupported netfilter hook %d\n", + esp_tuple->tuple->hook); + err = 1; + goto out_err; + } + + HIP_DEBUG("insert = %d\n", insert); + HIP_DEBUG("table = %s\n", table); + HIP_DEBUG("esp_tuple->spi = 0x%08X\n", esp_tuple->spi); + HIP_DEBUG_IN6ADDR("dest ip", dest); + + if (IN6_IS_ADDR_V4MAPPED(dest)) { + /* sizeof(4*3 digits + 3 dots + \0) = 16 */ + char daddr[16]; + struct in_addr dest4; + + IPV6_TO_IPV4_MAP(dest, &dest4); + HIP_IFEL(!inet_ntop(AF_INET, &dest4, daddr, sizeof(daddr)), -1, + "inet_ntop: %s\n", strerror(errno)); + + if (esp_tuple->tuple->connection->udp_encap) { + + /* SPI is the first 32bit value in encapsulating UDP payload, so + * we may use a simple u32 Pattern. Here, '4&0x1FFF=0' ensures + * we're not processing a fragmented packet. + */ + err = system_printf("iptables %s %s -p UDP " + "--dport 10500 --sport 10500 -d %s -m u32 " + "--u32 '4&0x1FFF=0 && 0>>22&0x3C@8=0x%08X' -j ACCEPT", + flag, table, daddr, esp_tuple->spi); + } else { + err = system_printf("iptables %s %s -p 50 " + "-d %s -m esp --espspi 0x%08X -j ACCEPT", + flag, table, daddr, esp_tuple->spi); + } + + } else { + char daddr[51]; + HIP_IFEL(!inet_ntop(AF_INET6, &dest, daddr, sizeof(daddr)), -1, + "inet_ntop: %s\n", strerror(errno)); + + HIP_ASSERT(!esp_tuple->tuple->connection->udp_encap); + err = system_printf("ip6tables %s %s -p 50 " + "-d %s -m esp --espspi 0x%08X -j ACCEPT", + flag, table, daddr, esp_tuple->spi); + } + +out_err: + if (err == EXIT_SUCCESS) { + total_esp_rules_count += (insert ? 1 : -1); + HIP_DEBUG("total_esp_rules_count = %d\n", total_esp_rules_count); + } + return err; +} + +/** + * Forward all spi/dest combos associated with @a esp_tuple via iptables, + * or delete the corresponding rules. + * + * @param esp_tuple Extract SPI and connection info from this tuple. + * @param insert remove all rules if zero, add otherwise. + * + * @see hip_fw_manage_esp_rule + */ +void hip_fw_manage_esp_tuple(const struct esp_tuple *esp_tuple, bool insert) +{ + struct slist *lst = esp_tuple->dst_addr_list; + while (lst) { + hip_fw_manage_esp_rule(esp_tuple, lst->data, insert); + lst = lst->next; + } +} + +/** + * Insert a destination address into an esp_tuple. If same address exists already, + * the update_id is replaced with the new value instead. + * + * @param esp_tuple the esp tuple + * @param addr the address to be added + * @param upd_id update id + */ +static void update_esp_address(struct esp_tuple *esp_tuple, + const struct in6_addr *addr, + const uint32_t *upd_id) +{ + struct esp_address *esp_addr = get_esp_address(esp_tuple->dst_addr_list, addr); HIP_DEBUG("update_esp_address: address: %s \n", addr_to_numeric(addr)); - if (!addr_list) { + if (!esp_tuple->dst_addr_list) { HIP_DEBUG("Esp slist is empty\n"); } if (esp_addr != NULL) { @@ -346,7 +503,7 @@ *esp_addr->update_id = *upd_id; } HIP_DEBUG("update_esp_address: found and updated\n"); - return addr_list; + return; } esp_addr = malloc(sizeof(struct esp_address)); memcpy(&esp_addr->dst_addr, addr, sizeof(struct in6_addr)); @@ -356,8 +513,10 @@ } else { esp_addr->update_id = NULL; } + + esp_tuple->dst_addr_list = append_to_slist(esp_tuple->dst_addr_list, esp_addr); HIP_DEBUG("update_esp_address: addr created and added\n"); - return append_to_slist(addr_list, esp_addr); + hip_fw_manage_esp_rule(esp_tuple, addr, 1); } /** @@ -425,9 +584,11 @@ * initialize and store a new HIP/ESP connnection into the connection table * * @param data the connection-related data to be inserted + * @param ctx the context * @see remove_connection */ -static void insert_new_connection(const struct hip_data *data) +static void insert_new_connection(const struct hip_data *data, + const struct hip_fw_context *ctx) { struct connection *connection = NULL; @@ -436,9 +597,9 @@ connection = malloc(sizeof(struct connection)); memset(connection, 0, sizeof(struct connection)); - connection->state = STATE_ESTABLISHED; - //set time stamp - gettimeofday(&connection->time_stamp, NULL); + connection->state = STATE_ESTABLISHED; + connection->udp_encap = ctx->udp_encap_hdr ? 1 : 0; + connection->timestamp = time(NULL); #ifdef HIP_CONFIG_MIDAUTH connection->pisa_state = PISA_STATE_DISALLOW; #endif @@ -477,6 +638,7 @@ hip_list = append_to_list(hip_list, connection->original.hip_tuple); hip_list = append_to_list(hip_list, connection->reply.hip_tuple); HIP_DEBUG("inserting connection \n"); + conn_list = append_to_slist(conn_list, connection); } /** @@ -541,13 +703,17 @@ esp_tuple->dst_addr_list = remove_link_slist(esp_tuple->dst_addr_list, list); addr = list->data; - + hip_fw_manage_esp_rule(esp_tuple, &addr->dst_addr, 0); free(addr->update_id); free(addr); + + free(list); list = esp_tuple->dst_addr_list; } - esp_tuple->tuple = NULL; + // update global esp_tuple list + esp_list = remove_link_dlist(esp_list, + find_in_dlist(esp_list, esp_tuple)); free(esp_tuple); } } @@ -569,10 +735,6 @@ struct slist *list = tuple->esp_tuples; while (list) { - // remove esp_tuples from helper list - esp_list = remove_link_dlist(esp_list, - find_in_dlist(esp_list, list->data)); - tuple->esp_tuples = remove_link_slist(tuple->esp_tuples, list); free_esp_tuple(list->data); list->data = NULL; @@ -581,13 +743,6 @@ } tuple->esp_tuples = NULL; tuple->connection = NULL; - - // tuple was not malloced -> no free here - free(tuple->src_ip); - tuple->src_ip = NULL; - - free(tuple->dst_ip); - tuple->dst_ip = NULL; } } @@ -608,7 +763,8 @@ if (connection) { remove_tuple(&connection->original); remove_tuple(&connection->reply); - + conn_list = remove_link_slist(conn_list, + find_in_slist(conn_list, connection)); free(connection); } @@ -652,14 +808,7 @@ locator_addr = (const struct hip_locator_info_addr_item *) (locator + 1); while (n > 0) { - struct esp_address *esp_address = malloc(sizeof(struct esp_address)); - memcpy(&esp_address->dst_addr, - &locator_addr->address, - sizeof(struct in6_addr)); - esp_address->update_id = malloc(sizeof(uint32_t)); - *esp_address->update_id = seq->update_id; - new_esp->dst_addr_list = append_to_slist(new_esp->dst_addr_list, - esp_address); + update_esp_address(new_esp, &locator_addr->address, &seq->update_id); n--; if (n > 0) { locator_addr++; @@ -692,13 +841,7 @@ new_esp->spi = ntohl(esp_info->new_spi); new_esp->tuple = tuple; - struct esp_address *esp_address = malloc(sizeof(struct esp_address)); - - memcpy(&esp_address->dst_addr, addr, sizeof(struct in6_addr)); - - esp_address->update_id = NULL; - new_esp->dst_addr_list = append_to_slist(new_esp->dst_addr_list, - esp_address); + update_esp_address(new_esp, addr, NULL); } return new_esp; } @@ -836,16 +979,9 @@ HIP_IFEL(!(reverse_tuple = get_tuple_by_hits(&common->hits, &common->hitr)), 0, "No reverse tuple, skip\n"); - HIP_DEBUG("tuple src=%d dst=%d\n", tuple->src_port, tuple->dst_port); - HIP_DEBUG_IN6ADDR("tuple src ip", tuple->src_ip); - HIP_DEBUG_IN6ADDR("tuple dst ip", tuple->dst_ip); HIP_DEBUG("tuple dir=%d, sport=%d, dport=%d, rel=%d\n", tuple->direction, tuple->src_port, tuple->dst_port, tuple->esp_relay); - HIP_DEBUG("reverse tuple src=%d dst=%d\n", reverse_tuple->src_port, - reverse_tuple->dst_port); - HIP_DEBUG_IN6ADDR("reverse tuple src ip", reverse_tuple->src_ip); - HIP_DEBUG_IN6ADDR("reverse tuple dst ip", reverse_tuple->dst_ip); HIP_DEBUG("reverse tuple dir=%d, sport=%d, dport=%d, rel=%d\n", reverse_tuple->direction, reverse_tuple->src_port, reverse_tuple->dst_port, reverse_tuple->esp_relay); @@ -1029,12 +1165,11 @@ esp_tuple->new_spi = 0; esp_tuple->spi_update_id = 0; esp_tuple->dst_addr_list = NULL; - esp_tuple->dst_addr_list = update_esp_address(esp_tuple->dst_addr_list, - ip6_src, NULL); esp_tuple->tuple = other_dir; other_dir->esp_tuples = append_to_slist(other_dir->esp_tuples, esp_tuple); + update_esp_address(esp_tuple, ip6_src, NULL); insert_esp_tuple(esp_tuple); } @@ -1101,10 +1236,9 @@ esp_tuple->new_spi = 0; esp_tuple->spi_update_id = 0; esp_tuple->dst_addr_list = NULL; - esp_tuple->dst_addr_list = update_esp_address(esp_tuple->dst_addr_list, - ip6_src, NULL); esp_tuple->tuple = other_dir; + update_esp_address(esp_tuple, ip6_src, NULL); insert_esp_tuple(esp_tuple); HIP_DEBUG("ESP tuple inserted\n"); @@ -1178,9 +1312,7 @@ (locator + 1); while (n > 0) { - esp_tuple->dst_addr_list = update_esp_address(esp_tuple->dst_addr_list, - &locator_addr->address, - &seq->update_id); + update_esp_address(esp_tuple, &locator_addr->address, &seq->update_id); n--; if (n > 0) { @@ -1220,9 +1352,7 @@ print_esp_tuple(esp_tuple); while (n > 0) { - esp_tuple->dst_addr_list = update_esp_address(esp_tuple->dst_addr_list, - &locator_addr->address, - &seq->update_id); + update_esp_address(esp_tuple, &locator_addr->address, &seq->update_id); n--; if (n > 0) { @@ -1512,6 +1642,9 @@ * @param accept_mobile process UPDATE packets * @param ctx context for the packet * + * @todo Consider I1 flood protection. Store information in midauth + * cookies, perhaps? + * * @return 1 if packet if passed the verifications or otherwise 0 */ static int check_packet(const struct in6_addr *ip6_src, @@ -1597,7 +1730,7 @@ } #endif - insert_new_connection(data); + insert_new_connection(data, ctx); // TODO call free for all pointer members of data - comment by Rene free(data); @@ -1650,10 +1783,12 @@ // update time_stamp only on valid packets // for new connections time_stamp is set when creating if (tuple->connection) { - gettimeofday(&tuple->connection->time_stamp, NULL); + tuple->connection->timestamp = time(NULL); } else { HIP_DEBUG("Tuple connection NULL, could not timestamp\n"); } + + tuple->hook = ctx->ipq_packet->hook; } HIP_DEBUG("udp_encap_hdr=%p tuple=%p err=%d\n", ctx->udp_encap_hdr, tuple, err); @@ -1712,10 +1847,8 @@ HIP_IFEL(hip_fw_hit_is_our(&tuple->connection->original.hip_tuple->data->dst_hit), 0, "Destination HIT belongs to us, no relaying\n"); - HIP_DEBUG_IN6ADDR("I", &tuple->connection->original.hip_tuple->data->src_hit); - HIP_DEBUG_IN6ADDR("I", tuple->connection->original.src_ip); - HIP_DEBUG_IN6ADDR("R", &tuple->connection->original.hip_tuple->data->dst_hit); - HIP_DEBUG_IN6ADDR("R", tuple->connection->original.dst_ip); + HIP_DEBUG_IN6ADDR("src hit", &tuple->connection->original.hip_tuple->data->src_hit); + HIP_DEBUG_IN6ADDR("dst hit", &tuple->connection->original.hip_tuple->data->dst_hit); HIP_DEBUG("%d %d %d %d %d %d %d %d %d %d\n", tuple->src_port, @@ -1729,9 +1862,8 @@ tuple->direction, tuple->esp_relay_dport); - HIP_DEBUG_IN6ADDR("src", tuple->src_ip); - HIP_DEBUG_IN6ADDR("dst", tuple->dst_ip); - HIP_DEBUG_IN6ADDR("esp_relay_addr", &tuple->esp_relay_daddr); + HIP_DEBUG_IN6ADDR("src", &ctx->src); + HIP_DEBUG_IN6ADDR("dst", &tuple->esp_relay_daddr); udph->source = htons(HIP_NAT_UDP_PORT); udph->dest = htons(tuple->esp_relay_dport); @@ -1815,7 +1947,7 @@ out_err: // if we are going to accept the packet, update time stamp of the connection if (err > 0) { - gettimeofday(&tuple->connection->time_stamp, NULL); + tuple->connection->timestamp = time(NULL); } HIP_DEBUG("verdict %d \n", err); @@ -1956,3 +2088,273 @@ HIP_DEBUG("get_tuple_by_hits: no connection found\n"); return NULL; } + +/** + * Info about currently set esp rules and their respective packet counts. + */ +struct esp_rule_status { + uint32_t spi; /**< security parameter index */ + struct in6_addr addr; /**< dest address (may be IPV4-mapped) */ + unsigned int packet_count; /**< number of packets received */ +}; + +/** + * Update timestamp of connection corresponding to @a esp_tuple, if + * activity was detected. + * We especially take care of connections that aren't processed in + * userspace, because then we can determine activity only by + * examining the packet counts as reported by iptables. + * + * @param now current time + * @param snap snapshot of iptables state (relevant rules only) + * @param snap_count number of array items in @a snap + * @param esp_tuple extract SPI/dest pair from this ESP tuple + * + * @see hip_fw_conntrack_periodic_cleanup() + */ +static void update_esp_tuple_timestamp(const time_t now, + const struct esp_rule_status *snap, + const size_t snap_count, + struct esp_tuple *esp_tuple) +{ + struct connection *conn; + unsigned int i; + + HIP_ASSERT(snap); + HIP_ASSERT(esp_tuple); + + HIP_DEBUG("esp_tuple->spi = %08X\n", esp_tuple->spi); + + conn = esp_tuple->tuple->connection; + HIP_ASSERT(conn); + HIP_ASSERT(now >= conn->timestamp); + + if (now - conn->timestamp < connection_timeout) { + return; + } + + for (i = 0; i < snap_count; ++i) { + struct slist *cur; + + HIP_DEBUG("snap[i].spi = %08X\n", snap[i].spi); + if (snap[i].spi != esp_tuple->spi) { + continue; + } + + // We must check for SPI/dest pair. Checking for SPI only won't + // suffice in FORWARDING cases, because SPIs are local to + // end-hosts, not the firewall. + + cur = esp_tuple->dst_addr_list; + while (cur) { + struct esp_address *addr = cur->data; + + HIP_DEBUG_IN6ADDR("dst_addr", &addr->dst_addr); + HIP_DEBUG_IN6ADDR("snapi[i].addr", &snap[i].addr); + if (IN6_ARE_ADDR_EQUAL(&addr->dst_addr, &snap[i].addr)) { + // this is the right entry + if (esp_tuple->packet_count != snap[i].packet_count) { + // activity detected + HIP_DEBUG("activity detected\n"); + conn->timestamp = now; + esp_tuple->packet_count = snap[i].packet_count; + return; + } + } + cur = cur->next; + } + } +} + +/** + * Cache current iptables status. + * Currently, this works by parsing the output given by @a cmd, which + * is expected to have `iptables --nvL' format. + * + * @param cmd command line to capture output from + * @param out gathered info will be written here + * @param sz no more than @a sz entries will be written + * @return number of entries written + * + * @todo De-uglify this. You may be tempted to statically link in libiptc, + * and I'd generally approve of it because while it was never meant + * to be used publicly, quite some projects have relied on it without + * burning their fingers too badly for a long time now. But on the + * other hand, there's the impending nftables release that will render + * all your hard work obsolete anyway. I'd rather suggest waiting for + * a post-alpha release of libnl_nft before wasting your time... + * --cmroz, oct 2010 + */ +static size_t populate_snapshot(const char *cmd, + struct esp_rule_status *out, + const size_t sz) +{ + static const char u32_prefix[] = "u32 0x4&0x1fff=0x0&&0x0>>0x16&0x3c@0x8=0x"; + + int err = 0; + size_t ret = 0; + bool chain_ok = false; // current chain belongs to hipfw? + char bfr[256]; + FILE *p; + + if (sz == 0) { + return 0; + } + + HIP_IFEL(!(p = popen(cmd, "r")), 1, "popen(\"%s\"): %s\n", + cmd, strerror(errno)); + while (fgets(bfr, sizeof(bfr), p)) { + if (strncmp(bfr, "Chain", 5) == 0) { + chain_ok = strncmp(bfr, "Chain HIPFW-INPUT", 17) == 0 || + strncmp(bfr, "Chain HIPFW-OUTPUT", 18) == 0 || + strncmp(bfr, "Chain HIPFW-FORWARD", 19) == 0; + continue; + } + + if (chain_ok) { + HIP_ASSERT(ret < sz); + + // longest IPv6 addr is 50 chars long + char ip[50 + 1]; + const char *str_spi; + + // there are two ways of specifying SPIs (see hip_fw_manage_esp_rule) + + if ((str_spi = strstr(bfr, "spi:")) != NULL) { + // non-udp + if (sscanf(str_spi, "spi:%u", &out[ret].spi) < 1) { + HIP_ERROR("Malformed iptables output: %s\n", bfr); + continue; + } + } else if ((str_spi = strstr(bfr, u32_prefix)) != NULL) { + // udp + if (sscanf(str_spi + (sizeof(u32_prefix) - 1), "%x", &out[ret].spi) < 1) { + HIP_ERROR("Malformed iptables output: %s\n", bfr); + continue; + } + } else { + // no SPI specified, so it's no ESP rule + continue; + } + + // grab packet count and dest IP + if (sscanf(bfr, "%d %*s %*s %*s %*s %*s %*s %*s %s", + &out[ret].packet_count, ip) < 2) { + + HIP_ERROR("Malformed iptables output: %s\n", bfr); + continue; + } + + // parse dest IP; try IPv6 first, then IPv4 + if (!inet_pton(AF_INET6, ip, &out[ret].addr)) { + struct in_addr addr4; + if (!inet_pton(AF_INET, ip, &addr4)) { + HIP_ERROR("Can't parse dest IP: %s\n", bfr); + continue; + } + + IPV4_TO_IPV6_MAP(&addr4, &out[ret].addr); + } + + ret += 1; + if (ret >= sz) { + // we can't store any more info + break; + } + } + } + + if (ret < sz && !feof(p)) { + HIP_ERROR("fgets(\"%s\"): %s\n", cmd, strerror(errno)); + err = 1; + } + +out_err: + if (p) { + pclose(p); + } + + HIP_DEBUG("-> %u\n", ret); + return ret; +} + +/** + * Do some necessary bookkeeping concerning connection tracking. + * Currently, this only makes sure that stale locations will be removed. + * The actual tasks will be run at most once per @c connection_timeout + * seconds, no matter how often you call the function. + * + * @note Don't call this from a thread or timer, since most of hipfw is not + * reentrant (and so this function isn't either). + */ +void hip_fw_conntrack_periodic_cleanup(void) +{ + int err = 0; + struct slist *iter_conn = NULL; + struct esp_rule_status *snapshot = NULL; + static time_t last_check = 0; // timestamp of last call + const time_t now = time(NULL); + + if (connection_timeout == 0) { + // timeout disabled + return; + } + + HIP_ASSERT(now >= last_check); + if (last_check != 0 && now - last_check < cleanup_interval) { + // don't sweep yet + return; + } + + HIP_DEBUG("Commencing periodic cleanup\n"); + + last_check = now; + + // If connections are covered by iptables rules, we rely on packet + // counters to update timestamps indirectly. + // By taking care of this now, the affected connections won't be + // examined again in the conn_list loop later on. + + if (total_esp_rules_count > 0) { + const size_t sz = sizeof(*snapshot) * total_esp_rules_count; + size_t n = 0; + + HIP_IFEL(!(snapshot = malloc(sz)), 1, "Insufficient memory\n"); + + n += populate_snapshot("iptables -nvL", &snapshot[n], total_esp_rules_count - n); + HIP_ASSERT(n <= total_esp_rules_count); + + n += populate_snapshot("ip6tables -nvL", &snapshot[n], total_esp_rules_count - n); + HIP_ASSERT(n <= total_esp_rules_count); + + struct dlist *iter_esp = esp_list; + while (iter_esp) { + update_esp_tuple_timestamp(now, snapshot, n, iter_esp->data); + iter_esp = iter_esp->next; + } + free(snapshot); + snapshot = NULL; + } + + // do the actual cleaning now + + iter_conn = conn_list; + while (iter_conn) { + struct connection *conn = (struct connection*) iter_conn->data; + + HIP_ASSERT(now >= conn->timestamp); + if (now - conn->timestamp >= connection_timeout) { + HIP_DEBUG("Connection timeout\n"); + + // iter_conn will be invalid after remove_connection() + struct slist *tmp = iter_conn->next; + remove_connection(conn); + iter_conn = tmp; + } else { + iter_conn = iter_conn->next; + } + } + +out_err: + free(snapshot); +} === modified file 'firewall/conntrack.h' --- firewall/conntrack.h 2010-12-13 19:09:27 +0000 +++ firewall/conntrack.h 2010-12-13 21:28:35 +0000 @@ -29,6 +29,7 @@ #define _BSD_SOURCE #include <stdint.h> +#include <stdbool.h> #include <netinet/in.h> #include "lib/core/protodefs.h" @@ -38,6 +39,9 @@ /*-------------- CONNECTION TRACKING ------------*/ + +extern time_t connection_timeout; + enum { ORIGINAL_DIR, REPLY_DIR, @@ -58,5 +62,7 @@ struct tuple *get_tuple_by_hits(const struct in6_addr *src_hit, const struct in6_addr *dst_hit); int hipfw_relay_esp(const hip_fw_context_t *ctx); - +void hip_fw_manage_esp_tuple(const struct esp_tuple *esp_tuple, + bool insert); +void hip_fw_conntrack_periodic_cleanup(void); #endif /* HIP_FIREWALL_CONNTRACK_H */ === modified file 'firewall/firewall.c' --- firewall/firewall.c 2010-12-13 18:41:43 +0000 +++ firewall/firewall.c 2010-12-13 21:28:35 +0000 @@ -137,7 +137,6 @@ static hip_fw_handler_t hip_fw_handler[NF_IP_NUMHOOKS][FW_PROTO_NUM]; /* extension-specific state */ -static int hip_userspace_ipsec = 0; static int hip_esp_protection = 0; static int restore_filter_traffic = HIP_FW_FILTER_TRAFFIC_BY_DEFAULT; static int restore_accept_hip_esp_traffic = HIP_FW_ACCEPT_HIP_ESP_TRAFFIC_BY_DEFAULT; @@ -149,6 +148,9 @@ int hip_lsi_support = 0; int system_based_opp_mode = 0; int esp_relay = 0; +int hip_userspace_ipsec = 0; +int prefer_userspace = 0; +int prohibit_relay = 0; #ifdef CONFIG_HIP_MIDAUTH int use_midauth = 0; #endif @@ -177,7 +179,7 @@ static void print_usage(void) { printf("HIP Firewall\n"); - printf("Usage: hipfw [-f file_name] [-d|-v] [-A] [-F] [-H] [-b] [-a] [-c] [-k] [-i|-I|-e] [-l] [-o] [-p] [-h] [-V]"); + printf("Usage: hipfw [-f file_name] [-d|-v] [-A] [-F] [-H] [-b] [-a] [-c] [-k] [-i|-I|-e] [-l] [-o] [-p] [-R] [-t <seconds>] [-U] [-h] [-V]"); #ifdef CONFIG_HIP_MIDAUTH printf(" [-m]"); #endif @@ -197,6 +199,9 @@ printf(" -l = activate lsi support\n"); printf(" -o = system-based opportunistic mode\n\n"); printf(" -p = run with lowered priviledges. iptables rules will not be flushed on exit\n"); + printf(" -R = don't track rvs/relay even if enabled in hipd\n"); + printf(" -t <seconds> = set timeout interval to <seconds>. Disable if <seconds> = 0.\n"); + printf(" -U = prefer userspace processing, i.e. don't add iptables rules for speedups\n"); printf(" -h = print this help\n"); #ifdef CONFIG_HIP_MIDAUTH printf(" -m = middlebox authentification\n"); @@ -1933,6 +1938,7 @@ hip_fw_context_t ctx; int limit_capabilities = 0; int is_root = 0, access_ok = 0, msg_type = 0; //variables for accepting user messages only from hipd + char *ptr = NULL; // temporary pointer (see -t option) /* Make sure that root path is set up correcly (e.g. on Fedora 9). * Otherwise may get warnings from system_print() commands. @@ -1974,7 +1980,7 @@ hip_set_logdebug(LOGDEBUG_ALL); - while ((ch = getopt(argc, argv, "aAbcdef:FhHiIklmopvV")) != -1) { + while ((ch = getopt(argc, argv, "aAbcdef:FhHiIklmopRt:UvV")) != -1) { switch (ch) { case 'A': accept_hip_esp_traffic_by_default = 1; @@ -2030,6 +2036,20 @@ case 'p': limit_capabilities = 1; break; + case 'R': + prohibit_relay = 1; + break; + case 't': + connection_timeout = strtoul(optarg, &ptr, 10); + if (ptr == optarg) { + fprintf(stderr, "Error: Invalid timeout given\n"); + print_usage(); + errflg = 1; + } + break; + case 'U': + prefer_userspace = 1; + break; case 'v': log_level = LOGDEBUG_MEDIUM; hip_set_logfmt(LOGFMT_SHORT); @@ -2179,6 +2199,8 @@ } #endif + hip_fw_conntrack_periodic_cleanup(); + if (FD_ISSET(h4->fd, &read_fdset)) { HIP_DEBUG("received IPv4 packet from iptables queue\n"); err = hip_fw_handle_packet(buf, h4, 4, &ctx); === modified file 'firewall/firewall.h' --- firewall/firewall.h 2010-10-15 15:29:14 +0000 +++ firewall/firewall.h 2010-12-13 21:28:35 +0000 @@ -37,6 +37,9 @@ extern int hip_fw_sock; extern int hip_fw_async_sock; extern int system_based_opp_mode; +extern int hip_userspace_ipsec; +extern int prefer_userspace; +extern int prohibit_relay; int hip_fw_init_esp_relay(void); void hip_fw_uninit_esp_relay(void); === modified file 'firewall/firewall_control.c' --- firewall/firewall_control.c 2010-11-30 14:50:30 +0000 +++ firewall/firewall_control.c 2010-12-13 21:28:35 +0000 @@ -142,6 +142,10 @@ hip_firewall_cache_delete_hldb(0); break; case HIP_MSG_OFFER_FULLRELAY: + if (prohibit_relay) { + HIP_DEBUG("-r given: Explicitly ignoring MSG_OFFER_FULLRELAY\n"); + break; + } if (!esp_relay) { HIP_DEBUG("Enabling ESP relay\n"); hip_fw_init_esp_relay(); @@ -150,6 +154,10 @@ } break; case HIP_MSG_CANCEL_FULLRELAY: + if (prohibit_relay) { + HIP_DEBUG("-r given: Explicitly ignoring MSG_CANCEL_FULLRELAY\n"); + break; + } HIP_DEBUG("Disabling ESP relay\n"); hip_fw_uninit_esp_relay(); break; === modified file 'firewall/firewall_defines.h' --- firewall/firewall_defines.h 2010-12-13 19:09:27 +0000 +++ firewall/firewall_defines.h 2010-12-13 21:28:35 +0000 @@ -30,6 +30,7 @@ #include <libipq.h> #include <stdint.h> +#include <stdbool.h> #include <netinet/in.h> #include <netinet/ip6.h> #include <netinet/tcp.h> @@ -78,11 +79,18 @@ // null update_id can be removed. }; +/** + * Stores information about one ESP connection endpoint (i.e., one SPI). + * + * @todo Write allocator/deallocator for automatic synching with ::esp_list. + */ struct esp_tuple { uint32_t spi; uint32_t new_spi; uint32_t spi_update_id; struct slist *dst_addr_list; + /** previous packet count. @see hip_fw_conntrack_periodic_cleanup() */ + unsigned int packet_count; struct tuple *tuple; /* tracking of the ESP SEQ number */ uint32_t seq_no; @@ -119,6 +127,11 @@ struct tuple *tuple; }; +/** + * Connection endpoint. + * + * @todo Write allocator/deallocator for automatic syncing with ::hip_list. + */ struct tuple { struct hip_tuple *hip_tuple; struct in6_addr *src_ip; @@ -129,18 +142,26 @@ int direction; struct connection *connection; int state; + int hook; uint32_t lupdate_seq; int esp_relay; struct in6_addr esp_relay_daddr; in_port_t esp_relay_dport; }; +/** + * A connection (i.e., two tuples). + * alloc/dealloc using insert_new_connection() or + * remove_connection(), respectively. + */ struct connection { struct tuple original; struct tuple reply; int verify_responder; int state; - struct timeval time_stamp; + time_t timestamp; + /* members needed for iptables setup */ + bool udp_encap; /**< uses udp-nat? */ /* members needed for ESP protection extension */ int num_esp_prot_tfms; uint8_t esp_prot_tfms[MAX_NUM_TRANSFORMS]; === modified file 'firewall/helpers.c' --- firewall/helpers.c 2010-12-13 21:15:07 +0000 +++ firewall/helpers.c 2010-12-13 21:28:35 +0000 @@ -33,6 +33,8 @@ */ #include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> #include <arpa/inet.h> #include <netinet/in.h> @@ -84,10 +86,43 @@ * * @param command The system command. The caller of this function must take * care that command does not contain malicious code. - */ -void system_print(const char *command) -{ - if (system(command) == -1) { - HIP_ERROR("Could not execute system command %s", command); - } + * @return Exit code on success, -1 on failure. + */ +int system_print(const char *command) +{ + int ret; + + if ((ret = system(command)) == -1) { + HIP_ERROR("Could not execute system command `%s'", command); + return -1; + } + + HIP_DEBUG("$ %s -> %d\n", command, WEXITSTATUS(ret)); + + return WEXITSTATUS(ret); +} + +/** + * printf()-like wrapper arount system_print. + * + * @param command The system command. This is a printf format string. + * The caller of this function must take care that command + * does not contain malicious code. + * @return Exit code on success, -1 on failure. + */ +int system_printf(const char *command, ...) +{ + static char bfr[196]; + + va_list vargs; + va_start(vargs, command); + + if (vsnprintf(bfr, sizeof(bfr), command, vargs) <= 0) { + HIP_ERROR("vsnprintf failed\n"); + va_end(vargs); + return -1; + } + + va_end(vargs); + return system_print(bfr); } === modified file 'firewall/helpers.h' --- firewall/helpers.h 2010-10-15 15:29:14 +0000 +++ firewall/helpers.h 2010-12-13 21:28:35 +0000 @@ -30,6 +30,7 @@ const char *addr_to_numeric(const struct in6_addr *addrp); struct in6_addr *numeric_to_addr(const char *num); -void system_print(const char *command); +int system_print(const char *command); +int system_printf(const char *command, ...); #endif /* HIP_FIREWALL_HELPERS_H */ === modified file 'firewall/hslist.c' --- firewall/hslist.c 2010-12-13 19:09:27 +0000 +++ firewall/hslist.c 2010-12-13 21:28:35 +0000 @@ -129,3 +129,21 @@ return list; } + +/** + * find an element in the linked list + * + * @param list the linked list + * @param data the element to find + * @return the element in the linked list + */ +struct slist *find_in_slist(struct slist *list, void *data) +{ + while (list) { + if (list->data == data) { + return list; + } + list = list->next; + } + return NULL; +} === modified file 'firewall/hslist.h' --- firewall/hslist.h 2010-12-13 19:09:27 +0000 +++ firewall/hslist.h 2010-12-13 21:28:35 +0000 @@ -32,4 +32,6 @@ struct slist *remove_link_slist(struct slist *list, struct slist *link); +struct slist *find_in_slist(struct slist *list, void *data); + #endif /* HIP_FIREWALL_HSLIST_H */ === modified file 'firewall/pisa.c' --- firewall/pisa.c 2010-12-13 21:24:26 +0000 +++ firewall/pisa.c 2010-12-13 21:28:35 +0000 @@ -313,6 +313,12 @@ if (t) { t->connection->pisa_state = PISA_STATE_ALLOW; HIP_INFO("PISA accepted the connection.\n"); + + struct slist *lst = t->esp_tuples; + while (lst) { + hip_fw_manage_esp_tuple(lst->data, 1); + lst = lst->next; + } } else { HIP_ERROR("Connection not found.\n"); } @@ -320,7 +326,8 @@ /** * Remove a connection from the list of accepted connections based on the hits - * of a packet. + * of a packet. Update firewall to prevent further data packets from being + * passed through. * * @param ctx context of the packet that contains HITs of the connection */ @@ -331,6 +338,15 @@ if (t) { t->connection->pisa_state = PISA_STATE_DISALLOW; + HIP_INFO("PISA removed the connection.\n"); + + struct slist *lst = t->esp_tuples; + while (lst) { + hip_fw_manage_esp_tuple(lst->data, 0); + lst = lst->next; + } + } else { + HIP_ERROR("Connection not found.\n"); } } === modified file 'hipd/hiprelay.c' --- hipd/hiprelay.c 2010-11-30 14:50:30 +0000 +++ hipd/hiprelay.c 2010-12-13 21:28:35 +0000 @@ -1015,16 +1015,16 @@ * * @param r the HIP control message to be relayed * @param type_hdr message type - * @param r_saddr the original source address - * @param r_daddr the original destination address + * @param r_saddr (unused) the original source address + * @param r_daddr (unused) the original destination address * @param relay_to_addr the address where to relay the packet * @param relay_to_port the port where to relay the packet * @return zero on success or negative on error */ static int hip_relay_forward_response(const hip_common_t *r, const uint8_t type_hdr, - const struct in6_addr *r_saddr, - const struct in6_addr *r_daddr, + DBG const struct in6_addr *r_saddr, + DBG const struct in6_addr *r_daddr, const struct in6_addr *relay_to_addr, const in_port_t relay_to_port) { === modified file 'lib/core/debug.h' --- lib/core/debug.h 2010-12-06 19:58:46 +0000 +++ lib/core/debug.h 2010-12-13 21:28:35 +0000 @@ -29,6 +29,7 @@ #include <stdarg.h> #include <stdint.h> +#include "config.h" #include "protodefs.h" /* includes filename, line number and max(debug_prefix[]) */ @@ -295,10 +296,17 @@ #define HIP_INFO_LSI(str, lsi) hip_print_lsi(DEBUG_LEVEL_INFO, __FILE__, __LINE__, __FUNCTION__, str, lsi) #define HIP_INFO_INADDR(str, in) hip_print_lsi(DEBUG_LEVEL_INFO, __FILE__, __LINE__, __FUNCTION__, str, in) +#ifdef CONFIG_HIP_DEBUG #define HIP_DEBUG_HIT(str, hit) hip_print_hit(DEBUG_LEVEL_DEBUG, __FILE__, __LINE__, __FUNCTION__, str, hit) #define HIP_DEBUG_IN6ADDR(str, in6) hip_print_hit(DEBUG_LEVEL_DEBUG, __FILE__, __LINE__, __FUNCTION__, str, in6) #define HIP_DEBUG_LSI(str, lsi) hip_print_lsi(DEBUG_LEVEL_DEBUG, __FILE__, __LINE__, __FUNCTION__, str, lsi) #define HIP_DEBUG_INADDR(str, in) hip_print_lsi(DEBUG_LEVEL_DEBUG, __FILE__, __LINE__, __FUNCTION__, str, in) +#else +#define HIP_DEBUG_HIT(str, hit) do {} while (0) +#define HIP_DEBUG_IN6ADDR(str, in6) do {} while (0) +#define HIP_DEBUG_LSI(str, lsi) do {} while (0) +#define HIP_DEBUG_INADDR(str, in) do {} while (0) +#endif enum logtype_t { LOGTYPE_NOLOG, LOGTYPE_SYSLOG, LOGTYPE_STDERR }; enum logfmt_t { LOGFMT_SHORT, LOGFMT_LONG }; === modified file 'modules/update/hipd/update.c' --- modules/update/hipd/update.c 2010-11-30 15:03:33 +0000 +++ modules/update/hipd/update.c 2010-12-13 21:28:35 +0000 @@ -427,7 +427,7 @@ * @return zero on success or negative on failure */ static int hip_select_local_addr_for_first_update(const struct hip_hadb_state *ha, - const struct in6_addr *src_addr, + DBG const struct in6_addr *src_addr, const struct in6_addr *dst_addr, struct in6_addr *new_src_addr) {