blob: 45e454c4a9aa632a8fa4f333d9bf83c94a2bc503 [file] [log] [blame]
/*
* Copyright (c) 2018-2020 The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Changes from Qualcomm Innovation Center are provided under the following license:
*
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted (subject to the limitations in the
* disclaimer below) provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* * Neither the name of Qualcomm Innovation Center, Inc. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
* GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
* HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
*/
#include "ipa_ipv6ct.h"
#include "ipa_ipv6cti.h"
#include <sys/ioctl.h>
#include <stdlib.h>
#include <errno.h>
#include <pthread.h>
#include <unistd.h>
#include <linux/msm_ipa.h>
#define IPA_IPV6CT_DEBUG_FILE_PATH "/sys/kernel/debug/ipa/ipv6ct"
#define IPA_UC_ACT_DEBUG_FILE_PATH "/sys/kernel/debug/ipa/uc_act_table"
#define IPA_IPV6CT_TABLE_NAME "IPA IPv6CT table"
#define IPA_MAX_DMA_ENTRIES_FOR_ADD 2
#define IPA_MAX_DMA_ENTRIES_FOR_DEL 2
static int ipa_ipv6ct_create_table(ipa_ipv6ct_table* ipv6ct_table, uint16_t number_of_entries, uint8_t table_index);
static int ipa_ipv6ct_destroy_table(ipa_ipv6ct_table* ipv6ct_table);
static void ipa_ipv6ct_create_table_dma_cmd_helpers(ipa_ipv6ct_table* ipv6ct_table, uint8_t table_indx);
static int ipa_ipv6ct_post_init_cmd(ipa_ipv6ct_table* ipv6ct_table, uint8_t tbl_index);
static int ipa_ipv6ct_post_dma_cmd(struct ipa_ioc_nat_dma_cmd* cmd);
static uint16_t ipa_ipv6ct_hash(const ipa_ipv6ct_rule* rule, uint16_t size);
static uint16_t ipa_ipv6ct_xor_segments(uint64_t num);
static int table_entry_is_valid(void* entry);
static uint16_t table_entry_get_next_index(void* entry);
static uint16_t table_entry_get_prev_index(void* entry, uint16_t entry_index, void* meta, uint16_t base_table_size);
static void table_entry_set_prev_index(void* entry, uint16_t entry_index, uint16_t prev_index,
void* meta, uint16_t base_table_size);
static int table_entry_head_insert(void* entry, void* user_data, uint16_t* dma_command_data);
static int table_entry_tail_insert(void* entry, void* user_data);
static uint16_t table_entry_get_delete_head_dma_command_data(void* head, void* next_entry);
static ipa_ipv6ct ipv6ct;
static pthread_mutex_t ipv6ct_mutex = PTHREAD_MUTEX_INITIALIZER;
static ipa_table_entry_interface entry_interface =
{
table_entry_is_valid,
table_entry_get_next_index,
table_entry_get_prev_index,
table_entry_set_prev_index,
table_entry_head_insert,
table_entry_tail_insert,
table_entry_get_delete_head_dma_command_data
};
/**
* ipa_ipv6ct_add_tbl() - Adds a new IPv6CT table
* @number_of_entries: [in] number of IPv6CT entries
* @table_handle: [out] handle of new IPv6CT table
*
* This function creates new IPv6CT table and posts IPv6CT init command to HW
*
* Returns: 0 On Success, negative on failure
*/
int ipa_ipv6ct_add_tbl(uint16_t number_of_entries, uint32_t* table_handle)
{
int ret;
ipa_ipv6ct_table* ipv6ct_table;
IPADBG("\n");
if (table_handle == NULL || number_of_entries == 0)
{
IPAERR("Invalid parameters table_handle=%pK number_of_entries=%d\n", table_handle, number_of_entries);
return -EINVAL;
}
*table_handle = 0;
if (ipv6ct.table_cnt >= IPA_IPV6CT_MAX_TBLS)
{
IPAERR("Can't add addition IPv6 connection tracking table. Maximum %d tables allowed\n", IPA_IPV6CT_MAX_TBLS);
return -EINVAL;
}
if (!ipv6ct.ipa_desc)
{
ipv6ct.ipa_desc = ipa_descriptor_open();
if (ipv6ct.ipa_desc == NULL)
{
IPAERR("failed to open IPA driver file descriptor\n");
return -EIO;
}
}
if (ipv6ct.ipa_desc->ver < IPA_HW_v4_0)
{
IPAERR("IPv6 connection tracking isn't supported for IPA version %d\n", ipv6ct.ipa_desc->ver);
ret = -EPERM;
goto bail_ipa_desc;
}
ipv6ct_table = &ipv6ct.tables[ipv6ct.table_cnt];
ret = ipa_ipv6ct_create_table(ipv6ct_table, number_of_entries, ipv6ct.table_cnt);
if (ret)
{
IPAERR("unable to create ipv6ct table Error: %d\n", ret);
goto bail_ipa_desc;
}
/* Initialize the ipa hw with ipv6ct table dimensions */
ret = ipa_ipv6ct_post_init_cmd(ipv6ct_table, ipv6ct.table_cnt);
if (ret)
{
IPAERR("unable to post ipv6ct_init command Error %d\n", ret);
goto bail_ipv6ct_table;
}
/* Return table handle */
++ipv6ct.table_cnt;
*table_handle = ipv6ct.table_cnt;
IPADBG("Returning table handle 0x%x\n", *table_handle);
return 0;
bail_ipv6ct_table:
ipa_ipv6ct_destroy_table(ipv6ct_table);
bail_ipa_desc:
if (!ipv6ct.table_cnt) {
ipa_descriptor_close(ipv6ct.ipa_desc);
ipv6ct.ipa_desc = NULL;
}
return ret;
}
int ipa_ipv6ct_del_tbl(uint32_t table_handle)
{
ipa_ipv6ct_table* ipv6ct_table;
int ret;
IPADBG("\n");
if (ipv6ct.ipa_desc->ver < IPA_HW_v4_0)
{
IPAERR("IPv6 connection tracking isn't supported for IPA version %d\n", ipv6ct.ipa_desc->ver);
return -EINVAL;
}
if (table_handle == IPA_TABLE_INVALID_ENTRY || table_handle > IPA_IPV6CT_MAX_TBLS)
{
IPAERR("invalid table handle %d passed\n", table_handle);
return -EINVAL;
}
IPADBG("Passed Table Handle: 0x%x\n", table_handle);
if (pthread_mutex_lock(&ipv6ct_mutex))
{
IPAERR("unable to lock the ipv6ct mutex\n");
return -EINVAL;
}
ipv6ct_table = &ipv6ct.tables[table_handle - 1];
if (!ipv6ct_table->mem_desc.valid)
{
IPAERR("invalid table handle %d\n", table_handle);
ret = -EINVAL;
goto unlock;
}
ret = ipa_ipv6ct_destroy_table(ipv6ct_table);
if (ret)
{
IPAERR("unable to delete IPV6CT table with handle %d\n", table_handle);
goto unlock;
}
if (!--ipv6ct.table_cnt) {
ipa_descriptor_close(ipv6ct.ipa_desc);
ipv6ct.ipa_desc = NULL;
}
unlock:
if (pthread_mutex_unlock(&ipv6ct_mutex))
{
IPAERR("unable to unlock the ipv6ct mutex\n");
return (ret) ? ret : -EPERM;
}
IPADBG("return\n");
return ret;
}
int ipa_ipv6ct_add_rule(uint32_t table_handle, const ipa_ipv6ct_rule* user_rule, uint32_t* rule_handle)
{
int ret;
ipa_ipv6ct_table* ipv6ct_table;
uint16_t new_entry_index;
uint32_t new_entry_handle;
uint32_t cmd_sz = sizeof(struct ipa_ioc_nat_dma_cmd) +
(IPA_MAX_DMA_ENTRIES_FOR_ADD * sizeof(struct ipa_ioc_nat_dma_one));
char cmd_buf[cmd_sz];
struct ipa_ioc_nat_dma_cmd* cmd;
IPADBG("\n");
if (ipv6ct.ipa_desc->ver < IPA_HW_v4_0)
{
IPAERR("IPv6 connection tracking isn't supported for IPA version %d\n", ipv6ct.ipa_desc->ver);
return -EINVAL;
}
if (table_handle == IPA_TABLE_INVALID_ENTRY || table_handle > IPA_IPV6CT_MAX_TBLS ||
rule_handle == NULL || user_rule == NULL)
{
IPAERR("Invalid parameters table_handle=%d rule_handle=%pK user_rule=%pK\n",
table_handle, rule_handle, user_rule);
return -EINVAL;
}
IPADBG("Passed Table handle: 0x%x\n", table_handle);
if (user_rule->protocol == IPA_IPV6CT_INVALID_PROTO_FIELD_CMP)
{
IPAERR("invalid parameter protocol=%d\n", user_rule->protocol);
return -EINVAL;
}
if (pthread_mutex_lock(&ipv6ct_mutex))
{
IPAERR("unable to lock the ipv6ct mutex\n");
return -EINVAL;
}
ipv6ct_table = &ipv6ct.tables[table_handle - 1];
if (!ipv6ct_table->mem_desc.valid)
{
IPAERR("invalid table handle %d\n", table_handle);
ret = -EINVAL;
goto unlock;
}
memset(cmd_buf, 0, sizeof(cmd_buf));
cmd = (struct ipa_ioc_nat_dma_cmd*) cmd_buf;
cmd->entries = 0;
new_entry_index = ipa_ipv6ct_hash(user_rule, ipv6ct_table->table.table_entries - 1);
ret = ipa_table_add_entry(&ipv6ct_table->table, (void*)user_rule, &new_entry_index, &new_entry_handle, cmd);
if (ret)
{
IPAERR("failed to add a new IPV6CT entry\n");
goto unlock;
}
ret = ipa_ipv6ct_post_dma_cmd(cmd);
if (ret)
{
IPAERR("unable to post dma command\n");
goto bail;
}
if (pthread_mutex_unlock(&ipv6ct_mutex))
{
IPAERR("unable to unlock the ipv6ct mutex\n");
return -EPERM;
}
*rule_handle = new_entry_handle;
IPADBG("return\n");
return 0;
bail:
ipa_table_erase_entry(&ipv6ct_table->table, new_entry_index);
unlock:
if (pthread_mutex_unlock(&ipv6ct_mutex))
IPAERR("unable to unlock the ipv6ct mutex\n");
return ret;
}
int ipa_ipv6ct_del_rule(uint32_t table_handle, uint32_t rule_handle)
{
ipa_ipv6ct_table* ipv6ct_table;
ipa_table_iterator table_iterator;
ipa_ipv6ct_hw_entry* entry;
uint32_t cmd_sz = sizeof(struct ipa_ioc_nat_dma_cmd) +
(IPA_MAX_DMA_ENTRIES_FOR_DEL * sizeof(struct ipa_ioc_nat_dma_one));
char cmd_buf[cmd_sz];
struct ipa_ioc_nat_dma_cmd* cmd;
uint16_t index;
int ret;
IPADBG("\n");
if (ipv6ct.ipa_desc->ver < IPA_HW_v4_0)
{
IPAERR("IPv6 connection tracking isn't supported for IPA version %d\n", ipv6ct.ipa_desc->ver);
return -EINVAL;
}
if (table_handle == IPA_TABLE_INVALID_ENTRY || table_handle > IPA_IPV6CT_MAX_TBLS ||
rule_handle == IPA_TABLE_INVALID_ENTRY)
{
IPAERR("Invalid parameters table_handle=%d rule_handle=%d\n", table_handle, rule_handle);
return -EINVAL;
}
IPADBG("Passed Table: 0x%x and rule handle 0x%x\n", table_handle, rule_handle);
if (pthread_mutex_lock(&ipv6ct_mutex))
{
IPAERR("unable to lock the ipv6ct mutex\n");
return -EINVAL;
}
ipv6ct_table = &ipv6ct.tables[table_handle - 1];
if (!ipv6ct_table->mem_desc.valid)
{
IPAERR("invalid table handle %d\n", table_handle);
ret = -EINVAL;
goto unlock;
}
ret = ipa_table_get_entry(&ipv6ct_table->table, rule_handle, (void**)&entry, &index);
if (ret)
{
IPAERR("unable to retrive the entry with handle=%d in IPV6CT table with handle=%d\n",
rule_handle, table_handle);
goto unlock;
}
ret = ipa_table_iterator_init(&table_iterator, &ipv6ct_table->table, entry, index);
if (ret)
{
IPAERR("unable to create iterator which points to the entry index=%d in IPV6CT table with handle=%d\n",
index, table_handle);
goto unlock;
}
memset(cmd_buf, 0, sizeof(cmd_buf));
cmd = (struct ipa_ioc_nat_dma_cmd*) cmd_buf;
cmd->entries = 0;
ipa_table_create_delete_command(&ipv6ct_table->table, cmd, &table_iterator);
ret = ipa_ipv6ct_post_dma_cmd(cmd);
if (ret)
{
IPAERR("unable to post dma command\n");
goto unlock;
}
if (!ipa_table_iterator_is_head_with_tail(&table_iterator))
{
/* The entry can be deleted */
uint8_t is_prev_empty = (table_iterator.prev_entry != NULL &&
((ipa_ipv6ct_hw_entry*)table_iterator.prev_entry)->protocol == IPA_IPV6CT_INVALID_PROTO_FIELD_CMP);
ipa_table_delete_entry(&ipv6ct_table->table, &table_iterator, is_prev_empty);
}
unlock:
if (pthread_mutex_unlock(&ipv6ct_mutex))
{
IPAERR("unable to unlock the ipv6ct mutex\n");
return (ret) ? ret : -EPERM;
}
IPADBG("return\n");
return ret;
}
int ipa_ipv6ct_query_timestamp(uint32_t table_handle, uint32_t rule_handle, uint32_t* time_stamp)
{
int ret;
ipa_ipv6ct_table* ipv6ct_table;
ipa_ipv6ct_hw_entry *entry;
IPADBG("\n");
if (ipv6ct.ipa_desc->ver < IPA_HW_v4_0)
{
IPAERR("IPv6 connection tracking isn't supported for IPA version %d\n", ipv6ct.ipa_desc->ver);
return -EINVAL;
}
if (table_handle == IPA_TABLE_INVALID_ENTRY || table_handle > IPA_IPV6CT_MAX_TBLS ||
rule_handle == IPA_TABLE_INVALID_ENTRY || time_stamp == NULL)
{
IPAERR("invalid parameters passed table_handle=%d rule_handle=%d time_stamp=%pK\n",
table_handle, rule_handle, time_stamp);
return -EINVAL;
}
IPADBG("Passed Table: %d and rule handle %d\n", table_handle, rule_handle);
if (pthread_mutex_lock(&ipv6ct_mutex))
{
IPAERR("unable to lock the ipv6ct mutex\n");
return -EINVAL;
}
ipv6ct_table = &ipv6ct.tables[table_handle - 1];
if (!ipv6ct_table->mem_desc.valid)
{
IPAERR("invalid table handle %d\n", table_handle);
ret = -EINVAL;
goto unlock;
}
ret = ipa_table_get_entry(&ipv6ct_table->table, rule_handle, (void**)&entry, NULL);
if (ret)
{
IPAERR("unable to retrive the entry with handle=%d in IPV6CT table with handle=%d\n",
rule_handle, table_handle);
goto unlock;
}
*time_stamp = entry->time_stamp;
unlock:
if (pthread_mutex_unlock(&ipv6ct_mutex))
{
IPAERR("unable to unlock the ipv6ct mutex\n");
return (ret) ? ret : -EPERM;
}
IPADBG("return\n");
return ret;
}
/**
* ipv6ct_hash() - Find the index into ipv6ct table
* @rule: [in] an IPv6CT rule
* @size: [in] size of the IPv6CT table
*
* This hash method is used to find the hash index of an entry into IPv6CT table.
* In case of result zero, N-1 will be returned, where N is size of IPv6CT table.
*
* Returns: >0 index into IPv6CT table, negative on failure
*/
static uint16_t ipa_ipv6ct_hash(const ipa_ipv6ct_rule* rule, uint16_t size)
{
uint16_t hash = 0;
IPADBG("src_ipv6_lsb 0x%llx\n", rule->src_ipv6_lsb);
IPADBG("src_ipv6_msb 0x%llx\n", rule->src_ipv6_msb);
IPADBG("dest_ipv6_lsb 0x%llx\n", rule->dest_ipv6_lsb);
IPADBG("dest_ipv6_msb 0x%llx\n", rule->dest_ipv6_msb);
IPADBG("src_port: 0x%x dest_port: 0x%x\n", rule->src_port, rule->dest_port);
IPADBG("protocol: 0x%x size: 0x%x\n", rule->protocol, size);
hash ^= ipa_ipv6ct_xor_segments(rule->src_ipv6_lsb);
hash ^= ipa_ipv6ct_xor_segments(rule->src_ipv6_msb);
hash ^= ipa_ipv6ct_xor_segments(rule->dest_ipv6_lsb);
hash ^= ipa_ipv6ct_xor_segments(rule->dest_ipv6_msb);
hash ^= rule->src_port;
hash ^= rule->dest_port;
hash ^= rule->protocol;
/*
* The size passed to hash function expected be power^2-1, while the actual size is power^2,
* actual_size = size + 1
*/
hash &= size;
/* If the hash resulted to zero then set it to maximum value as zero is unused entry in ipv6ct table */
if (hash == 0)
{
hash = size;
}
IPADBG("ipa_ipv6ct_hash returning value: %d\n", hash);
return hash;
}
static uint16_t ipa_ipv6ct_xor_segments(uint64_t num)
{
const uint64_t mask = 0xffff;
const size_t bits_in_two_byte = 16;
uint16_t ret = 0;
IPADBG("\n");
while (num)
{
ret ^= (uint16_t)(num & mask);
num >>= bits_in_two_byte;
}
IPADBG("return\n");
return ret;
}
static int table_entry_is_valid(void* entry)
{
ipa_ipv6ct_hw_entry* ipv6ct_entry = (ipa_ipv6ct_hw_entry*)entry;
IPADBG("\n");
return ipv6ct_entry->enable;
}
static uint16_t table_entry_get_next_index(void* entry)
{
uint16_t result;
ipa_ipv6ct_hw_entry* ipv6ct_entry = (ipa_ipv6ct_hw_entry*)entry;
IPADBG("\n");
result = ipv6ct_entry->next_index;
IPADBG("Next entry of %pK is %d\n", entry, result);
return result;
}
static uint16_t table_entry_get_prev_index(void* entry, uint16_t entry_index, void* meta, uint16_t base_table_size)
{
uint16_t result;
ipa_ipv6ct_hw_entry* ipv6ct_entry = (ipa_ipv6ct_hw_entry*)entry;
IPADBG("\n");
result = ipv6ct_entry->prev_index;
IPADBG("Previous entry of %d is %d\n", entry_index, result);
return result;
}
static void table_entry_set_prev_index(void* entry, uint16_t entry_index, uint16_t prev_index,
void* meta, uint16_t base_table_size)
{
ipa_ipv6ct_hw_entry* ipv6ct_entry = (ipa_ipv6ct_hw_entry*)entry;
IPADBG("Previous entry of %d is %d\n", entry_index, prev_index);
ipv6ct_entry->prev_index = prev_index;
IPADBG("return\n");
}
static int table_entry_copy_from_user(void* entry, void* user_data)
{
ipa_ipv6ct_hw_entry* ipv6ct_entry = (ipa_ipv6ct_hw_entry*)entry;
const ipa_ipv6ct_rule* user_rule = (const ipa_ipv6ct_rule*)user_data;
IPADBG("\n");
ipv6ct_entry->src_ipv6_lsb = user_rule->src_ipv6_lsb;
ipv6ct_entry->src_ipv6_msb = user_rule->src_ipv6_msb;
ipv6ct_entry->dest_ipv6_lsb = user_rule->dest_ipv6_lsb;
ipv6ct_entry->dest_ipv6_msb = user_rule->dest_ipv6_msb;
ipv6ct_entry->protocol = user_rule->protocol;
ipv6ct_entry->src_port = user_rule->src_port;
ipv6ct_entry->dest_port = user_rule->dest_port;
ipv6ct_entry->ucp = user_rule->ucp;
ipv6ct_entry->uc_activation_index = user_rule->uc_activation_index;
ipv6ct_entry->s = user_rule->s;
switch (user_rule->direction_settings)
{
case IPA_IPV6CT_DIRECTION_DENY_ALL:
break;
case IPA_IPV6CT_DIRECTION_ALLOW_OUT:
ipv6ct_entry->out_allowed = IPA_IPV6CT_DIRECTION_ALLOW_BIT;
break;
case IPA_IPV6CT_DIRECTION_ALLOW_IN:
ipv6ct_entry->in_allowed = IPA_IPV6CT_DIRECTION_ALLOW_BIT;
break;
case IPA_IPV6CT_DIRECTION_ALLOW_ALL:
ipv6ct_entry->out_allowed = IPA_IPV6CT_DIRECTION_ALLOW_BIT;
ipv6ct_entry->in_allowed = IPA_IPV6CT_DIRECTION_ALLOW_BIT;
break;
default:
IPAERR("wrong value for IPv6CT direction setting parameter %d\n", user_rule->direction_settings);
return -EINVAL;
}
IPADBG("return\n");
return 0;
}
static int table_entry_head_insert(void* entry, void* user_data, uint16_t* dma_command_data)
{
int ret;
IPADBG("\n");
ret = table_entry_copy_from_user(entry, user_data);
if (ret)
{
IPAERR("unable to copy from user a new entry\n");
return ret;
}
*dma_command_data = 0;
((ipa_ipv6ct_flags*)dma_command_data)->enable = IPA_IPV6CT_FLAG_ENABLE_BIT;
IPADBG("return\n");
return 0;
}
static int table_entry_tail_insert(void* entry, void* user_data)
{
int ret;
IPADBG("\n");
ret = table_entry_copy_from_user(entry, user_data);
if (ret)
{
IPAERR("unable to copy from user a new entry\n");
return ret;
}
((ipa_ipv6ct_hw_entry*)entry)->enable = IPA_IPV6CT_FLAG_ENABLE_BIT;
IPADBG("return\n");
return 0;
}
static uint16_t table_entry_get_delete_head_dma_command_data(void* head, void* next_entry)
{
IPADBG("\n");
return IPA_IPV6CT_INVALID_PROTO_FIELD_VALUE;
}
/**
* ipa_ipv6ct_create_table() - Creates a new IPv6CT table
* @ipv6ct_table: [in] IPv6CT table
* @number_of_entries: [in] number of IPv6CT entries
* @table_index: [in] the index of the IPv6CT table
*
* This function creates new IPv6CT table:
* - Initializes table, memory descriptor and table_dma_cmd_helpers structures
* - Allocates, maps and clears the memory for table
*
* Returns: 0 On Success, negative on failure
*/
static int ipa_ipv6ct_create_table(ipa_ipv6ct_table* ipv6ct_table, uint16_t number_of_entries, uint8_t table_index)
{
int ret, size;
IPADBG("\n");
ipa_table_init(
&ipv6ct_table->table, IPA_IPV6CT_TABLE_NAME, IPA_NAT_MEM_IN_DDR,
sizeof(ipa_ipv6ct_hw_entry), NULL, 0, &entry_interface);
ret = ipa_table_calculate_entries_num(
&ipv6ct_table->table, number_of_entries, IPA_NAT_MEM_IN_DDR);
if (ret)
{
IPAERR("unable to calculate number of entries in ipv6ct table %d, while required by user %d\n",
table_index, number_of_entries);
return ret;
}
size = ipa_table_calculate_size(&ipv6ct_table->table);
IPADBG("IPv6CT table size: %d\n", size);
ipa_mem_descriptor_init(
&ipv6ct_table->mem_desc,
IPA_IPV6CT_DEV_NAME,
size,
table_index,
IPA_IOC_ALLOC_IPV6CT_TABLE,
IPA_IOC_DEL_IPV6CT_TABLE,
false); /* false here means don't consider using sram */
ret = ipa_mem_descriptor_allocate_memory(
&ipv6ct_table->mem_desc,
ipv6ct.ipa_desc->fd);
if (ret)
{
IPAERR("unable to allocate ipv6ct memory descriptor Error: %d\n", ret);
goto bail;
}
ipa_table_calculate_addresses(&ipv6ct_table->table, ipv6ct_table->mem_desc.base_addr);
ipa_table_reset(&ipv6ct_table->table);
ipa_ipv6ct_create_table_dma_cmd_helpers(ipv6ct_table, table_index);
IPADBG("return\n");
return 0;
bail:
memset(ipv6ct_table, 0, sizeof(*ipv6ct_table));
return ret;
}
static int ipa_ipv6ct_destroy_table(ipa_ipv6ct_table* ipv6ct_table)
{
int ret;
IPADBG("\n");
ret = ipa_mem_descriptor_delete(&ipv6ct_table->mem_desc, ipv6ct.ipa_desc->fd);
if (ret)
IPAERR("unable to delete IPV6CT descriptor\n");
memset(ipv6ct_table, 0, sizeof(*ipv6ct_table));
IPADBG("return\n");
return ret;
}
/**
* ipa_ipv6ct_create_table_dma_cmd_helpers() -
* Creates dma_cmd_helpers for base table in the received IPv6CT table
* @ipv6ct_table: [in] IPv6CT table
* @table_indx: [in] The index of the IPv6CT table
*
* A DMA command helper helps to generate the DMA command for one
* specific field change. Each table has 3 different types of field
* change: update_head, update_entry and delete_head. This function
* creates the helpers and updates the base table correspondingly.
*/
static void ipa_ipv6ct_create_table_dma_cmd_helpers(
ipa_ipv6ct_table* ipv6ct_table,
uint8_t table_indx )
{
IPADBG("\n");
ipa_table_dma_cmd_helper_init(
&ipv6ct_table->table_dma_cmd_helpers[IPA_IPV6CT_TABLE_FLAGS],
table_indx,
IPA_IPV6CT_BASE_TBL,
IPA_IPV6CT_EXPN_TBL,
ipv6ct_table->mem_desc.addr_offset + IPA_IPV6CT_RULE_FLAG_FIELD_OFFSET);
ipa_table_dma_cmd_helper_init(
&ipv6ct_table->table_dma_cmd_helpers[IPA_IPV6CT_TABLE_NEXT_INDEX],
table_indx,
IPA_IPV6CT_BASE_TBL,
IPA_IPV6CT_EXPN_TBL,
ipv6ct_table->mem_desc.addr_offset + IPA_IPV6CT_RULE_NEXT_FIELD_OFFSET);
ipa_table_dma_cmd_helper_init(
&ipv6ct_table->table_dma_cmd_helpers[IPA_IPV6CT_TABLE_PROTOCOL],
table_indx,
IPA_IPV6CT_BASE_TBL,
IPA_IPV6CT_EXPN_TBL,
ipv6ct_table->mem_desc.addr_offset + IPA_IPV6CT_RULE_PROTO_FIELD_OFFSET);
ipv6ct_table->table.dma_help[HELP_UPDATE_HEAD] =
&ipv6ct_table->table_dma_cmd_helpers[IPA_IPV6CT_TABLE_FLAGS];
ipv6ct_table->table.dma_help[HELP_UPDATE_ENTRY] =
&ipv6ct_table->table_dma_cmd_helpers[IPA_IPV6CT_TABLE_NEXT_INDEX];
ipv6ct_table->table.dma_help[HELP_DELETE_HEAD] =
&ipv6ct_table->table_dma_cmd_helpers[IPA_IPV6CT_TABLE_PROTOCOL];
IPADBG("return\n");
}
static int ipa_ipv6ct_post_init_cmd(ipa_ipv6ct_table* ipv6ct_table, uint8_t tbl_index)
{
struct ipa_ioc_ipv6ct_init cmd;
int ret;
IPADBG("\n");
cmd.tbl_index = tbl_index;
cmd.base_table_offset = ipv6ct_table->mem_desc.addr_offset;
cmd.expn_table_offset = cmd.base_table_offset + (ipv6ct_table->table.table_entries * sizeof(ipa_ipv6ct_hw_entry));
/* Driverr/HW expected base table size to be power^2-1 due to H/W hash calculation */
cmd.table_entries = ipv6ct_table->table.table_entries - 1;
cmd.expn_table_entries = ipv6ct_table->table.expn_table_entries;
ret = ioctl(ipv6ct.ipa_desc->fd, IPA_IOC_INIT_IPV6CT_TABLE, &cmd);
if (ret)
{
IPAERR("unable to post init cmd Error: %d IPA fd %d\n", ret, ipv6ct.ipa_desc->fd);
return ret;
}
IPADBG("Posted IPA_IOC_INIT_IPV6CT_TABLE to kernel successfully\n");
return 0;
}
static int ipa_ipv6ct_post_dma_cmd(struct ipa_ioc_nat_dma_cmd* cmd)
{
IPADBG("\n");
cmd->mem_type = IPA_NAT_MEM_IN_DDR;
if (ioctl(ipv6ct.ipa_desc->fd, IPA_IOC_TABLE_DMA_CMD, cmd))
{
IPAERR("ioctl (IPA_IOC_TABLE_DMA_CMD) on fd %d has failed\n",
ipv6ct.ipa_desc->fd);
return -EIO;
}
IPADBG("posted IPA_IOC_TABLE_DMA_CMD to kernel successfully\n");
return 0;
}
void ipa_ipv6ct_dump_table(uint32_t table_handle)
{
ipa_ipv6ct_table* ipv6ct_table;
if (ipv6ct.ipa_desc->ver < IPA_HW_v4_0)
{
IPAERR("IPv6 connection tracking isn't supported for IPA version %d\n", ipv6ct.ipa_desc->ver);
return;
}
if (table_handle == IPA_TABLE_INVALID_ENTRY || table_handle > IPA_IPV6CT_MAX_TBLS)
{
IPAERR("invalid parameters passed %d\n", table_handle);
return;
}
if (pthread_mutex_lock(&ipv6ct_mutex))
{
IPAERR("unable to lock the ipv6ct mutex\n");
return;
}
ipv6ct_table = &ipv6ct.tables[table_handle - 1];
if (!ipv6ct_table->mem_desc.valid)
{
IPAERR("invalid table handle %d\n", table_handle);
goto unlock;
}
/* Prevents interleaving with later kernel printouts. Flush doesn't help. */
sleep(1);
ipa_read_debug_info(IPA_IPV6CT_DEBUG_FILE_PATH);
ipa_read_debug_info(IPA_UC_ACT_DEBUG_FILE_PATH);
sleep(1);
unlock:
if (pthread_mutex_unlock(&ipv6ct_mutex))
IPAERR("unable to unlock the ipv6ct mutex\n");
}
/**
* ipa_ipv6ct_add_uc_act_entry() - add uc activation entry
* @u: [in] structure specifying the uC activation entry
*
* Returns: 0 On Success, negative on failure
*/
int ipa_ipv6ct_add_uc_act_entry(union ipa_ioc_uc_activation_entry *u)
{
IPADBG("\n");
if(ioctl(ipv6ct.ipa_desc->fd, IPA_IOC_ADD_UC_ACT_ENTRY, u))
{
IPAERR("ioctl (IPA_IOC_ADD_UC_ACT_ENTRY) on fd %d has failed\n",
ipv6ct.ipa_desc->fd);
return -EIO;
}
IPADBG("posted IPA_IOC_ADD_UC_ACT_ENTRY to kernel successfully, index %d\n",
u->ipv6_nat.index);
return 0;
}
/**
* ipa_ipv6ct_del_uc_act_entry() - del uc activation entry
* @index: [in] index of the uc activation entry to be removed
*
* Returns: 0 On Success, negative on failure
*/
int ipa_ipv6ct_del_uc_act_entry(uint16_t index)
{
IPADBG("\n");
if(ioctl(ipv6ct.ipa_desc->fd, IPA_IOC_DEL_UC_ACT_ENTRY, index))
{
IPAERR("ioctl (IPA_IOC_DEL_UC_ACT_ENTRY) on fd %d has failed\n",
ipv6ct.ipa_desc->fd);
return -EIO;
}
IPADBG("posted IPA_IOC_DEL_UC_ACT_ENTRY to kernel successfully, index %d\n",
index);
return 0;
}