aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/interceptor/linux_usermode.c
diff options
context:
space:
mode:
authorcodeworkx <codeworkx@cyanogenmod.com>2012-09-17 17:53:57 +0200
committercodeworkx <codeworkx@cyanogenmod.com>2012-09-18 16:31:59 +0200
commitc28265764ec6ad9995eb0c761a376ffc9f141fcd (patch)
tree3ad899757480d47deb2be6011509a4243e8e0dc2 /drivers/interceptor/linux_usermode.c
parent0ddbcb39c0dc0318f68d858f25a96a074142af2f (diff)
downloadkernel_samsung_smdk4412-c28265764ec6ad9995eb0c761a376ffc9f141fcd.zip
kernel_samsung_smdk4412-c28265764ec6ad9995eb0c761a376ffc9f141fcd.tar.gz
kernel_samsung_smdk4412-c28265764ec6ad9995eb0c761a376ffc9f141fcd.tar.bz2
applied patches from i9305 jb sources, updated mali to r3p0
Change-Id: Iec4bc4e2fb59e2cf5b4d25568a644d4e3719565e
Diffstat (limited to 'drivers/interceptor/linux_usermode.c')
-rw-r--r--drivers/interceptor/linux_usermode.c384
1 files changed, 384 insertions, 0 deletions
diff --git a/drivers/interceptor/linux_usermode.c b/drivers/interceptor/linux_usermode.c
new file mode 100644
index 0000000..4a6401d
--- /dev/null
+++ b/drivers/interceptor/linux_usermode.c
@@ -0,0 +1,384 @@
+/* Netfilter Driver for IPSec VPN Client
+ *
+ * Copyright(c) 2012 Samsung Electronics
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/*
+ * linux_usermode.c
+ *
+ */
+
+#include "linux_internal.h"
+#include "linux_packet_internal.h"
+
+#define SSH_DEBUG_MODULE "SshInterceptorPacketDstCache"
+
+extern SshInterceptor ssh_interceptor_context;
+
+Boolean
+ssh_interceptor_dst_entry_cache_init(SshInterceptor interceptor)
+{
+ SSH_DEBUG(SSH_D_MIDOK, ("Initialising dst entry cache"));
+
+ /* When the IPM is open, we cache dst entries with usermode engine. */
+ interceptor->dst_entry_cache_lock = ssh_kernel_mutex_alloc();
+ if (interceptor->dst_entry_cache_lock == NULL)
+ return FALSE;
+
+ interceptor->dst_entry_cache_timeout_registered = FALSE;
+ memset(interceptor->dst_entry_table, 0x0,
+ sizeof(SshDstEntry) * SSH_DST_ENTRY_TBL_SIZE);
+
+ interceptor->dst_entry_id = 1;
+ interceptor->dst_entry_cached_items = 0;
+
+ return TRUE;
+}
+
+/* How long the dst entry can live in the cache. */
+#define DST_ENTRY_MAX_CACHE_TIME 15
+static void
+ssh_interceptor_dst_entry_cache_timeout(unsigned long data)
+{
+ SshInterceptor interceptor = ssh_interceptor_context;
+ SshUInt32 slot;
+ SshDstEntry tmp, prev = NULL;
+ struct timeval tv;
+ unsigned long time_now;
+ unsigned long expiry;
+
+ ssh_kernel_mutex_lock(interceptor->dst_entry_cache_lock);
+
+ SSH_DEBUG(SSH_D_MIDOK,
+ ("Dst entry cache timeout %lu items in cache",
+ (unsigned long)interceptor->dst_entry_cached_items));
+ SSH_ASSERT(interceptor->dst_entry_cache_timeout_registered == TRUE);
+
+ if (interceptor->dst_entry_cached_items == 0)
+ {
+ interceptor->dst_entry_cache_timeout_registered = FALSE;
+ ssh_kernel_mutex_unlock(interceptor->dst_entry_cache_lock);
+ return;
+ }
+
+ tv.tv_sec = DST_ENTRY_MAX_CACHE_TIME;
+ tv.tv_usec = 0;
+ time_now = jiffies;
+ expiry = timeval_to_jiffies(&tv);
+
+ for (slot = 0; slot < SSH_DST_ENTRY_TBL_SIZE; slot++)
+ {
+ restart:
+ prev = NULL;
+ for (tmp = interceptor->dst_entry_table[slot];
+ tmp != NULL;
+ tmp = tmp->next)
+ {
+ /* Do we have a match? */
+ if ((tmp->allocation_time + expiry) < time_now ||
+ (time_now - tmp->allocation_time) > expiry)
+ {
+ /* Head of list. */
+ if (tmp == interceptor->dst_entry_table[slot])
+ {
+ SSH_DEBUG(SSH_D_MIDOK,
+ ("Dst entry cache timeout freeing head ID %lu",
+ (unsigned long)tmp->dst_entry_id));
+ interceptor->dst_entry_table[slot] = tmp->next;
+
+ interceptor->dst_entry_cached_items--;
+
+ dst_release(tmp->dst_entry);
+ ssh_free(tmp);
+
+ goto restart;
+ }
+
+ /* Any other place in the list. */
+ else
+ {
+ prev->next = tmp->next;
+
+ interceptor->dst_entry_cached_items--;
+
+ SSH_DEBUG(SSH_D_MIDOK,
+ ("Dst entry cache timeout freeing ID %lu",
+ (unsigned long)tmp->dst_entry_id));
+
+ dst_release(tmp->dst_entry);
+ ssh_free(tmp);
+
+ goto restart;
+ }
+ }
+
+ prev = tmp;
+ }
+ }
+
+ if (interceptor->dst_entry_cached_items > 0)
+ {
+ struct timeval tv;
+
+ tv.tv_sec = DST_ENTRY_MAX_CACHE_TIME;
+ tv.tv_usec = 0;
+
+ interceptor->dst_cache_timer.expires = jiffies + timeval_to_jiffies(&tv);
+ interceptor->dst_cache_timer.data = (unsigned long)interceptor;
+ interceptor->dst_cache_timer.function =
+ ssh_interceptor_dst_entry_cache_timeout;
+
+ mod_timer(&interceptor->dst_cache_timer,
+ interceptor->dst_cache_timer.expires);
+ }
+ else
+ {
+ interceptor->dst_entry_cache_timeout_registered = FALSE;
+ }
+
+ SSH_DEBUG(SSH_D_NICETOKNOW, ("Left %lu items in dst cache",
+ (unsigned long)
+ interceptor->dst_entry_cached_items));
+
+ ssh_kernel_mutex_unlock(interceptor->dst_entry_cache_lock);
+}
+
+void
+ssh_interceptor_dst_entry_cache_flush(SshInterceptor interceptor)
+{
+ SshUInt32 slot;
+ SshDstEntry tmp;
+
+ SSH_DEBUG(SSH_D_MIDOK, ("Dst entry cache flush, %lu items in cache",
+ (unsigned long)interceptor->dst_entry_cached_items));
+
+ ssh_kernel_mutex_lock(interceptor->dst_entry_cache_lock);
+ if (interceptor->dst_entry_cache_timeout_registered == TRUE)
+ {
+ ssh_kernel_mutex_unlock(interceptor->dst_entry_cache_lock);
+ del_timer_sync(&interceptor->dst_cache_timer);
+ ssh_kernel_mutex_lock(interceptor->dst_entry_cache_lock);
+ }
+
+ interceptor->dst_entry_cache_timeout_registered = FALSE;
+
+ /* Free all entries that are left in the table. */
+ for (slot = 0; slot < SSH_DST_ENTRY_TBL_SIZE; slot++)
+ {
+ tmp = interceptor->dst_entry_table[slot];
+ while (tmp != NULL)
+ {
+ SshDstEntry next = tmp->next;
+ interceptor->dst_entry_table[slot] = next;
+
+ interceptor->dst_entry_cached_items--;
+
+ SSH_DEBUG(SSH_D_NICETOKNOW, ("Releasing dst cache entry"));
+
+ dst_release(tmp->dst_entry);
+ ssh_free(tmp);
+
+ tmp = next;
+ }
+ }
+
+ SSH_ASSERT(interceptor->dst_entry_cached_items == 0);
+ ssh_kernel_mutex_unlock(interceptor->dst_entry_cache_lock);
+}
+
+void
+ssh_interceptor_dst_entry_cache_uninit(SshInterceptor interceptor)
+{
+ /* Something failed during initialization. */
+ if (interceptor->dst_entry_cache_lock == NULL)
+ return;
+
+ SSH_DEBUG(SSH_D_MIDOK, ("Dst entry cache uninit, %lu items in cache",
+ (unsigned long)interceptor->dst_entry_cached_items));
+
+ ssh_interceptor_dst_entry_cache_flush(interceptor);
+ ssh_kernel_mutex_uninit(interceptor->dst_entry_cache_lock);
+ ssh_kernel_mutex_free(interceptor->dst_entry_cache_lock);
+}
+
+/* Cache a dst entry for later purposes. This is required by the
+ pass unmodified to work. If we lose the dst entry, we basically
+ cannot return the packet as unmodified to the linux. Return 0
+ if the caching fails. If it succeeds, return a valid cache ID. */
+SshUInt32
+ssh_interceptor_packet_cache_dst_entry(SshInterceptor interceptor,
+ SshInterceptorPacket pp)
+{
+ SshInterceptorInternalPacket ipp = (SshInterceptorInternalPacket)pp;
+ SshDstEntry cache_dst;
+ SshDstEntry tmp;
+ SshUInt32 slot;
+
+ SSH_DEBUG(SSH_D_MIDOK,
+ ("Dst entry cache, caching dst for pp 0x%p, %lu items in cache",
+ pp, (unsigned long)interceptor->dst_entry_cached_items));
+
+ if (ipp->skb == NULL || SSH_SKB_DST(ipp->skb) == NULL)
+ return 0;
+
+ cache_dst = ssh_calloc(1, sizeof(SshDstEntryStruct));
+ if (cache_dst == NULL)
+ return 0;
+
+ cache_dst->allocation_time = jiffies;
+ cache_dst->next = NULL;
+
+ cache_dst->dst_entry = SSH_SKB_DST(ipp->skb);
+ dst_hold(cache_dst->dst_entry);
+
+ ssh_kernel_mutex_lock(interceptor->dst_entry_cache_lock);
+
+ cache_dst->dst_entry_id = interceptor->dst_entry_id++;
+ slot = cache_dst->dst_entry_id % SSH_DST_ENTRY_TBL_SIZE;
+
+ interceptor->dst_entry_cached_items++;
+
+ SSH_ASSERT(slot < SSH_DST_ENTRY_TBL_SIZE);
+
+ /* Head of list. */
+ if (interceptor->dst_entry_table[slot] == NULL)
+ {
+ interceptor->dst_entry_table[slot] = cache_dst;
+ }
+ else
+ {
+ /* We do not care about potential collisions. These are highly unlikely
+ to happen and in the end */
+ for (tmp = interceptor->dst_entry_table[slot];
+ tmp->next != NULL;
+ tmp = tmp->next)
+ SSH_ASSERT(cache_dst->dst_entry_id != tmp->dst_entry_id);
+
+ tmp->next = cache_dst;
+ }
+
+ /* Handle special case, the id is overflowing. 0 is used for special
+ purposes, i.e. for 'real' engine created packets. */
+ if (interceptor->dst_entry_id == 0)
+ interceptor->dst_entry_id = 1;
+
+ if (interceptor->dst_entry_cache_timeout_registered == FALSE)
+ {
+ struct timeval tv;
+
+ SSH_ASSERT(interceptor->dst_entry_cached_items > 0);
+
+ tv.tv_sec = DST_ENTRY_MAX_CACHE_TIME;
+ tv.tv_usec = 0;
+
+ init_timer(&interceptor->dst_cache_timer);
+ interceptor->dst_cache_timer.expires = jiffies + timeval_to_jiffies(&tv);
+ interceptor->dst_cache_timer.data = (unsigned long)interceptor;
+ interceptor->dst_cache_timer.function =
+ ssh_interceptor_dst_entry_cache_timeout;
+ add_timer(&interceptor->dst_cache_timer);
+
+ interceptor->dst_entry_cache_timeout_registered = TRUE;
+ }
+
+ SSH_DEBUG(SSH_D_NICETOKNOW, ("Cache ID %lu, left %lu items in dst cache",
+ (unsigned long)cache_dst->dst_entry_id,
+ (unsigned long)
+ interceptor->dst_entry_cached_items));
+
+ ssh_kernel_mutex_unlock(interceptor->dst_entry_cache_lock);
+
+ return cache_dst->dst_entry_id;
+}
+
+void
+ssh_interceptor_packet_return_dst_entry(SshInterceptor interceptor,
+ SshUInt32 dst_entry_id,
+ SshInterceptorPacket pp,
+ Boolean remove_only)
+{
+ SshInterceptorInternalPacket ipp = (SshInterceptorInternalPacket)pp;
+ SshUInt32 slot = dst_entry_id % SSH_DST_ENTRY_TBL_SIZE;
+ SshDstEntry tmp, prev = NULL;
+
+ SSH_DEBUG(SSH_D_MIDOK,
+ ("Returning dst entry ID %lu, pp 0x%p, %lu items in cache, "
+ "update %s",
+ (unsigned long)dst_entry_id, pp,
+ (unsigned long)interceptor->dst_entry_cached_items,
+ remove_only == TRUE ? "no" : "yes"));
+
+ /* Special case, 'real' engine created packets. */
+ if (dst_entry_id == 0)
+ return;
+
+ SSH_ASSERT(slot < SSH_DST_ENTRY_TBL_SIZE);
+
+ ssh_kernel_mutex_lock(interceptor->dst_entry_cache_lock);
+ for (tmp = interceptor->dst_entry_table[slot]; tmp != NULL; tmp = tmp->next)
+ {
+ /* Do we have a match? */
+ if (tmp->dst_entry_id == dst_entry_id)
+ {
+ /* Head of list. */
+ if (tmp == interceptor->dst_entry_table[slot])
+ {
+ interceptor->dst_entry_table[slot] = tmp->next;
+
+ interceptor->dst_entry_cached_items--;
+ ssh_kernel_mutex_unlock(interceptor->dst_entry_cache_lock);
+
+ if (remove_only == FALSE && pp != NULL)
+ SSH_SKB_DST_SET(ipp->skb, tmp->dst_entry);
+ else
+ dst_release(tmp->dst_entry);
+
+ ssh_free(tmp);
+
+ SSH_DEBUG(SSH_D_NICETOKNOW,
+ ("Removed cache ID %lu, left %lu items in dst cache",
+ (unsigned long)dst_entry_id,
+ (unsigned long)interceptor->dst_entry_cached_items));
+
+ return;
+ }
+
+ /* Any other place in the list. */
+ else
+ {
+ prev->next = tmp->next;
+
+ interceptor->dst_entry_cached_items--;
+ ssh_kernel_mutex_unlock(interceptor->dst_entry_cache_lock);
+
+ if (remove_only == FALSE)
+ SSH_SKB_DST_SET(ipp->skb, tmp->dst_entry);
+ else
+ dst_release(tmp->dst_entry);
+
+ ssh_free(tmp);
+
+ SSH_DEBUG(SSH_D_NICETOKNOW,
+ ("Removed cache ID %lu, left %lu items in dst cache",
+ (unsigned long)dst_entry_id,
+ (unsigned long)interceptor->dst_entry_cached_items));
+
+ return;
+ }
+ }
+
+ prev = tmp;
+ }
+
+ SSH_DEBUG(SSH_D_NICETOKNOW,
+ ("Cache ID %lu was not found, left %lu items in dst cache",
+ (unsigned long)dst_entry_id,
+ (unsigned long)interceptor->dst_entry_cached_items));
+
+ ssh_kernel_mutex_unlock(interceptor->dst_entry_cache_lock);
+}