[hipl-dev] [Merge] lp:~diego-biurrun/hipl/hipfw-performance into lp:hipl

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)
 {

Other related posts: