blob: 8594188116a164ea314fa5fe646d3256355d7065 [file] [log] [blame]
/*
* Copyright (c) 2012, 2015, 2018, The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifdef KVER32
#include <linux/kconfig.h>
#include <generated/autoconf.h>
#else
#include <linux/autoconf.h>
#endif
#include <linux/kthread.h>
#include <linux/udp.h>
#include <linux/rculist_nulls.h>
#include <net/netfilter/nf_conntrack_acct.h>
#include "../nat_helper.h"
#include "../napt_helper.h"
#include "../napt_acl.h"
#include "fal_type.h"
#include "fal_nat.h"
#include "nat_helper_dt.h"
#include "nat_helper_hsl.h"
#define NAPT_CT_POLLING_SEC 5
#define NPAT_CT_POLLING_QUOTA 256
extern int nat_sockopts_init;
extern uint32_t napt_set_default_route(fal_ip4_addr_t dst_addr, fal_ip4_addr_t src_addr);
#ifdef CONFIG_IPV6_HWACCEL
extern uint32_t napt_set_ipv6_default_route(void);
#endif
extern void nat_ipt_sockopts_replace(void);
extern void qcaswitch_hostentry_flush(void);
#define NAPT_BUFFER_HASH_SIZE (NAPT_TABLE_SIZE)
#define NAPT_BUFFER_SIZE ((NAPT_BUFFER_HASH_SIZE)*8)
static a_uint32_t napt_buffer_hash_size = NAPT_TABLE_SIZE;
static a_uint32_t napt_buffer_size = (NAPT_BUFFER_HASH_SIZE)*8;
static a_uint32_t napt_ct_hw_cnt = 0;
static a_uint8_t napt_thread_pending = 0;
a_uint32_t polling_quota = NPAT_CT_POLLING_QUOTA;
extern int nat_chip_ver;
struct napt_ct
{
struct napt_ct *next;
uintptr_t ct_addr;
a_uint64_t ct_packets;
a_uint8_t in_hw;
a_uint16_t hw_index;
a_uint8_t deny;
a_uint64_t last_jiffies;
};
struct napt_debug_counter
{
a_uint32_t interate_cnt;
a_uint32_t care_cnt;
a_uint32_t thresh_low_cnt;
};
struct nhlist_head
{
struct napt_ct *next;
};
static struct nhlist_head *ct_buf_hash_head = NULL;
static struct napt_ct *ct_buf = NULL;
static a_uint32_t ct_buf_ct_cnt = 0;
static struct napt_debug_counter napt_ct_debug_info;
int scan_period = NAPT_CT_POLLING_SEC;
int scan_enable = 1;
int napt_need_clean = 0;
/*pppoe dhcp siwtch pre-handle*/
int wan_switch = 0;
static a_int32_t
napt_hash_buf_init(struct napt_ct **hash, struct nhlist_head **hash_head)
{
a_uint32_t hash_size = NAPT_BUFFER_HASH_SIZE;
a_uint32_t buffer_size = NAPT_BUFFER_SIZE;
*hash = (struct napt_ct *)kmalloc(sizeof(struct napt_ct)*buffer_size, GFP_ATOMIC);
if(!(*hash))
{
HNAT_PRINTK("NAPT INIT ERROR! No Sufficient Memory!");
return -1;
}
*hash_head = (struct nhlist_head *)kmalloc(sizeof(struct nhlist_head)*hash_size, GFP_ATOMIC);
if(!(*hash_head))
{
HNAT_PRINTK("NAPT INIT ERROR! No Sufficient Memory!");
kfree(*hash);
return -1;
}
memset(*hash,0,sizeof(struct napt_ct)*buffer_size);
memset(*hash_head,0,sizeof(struct nhlist_head)*hash_size);
return 0;
}
#include <linux/hash.h>
static a_uint32_t
napt_ct_hash(uintptr_t ct_addr)
{
/*
if(nf_athrs17_hnat_sync_counter_en == 0)
return (((ct_addr) * (0x9e370001UL)) >> 22);
else
return (((ct_addr) * (0x9e370001UL)) >> 29);
*/
return hash_long(ct_addr, NAPT_TABLE_BITS);
}
static struct napt_ct *
napt_hash_add(uintptr_t ct_addr, a_uint32_t *hash_cnt,
struct napt_ct *hash, struct nhlist_head *hash_head)
{
struct napt_ct *entry = 0,*last = 0,*node = 0;
struct nhlist_head *head = 0;
a_uint32_t hash_index = napt_ct_hash(ct_addr);
a_uint32_t i = 0, j = 0, index = 0xffffffff;
if(*hash_cnt >= napt_buffer_size)
{
return NULL;
}
head = &(hash_head[hash_index]);
entry = head->next;
while(entry)
{
if(ct_addr == entry->ct_addr)
{
return entry;
}
else
{
last = entry;
entry = entry->next;
}
}
/*find a valid position*/
for (i = *hash_cnt; i < napt_buffer_size; i++) {
if (hash[i].ct_addr == 0)
break;
}
if (i >= napt_buffer_size) {
for (j = 0; j < *hash_cnt; j++) {
if (hash[j].ct_addr == 0)
break;
}
if (j >= *hash_cnt)
return NULL;
index = j;
} else {
index = i;
}
node = &(hash[index]);
node->ct_addr = ct_addr;
node->ct_packets = 0;
node->in_hw = 0;
node->hw_index = 0;
node->deny = 0;
if(head->next == 0)
{
head->next = node;
}
else
{
last->next = node;
}
(*hash_cnt)++;
return node;
}
static struct napt_ct *
napt_hash_find(uintptr_t ct_addr, a_uint32_t *hash_cnt,
struct napt_ct *hash, struct nhlist_head *hash_head)
{
struct napt_ct *entry = 0;
struct nhlist_head *head = 0;
a_uint32_t hash_index = napt_ct_hash(ct_addr);
if(*hash_cnt == 0)
{
return NULL;
}
#if 0 /* prevent empty entries. */
if(*hash_cnt >= NAPT_BUFFER_SIZE)
{
return NULL;
}
#endif
head = &(hash_head[hash_index]);
if(head->next == 0)
{
return NULL;
}
entry = head->next;
do
{
if(ct_addr == entry->ct_addr)
{
return entry;
}
entry = entry->next;
}
while(entry);
return NULL;
}
static a_int32_t
napt_ct_buf_init(void)
{
return napt_hash_buf_init(&ct_buf, &ct_buf_hash_head);
}
static void
napt_ct_buf_exit(void)
{
if(ct_buf_hash_head)
kfree(ct_buf_hash_head);
if(ct_buf)
kfree(ct_buf);
}
static void
napt_ct_buf_flush(void)
{
ct_buf_ct_cnt = 0;
memset(ct_buf,0,sizeof(struct napt_ct)*NAPT_BUFFER_SIZE);
memset(ct_buf_hash_head,0,sizeof(struct nhlist_head)*NAPT_BUFFER_HASH_SIZE);
}
static a_uint32_t
napt_ct_cnt_get(void)
{
return ct_buf_ct_cnt;
}
static struct napt_ct *
napt_ct_buf_ct_find(uintptr_t ct_addr)
{
return napt_hash_find(ct_addr, &ct_buf_ct_cnt,
ct_buf, ct_buf_hash_head);
}
static a_uint64_t
napt_ct_buf_pkts_get(struct napt_ct *napt_ct)
{
return napt_ct->ct_packets;
}
static void
napt_ct_buf_pkts_update(struct napt_ct *napt_ct, a_uint64_t packets)
{
napt_ct->ct_packets = packets;
}
static a_uint8_t
napt_ct_buf_deny_get(struct napt_ct *napt_ct)
{
return napt_ct->deny;
}
static void
napt_ct_buf_deny_set(struct napt_ct *napt_ct, a_uint8_t deny)
{
napt_ct->deny = deny;
}
static void
napt_ct_buf_deny_clear(struct napt_ct *napt_ct)
{
napt_ct->deny = 0;
}
static a_uint8_t
napt_ct_buf_in_hw_get(struct napt_ct *napt_ct, a_uint16_t *hw_index)
{
*hw_index = napt_ct->hw_index;
return napt_ct->in_hw;
}
static void
napt_ct_buf_in_hw_set(struct napt_ct *napt_ct, a_uint16_t hw_index)
{
napt_ct->in_hw = 1;
napt_ct->hw_index = hw_index;
}
static void
napt_ct_buf_in_hw_clear(struct napt_ct *napt_ct)
{
napt_ct->in_hw = 0;
napt_ct->hw_index = 0;
}
static void
napt_ct_buf_ct_info_clear(struct napt_ct *napt_ct)
{
a_uint32_t hash_index = napt_ct_hash(napt_ct->ct_addr);
struct nhlist_head *head = 0;
struct napt_ct *entry = 0, *last = 0;
head = &ct_buf_hash_head[hash_index];
entry = head->next;
while (entry && entry != napt_ct) {
last = entry;
entry = entry->next;
}
if (last == 0)
head->next = napt_ct->next;
else
last->next = napt_ct->next;
napt_ct->ct_addr = 0;
napt_ct->ct_packets = 0;
napt_ct->deny = 0;
napt_ct->next = NULL;
}
static struct napt_ct *
napt_ct_buf_ct_add(uintptr_t ct_addr)
{
struct napt_ct *napt_ct;
napt_ct = napt_hash_add(ct_addr, &ct_buf_ct_cnt,
ct_buf, ct_buf_hash_head);
if(napt_ct)
{
/*ct pkts initial*/
napt_ct_buf_pkts_update(napt_ct, NAPT_CT_PKTS_GET(ct_addr));
}
return napt_ct;
}
#define NAPT_CT_PERMANENT_DENY 5
static uintptr_t napt_ct_addr[NAPT_TABLE_SIZE] = {0};
a_uint32_t napt_cookie[NAPT_TABLE_SIZE*2] = {0};
a_uint32_t packet_thres_base = 50;
static a_uint64_t packets_bdir_total = 0;
static a_uint64_t packets_bdir_thres = 0;
static inline a_int32_t
before(a_uint64_t seq1, a_uint64_t seq2)
{
return ((int64_t)(seq1-seq2) < 0);
}
static a_uint8_t
napt_ct_pkts_reach_thres(uintptr_t ct_addr, struct napt_ct *napt_ct,
a_uint8_t pkts_sum)
{
a_uint64_t packets_bdir_old = napt_ct_buf_pkts_get(napt_ct);
a_uint64_t packets_bdir_new = NAPT_CT_PKTS_GET(ct_addr);
if(pkts_sum)
{
if(packets_bdir_new > packets_bdir_old)
{
packets_bdir_total += (packets_bdir_new - packets_bdir_old);
}
}
napt_ct_buf_pkts_update(napt_ct, packets_bdir_new);
HNAT_PRINTK("<%s> ct:%x packets_bdir_old:%lld ==> packets_bdir_new:%lld, thresh:%lld\n",
__func__, ct_addr, packets_bdir_old, packets_bdir_new, packets_bdir_thres);
if(before((packets_bdir_old+packets_bdir_thres), packets_bdir_new))
{
return 1;
}
return 0;
}
a_uint8_t napt_dnat_flow_find_del(
napt_entry_t *napt,
fal_napt_entry_t *entry)
{
fal_napt_entry_t tmp_entry;
napt_entry_t tmp_napt;
a_int32_t ret;
memset(&tmp_entry, 0, sizeof(tmp_entry));
memset(&tmp_napt, 0, sizeof(tmp_napt));
tmp_napt = *napt;
tmp_napt.src_addr = 0;
ret = napt_hw_get(&tmp_napt, &tmp_entry);
if(!ret) {
napt_hw_del(&tmp_napt);
*entry = tmp_entry;
return 0;
}
return 1;
}
a_uint8_t napt_snat_flow_find_del(
napt_entry_t *napt,
fal_napt_entry_t *entry)
{
fal_napt_entry_t tmp_entry;
napt_entry_t tmp_napt;
a_int32_t ret;
memset(&tmp_entry, 0, sizeof(tmp_entry));
memset(&tmp_napt, 0, sizeof(tmp_napt));
tmp_napt = *napt;
tmp_napt.trans_addr = 0;
ret = napt_hw_get(&tmp_napt, &tmp_entry);
if(!ret) {
napt_hw_del(&tmp_napt);
*entry = tmp_entry;
return 0;
}
return 1;
}
static a_int32_t
napt_ct_hw_add(uintptr_t ct_addr, a_uint16_t *hw_index)
{
napt_entry_t napt = {0};
a_uint32_t index, i, dcookie = 0, scookie = 0;
fal_napt_entry_t entry;
a_uint8_t ret = 0;
sw_error_t rv = SW_OK;
if (!ct_addr)
return -1;
NAPT_CT_TO_HW_ENTRY(ct_addr, &napt);
if(nat_hw_pub_ip_add(napt.trans_addr, &index) == 0)
{
napt.trans_addr = index;
}
else
{
HNAT_ERR_PRINTK("####%s##nat_hw_pub_ip_add fail!\n", __func__);
return -1;
}
if (((nat_chip_ver & 0xffff)>>8) == NAT_CHIP_VER_DESS) {
ret = napt_dnat_flow_find_del(&napt, &entry);
if(!ret) {
dcookie = entry.flow_cookie;
}
ret = napt_snat_flow_find_del(&napt, &entry);
if(!ret) {
scookie = entry.flow_cookie;
}
}
rv = napt_hw_add(&napt);
if(rv == 0)
{
HNAT_PRINTK("%s: success entry_id:0x%x ct 0x%x\n", __func__, napt.entry_id, ct_addr);
if(napt_ct_addr[napt.entry_id])
{
HNAT_ERR_PRINTK("fault: napt HW:%x can not be overwrited!\n",
napt.entry_id);
}
else
{
napt_ct_addr[napt.entry_id] = ct_addr;
*hw_index = napt.entry_id;
// Added from 1.0.7 for default route.
napt_set_default_route(napt.dst_addr, napt.src_addr);
if (((nat_chip_ver & 0xffff)>>8) == NAT_CHIP_VER_DESS) {
i = napt.entry_id;
napt_cookie[i*2] = dcookie;
napt_cookie[i*2+1] = scookie;
}
napt_ct_hw_cnt++;
return 0;
}
}
else
{
HNAT_PRINTK("%s:fail rv:%d entry_id:%x(maybe full)\n",
__func__,rv, napt.entry_id);
nat_hw_pub_ip_del(napt.trans_addr);
}
return -1;
}
static a_int32_t
napt_ct_hw_del (napt_entry_t *napt)
{
if(napt_hw_del(napt)!= 0)
{
HNAT_ERR_PRINTK("%s: isis_napt_del fail\n", __func__);
return -1;
}
napt_ct_hw_cnt--;
if(nat_hw_pub_ip_del(napt->trans_addr) != 0)
{
HNAT_ERR_PRINTK("%s: public_ip_del fail\n", __func__);
//return -1;
}
return 0;
}
void nat_helper_cookie_del(a_uint32_t hw_index)
{
struct napt_ct *napt_ct = NULL;
uintptr_t ct_addr;
ct_addr = napt_ct_addr[hw_index];
if(ct_addr) {
napt_ct = napt_ct_buf_ct_find(ct_addr);
}
if(napt_ct)
{
napt_ct_buf_in_hw_clear(napt_ct);
if(napt_ct_buf_deny_get(napt_ct) != NAPT_CT_PERMANENT_DENY)
{
napt_ct_buf_ct_info_clear(napt_ct);
}
NAPT_CT_AGING_ENABLE(napt_ct_addr[hw_index]);
}
napt_ct_addr[hw_index] = 0;
napt_cookie[hw_index*2] = 0;
napt_cookie[hw_index*2+1] = 0;
HNAT_INFO_PRINTK("nat_helper_cookie_del for index 0x%x\n", hw_index);
}
static a_int32_t
napt_ct_del(struct napt_ct *napt_ct, napt_entry_t *napt)
{
a_uint16_t hw_index = napt->entry_id;
HNAT_PRINTK("%s: 0x%x ct 0x%x\n", __FUNCTION__, hw_index, napt_ct_addr[hw_index]);
if(napt_ct_hw_del(napt) != 0)
{
return -1;
}
NAPT_CT_AGING_ENABLE(napt_ct_addr[hw_index]);
napt_ct_addr[hw_index] = 0;
if(napt_cookie[hw_index*2]) {
napt_hw_dnat_cookie_add(napt, napt_cookie[hw_index*2]);
napt_cookie[hw_index*2] = 0;
}
if(napt_cookie[hw_index*2+1]) {
napt_hw_snat_cookie_add(napt, napt_cookie[hw_index*2+1]);
napt_cookie[hw_index*2+1] = 0;
}
if(napt_ct)
{
napt_ct_buf_in_hw_clear(napt_ct);
if(napt_ct_buf_deny_get(napt_ct) != NAPT_CT_PERMANENT_DENY)
{
napt_ct_buf_ct_info_clear(napt_ct);
}
}
return 0;
}
static a_int32_t
napt_ct_del_by_index (struct napt_ct *napt_ct, a_uint16_t hw_index)
{
napt_entry_t napt = {0};
if(napt_hw_get_by_index(&napt, hw_index) != 0)
{
return -1;
}
return napt_ct_del(napt_ct, &napt);
}
static a_int32_t
napt_ct_in_hw_sanity_check(struct napt_ct *napt_ct, a_uint16_t hw_index)
{
a_uint16_t ct_hw_index = 0;
if(!napt_ct)
{
HNAT_ERR_PRINTK("<%s>hw_index:%d error napt_ct can't find\n",
__func__, hw_index);
return -1;
}
if(napt_ct_buf_in_hw_get(napt_ct, &ct_hw_index) == 0)
{
HNAT_ERR_PRINTK("<%s>hw_index:%d in_hw:0 error\n",
__func__, hw_index);
return -1;
}
if(hw_index != ct_hw_index)
{
HNAT_ERR_PRINTK("<%s>hw_index:%d buf_hw_index:%d\n",
__func__, hw_index, ct_hw_index);
return -1;
}
return 0;
}
void
napt_ct_hw_aging(void)
{
#define NAPT_AGEOUT_STATUS 1
uintptr_t ct_addr;
a_uint32_t cnt= 0;
napt_entry_t napt = {0};
if(napt_hw_first_by_age(&napt, NAPT_AGEOUT_STATUS) != 0)
{
return;
}
do
{
a_uint16_t hw_index = napt.entry_id;
struct napt_ct *napt_ct = NULL;
ct_addr = napt_ct_addr[hw_index];
if(ct_addr)
{
napt_ct = napt_ct_buf_ct_find(ct_addr);
if(napt_ct_in_hw_sanity_check(napt_ct, hw_index) != 0)
{
HNAT_ERR_PRINTK("<%s> napt_ct_in_hw_sanity_check fail\n", __func__);
continue;
}
if((nf_athrs17_hnat_sync_counter_en == 0) && (napt_ct_pkts_reach_thres(ct_addr, napt_ct, 0)))
{
printk("<aging>set PERMANENT deny ct:%lx\n", (a_ulong_t)ct_addr);
napt_ct_buf_deny_set(napt_ct, NAPT_CT_PERMANENT_DENY);
}
}
else
{
HNAT_ERR_PRINTK("<aging> error: in_hw but ct = NULL hw_index:%x\n", hw_index);
}
napt_ct_del(napt_ct, &napt);
HNAT_INFO_PRINTK("ct:%x aged!\n", ct_addr);
cnt++;
if ((cnt % polling_quota) == 0) {
NAPT_CT_TASK_SLEEP(1);
}
}
while(napt_hw_next_by_age(&napt, NAPT_AGEOUT_STATUS) != -1);
#if 0
if(napt_hw_used_count_get() == 0)
{
nat_hw_prv_base_update_enable();
}
#endif
return;
}
static a_uint32_t
napt_ct_counter_sync(a_uint32_t hw_index)
{
napt_entry_t napt = {0};
struct nf_conn *ct = NULL;
struct nf_conn_counter *cct = NULL;
a_uint64_t delta_jiffies = 0, now_jiffies;
a_uint32_t ct_addr = napt_ct_addr[hw_index];
struct napt_ct *napt_ct;
if((nf_athrs17_hnat_sync_counter_en == 0) || (napt_ct_addr[hw_index] == 0) || (hw_index >= NAPT_TABLE_SIZE))
return -1;
ct = (struct nf_conn *)(uintptr_t)napt_ct_addr[hw_index];
cct = (struct nf_conn_counter *)nf_conn_acct_find(ct);
napt_ct = napt_ct_buf_ct_find(ct_addr);
if (napt_ct) {
now_jiffies = (a_uint64_t)get_jiffies_64();
delta_jiffies = now_jiffies - napt_ct->last_jiffies;
napt_ct->last_jiffies = now_jiffies;
}
if (!test_bit(IPS_FIXED_TIMEOUT_BIT, &ct->status)) {
ct->timeout.expires += delta_jiffies;
}
if((cct != NULL) && (napt_hw_get_by_index(&napt, hw_index) == 0))
{
spin_lock_bh(&ct->lock);
if ((ct->status & IPS_NAT_MASK) == IPS_SRC_NAT) {
atomic64_add(napt.egress_packet, &cct[IP_CT_DIR_ORIGINAL].packets);
atomic64_add(napt.egress_byte, &cct[IP_CT_DIR_ORIGINAL].bytes);
atomic64_add(napt.ingress_packet, &cct[IP_CT_DIR_REPLY].packets);
atomic64_add(napt.ingress_byte, &cct[IP_CT_DIR_REPLY].bytes);
} else {
atomic64_add(napt.ingress_packet, &cct[IP_CT_DIR_ORIGINAL].packets);
atomic64_add(napt.ingress_byte, &cct[IP_CT_DIR_ORIGINAL].bytes);
atomic64_add(napt.egress_packet, &cct[IP_CT_DIR_REPLY].packets);
atomic64_add(napt.egress_byte, &cct[IP_CT_DIR_REPLY].bytes);
}
spin_unlock_bh(&ct->lock);
HNAT_PRINTK("original packets:0x%llx bytes:0x%llx\n",
cct[IP_CT_DIR_ORIGINAL].packets, cct[IP_CT_DIR_ORIGINAL].bytes);
HNAT_PRINTK("reply packets:0x%llx bytes:0x%llx\n",
cct[IP_CT_DIR_REPLY].packets, cct[IP_CT_DIR_REPLY].bytes);
}
return 0;
}
static a_int32_t
napt_ct_timer_update(a_uint32_t hw_index)
{
struct nf_conn *ct = NULL;
struct nf_conn_counter *cct = NULL;
a_uint64_t delta_jiffies = 0, now_jiffies;
uintptr_t ct_addr = napt_ct_addr[hw_index];
struct napt_ct *napt_ct;
if((napt_ct_addr[hw_index] == 0) || (hw_index >= NAPT_TABLE_SIZE))
return -1;
ct = (struct nf_conn *)(uintptr_t)napt_ct_addr[hw_index];
cct = (struct nf_conn_counter *)nf_conn_acct_find(ct);
napt_ct = napt_ct_buf_ct_find(ct_addr);
if (napt_ct) {
now_jiffies = (a_uint64_t)get_jiffies_64();
delta_jiffies = now_jiffies - napt_ct->last_jiffies;
napt_ct->last_jiffies = now_jiffies;
}
if (!test_bit(IPS_FIXED_TIMEOUT_BIT, &ct->status)) {
ct->timeout.expires += delta_jiffies;
}
return 0;
}
void napt_ct_counter_decrease(void)
{
ct_buf_ct_cnt--;
}
#define NAPT_INVALID_CT_NEED_HW_CLEAR(hw_index) \
((napt_ct_valid[hw_index] == 0) && \
(napt_ct_addr[hw_index] != 0))
static a_uint32_t
napt_ct_hw_sync(a_uint8_t napt_ct_valid[])
{
a_uint16_t hw_index;
a_uint32_t napt_ct_offload_cnt = 0;
for(hw_index = 0; hw_index < NAPT_TABLE_SIZE; hw_index++)
{
#if 0
napt_ct_counter_sync(hw_index);
#endif
if(NAPT_INVALID_CT_NEED_HW_CLEAR(hw_index))
{
uintptr_t ct_addr = napt_ct_addr[hw_index];
struct napt_ct *napt_ct = napt_ct_buf_ct_find(ct_addr);
HNAT_INFO_PRINTK("should hw clear for %d\n", hw_index);
if(napt_ct_in_hw_sanity_check(napt_ct, hw_index) != 0)
{
HNAT_ERR_PRINTK("<%s> napt_ct_in_hw_sanity_check fail\n", __func__);
continue;
}
if(napt_ct_del_by_index(napt_ct, hw_index) == 0)
{
napt_ct_buf_deny_clear(napt_ct);
}
else
{
HNAT_ERR_PRINTK("<napt_ct_hw_sync>hw_index:%d napt_hw_del_by_index fail\n",
hw_index);
}
}
if(napt_ct_valid[hw_index])
{
napt_ct_offload_cnt++;
}
}
return napt_ct_offload_cnt;
}
static void
napt_ct_frag_hw_yield(struct napt_ct *napt_ct, a_uint16_t hw_index)
{
napt_entry_t napt = {0};
/*os and hw are both traffic; hw offload giveup*/
if(napt_hw_get_by_index(&napt, hw_index) == 0)
{
if(napt.status == 0xe)
{
a_uint8_t deny = napt_ct_buf_deny_get(napt_ct);
napt_ct_buf_deny_set(napt_ct, (++deny));
if(deny >= NAPT_CT_PERMANENT_DENY)
{
/*os service only*/
HNAT_ERR_PRINTK("<napt_ct_frag_hw_yield> hw service deny\n");
napt_ct_del(napt_ct, &napt);
}
//printk("<napt_ct_frag_hw_yield> deny:%d\n", deny);
}
}
}
#define NAPT_CT_IS_REUSED_BY_OS(in_hw, ct_addr) ((in_hw) && \
NAPT_CT_AGING_IS_ENABLE(ct_addr))
static a_int32_t
napt_ct_check_add_one(uintptr_t ct_addr, a_uint8_t *napt_ct_valid)
{
struct napt_ct *napt_ct = NULL;
a_uint16_t hw_index;
a_uint8_t in_hw;
if((napt_ct = napt_ct_buf_ct_find(ct_addr)) == NULL)
{
if((napt_ct = napt_ct_buf_ct_add(ct_addr)) == NULL)
{
HNAT_ERR_PRINTK("<napt_ct_scan> error hash full\n");
return -1;
}
}
if(napt_ct_buf_deny_get(napt_ct) >= NAPT_CT_PERMANENT_DENY)
{
HNAT_INFO_PRINTK("<napt_ct_scan> ct:%x is PERMANENT deny\n",
ct_addr);
return -1;
}
else
{
if (napt_ct_pkts_reach_thres(ct_addr, napt_ct, 1))
{
if(napt_ct_buf_in_hw_get(napt_ct, &hw_index))
{
//printk("<napt_ct_scan> ct:%x* is exist\n", ct_addr);
if(nf_athrs17_hnat_sync_counter_en == 0)
{
HNAT_ERR_PRINTK("<napt_ct_scan> ct:%x* is exist\n", ct_addr);
napt_ct_frag_hw_yield(napt_ct, hw_index);
}
}
else
{
if(napt_ct_hw_add(ct_addr, &hw_index) == 0)
{
NAPT_CT_AGING_DISABLE(ct_addr);
napt_ct->last_jiffies = get_jiffies_64();
napt_ct_buf_in_hw_set(napt_ct, hw_index);
#ifdef NAT_TODO
ct->in_hnat = 1; /* contrack in HNAT now. */
#endif
}
}
} else {
HNAT_INFO_PRINTK("can not reach thres for napt_ct=%pK\n", napt_ct);
}
in_hw = napt_ct_buf_in_hw_get(napt_ct, &hw_index);
if(in_hw)
{
if(!NAPT_CT_IS_REUSED_BY_OS(in_hw, ct_addr))
{
napt_ct_valid[hw_index] = 1;
}
}
}
return 0;
}
static void
napt_ct_pkts_thres_calc_init(void)
{
packets_bdir_total = 0;
packets_bdir_thres = packet_thres_base;
}
a_uint64_t
uint64_div_uint32(a_uint64_t div, a_uint32_t base)
{
register a_uint32_t i;
a_uint64_t result;
union
{
a_uint64_t n64[2];
struct
{
a_uint32_t l0;// 0
a_uint32_t h0;// 1
a_uint32_t l1;// 2
a_uint32_t h1;// 3
} n32;
} n;
if(base == 0)
{
return 0;
}
if(div < base)
{
return 0;
}
n.n64[0] = div;
n.n64[1] = 0;
result = 0;
i = 0;
//if div is 32bits, set start from 32
if(n.n32.h0 == 0)
{
n.n32.h0 = n.n32.l0;
n.n32.l0 = 0;
i = 32;
}
//left shift until highest bit
for(; i<64; ++i)
{
if((n.n32.h0 & 0x80000000) == 0x80000000)
{
break;
}
else
{
n.n64[0] = n.n64[0] << 1;
}
}
for (; i<64; ++i)
{
n.n64[1] = (n.n64[1] << 1) + (n.n64[0] >> 63);
n.n64[0] = (n.n64[0] << 1);
result = result << 1 ;
if(n.n64[1] >= base)
{
n.n64[1] = n.n64[1]- base;
++result;
}
}
return result;
}
static void
napt_ct_pkts_thres_calc(a_uint32_t cnt, a_uint32_t napt_ct_offload_cnt)
{
a_uint64_t packets_bdir_avg = 0;
a_uint64_t packets_bdir_thres_temp = 0;
/*ct_avg_pkts* (1+ (ct_offload_cnt/ct_hw_max) )*/
packets_bdir_avg = uint64_div_uint32(packets_bdir_total, cnt);
packets_bdir_thres_temp = packets_bdir_avg +
uint64_div_uint32((packets_bdir_avg *(a_uint64_t)napt_ct_offload_cnt),
NAPT_TABLE_SIZE);
if(packets_bdir_thres_temp > packet_thres_base)
{
packets_bdir_thres = packets_bdir_thres_temp;
}
//HNAT_ERR_PRINTK("###<%s> total:%lld cnt:%d avg:%lld threshold:%lld###\n", __func__,
// packets_bdir_total, cnt, packets_bdir_avg, packets_bdir_thres);
HNAT_INFO_PRINTK("calc pkts avg:%lld offload_cnt:%d threshold:%lld\n",
packets_bdir_avg, napt_ct_offload_cnt, packets_bdir_thres);
}
#define NAPT_CT_SHOULD_CARE(ct) ((ct) && \
NAPT_CT_TYPE_IS_NAT(ct) && \
!NAPT_CT_TYPE_IS_NAT_ALG(ct) && \
NAPT_CT_INTF_EXPECTED(ct) && \
NAPT_CT_STATUS_IS_ESTAB(ct) &&\
nat_hw_prv_base_is_match( \
NAPT_CT_PRIV_IP_GET(ct)))
a_uint8_t napt_ct_valid_tbl[NAPT_TABLE_SIZE] = {0};
static a_int32_t
napt_ct_check_add(void)
{
uintptr_t ct_addr = 0;
a_uint32_t ct_buf_valid_cnt = 0, care_cnt = 0, ct_cnt = 0;
static a_uint32_t hash = 0;
uintptr_t iterate = 0;
a_uint32_t napt_ct_offload_cnt = 0;
a_uint16_t hw_index;
napt_ct_pkts_thres_calc_init();
NAPT_CT_LIST_LOCK();
while((ct_addr = NAPT_CT_LIST_ITERATE(&hash, &iterate)))
{
ct_cnt++;
if (NAPT_CT_SHOULD_CARE(ct_addr))
{
if(napt_ct_check_add_one(ct_addr, napt_ct_valid_tbl) != -1)
{
ct_buf_valid_cnt++;
}
care_cnt++;
if (care_cnt >= polling_quota) {
break;
}
}
}
NAPT_CT_LIST_UNLOCK();
HNAT_INFO_PRINTK("ct_cnt=0x%x, care_cnt=0x%x\n", ct_cnt, care_cnt);
if (!ct_addr) {
napt_ct_offload_cnt = napt_ct_hw_sync(napt_ct_valid_tbl);
napt_ct_pkts_thres_calc(ct_buf_valid_cnt, napt_ct_offload_cnt);
memset(napt_ct_valid_tbl, 0, NAPT_TABLE_SIZE);
}
for(hw_index = 0; hw_index < NAPT_TABLE_SIZE; hw_index++) {
if (nf_athrs17_hnat_sync_counter_en)
napt_ct_counter_sync(hw_index);
else
napt_ct_timer_update(hw_index);
}
return ct_buf_valid_cnt;
}
static a_int32_t
napt_ct_add(uintptr_t ct_addr, a_uint8_t *napt_ct_valid)
{
struct napt_ct *napt_ct;
if((napt_ct = napt_ct_buf_ct_add(ct_addr)) == NULL)
{
HNAT_ERR_PRINTK("<napt_ct_buffer_update> error hash full\n");
return -1;
}
return 0;
}
static a_int32_t
napt_ct_buffer_ct_status_update(void)
{
uintptr_t ct_addr = 0;
a_uint32_t hash = 0;
uintptr_t iterate = 0;
NAPT_CT_LIST_LOCK();
while((ct_addr = NAPT_CT_LIST_ITERATE(&hash, &iterate)))
{
if (NAPT_CT_SHOULD_CARE(ct_addr))
{
napt_ct_add(ct_addr, NULL);
}
}
NAPT_CT_LIST_UNLOCK();
return 0;
}
static void
napt_ct_buffer_hw_status_update(void)
{
a_uint16_t hw_index;
for(hw_index = 0; hw_index < NAPT_TABLE_SIZE; hw_index++)
{
uintptr_t ct_addr = napt_ct_addr[hw_index];
if(ct_addr)
{
struct napt_ct *napt_ct = napt_ct_buf_ct_find(ct_addr);
if(napt_ct)
{
napt_ct_buf_in_hw_set(napt_ct, hw_index);
}
else
{
if(napt_ct_del_by_index(napt_ct, hw_index) != 0)
{
HNAT_ERR_PRINTK("<%s>hw_index:%d napt_ct_del_by_index fail\n",
__func__, hw_index);
}
}
}
}
return;
}
static void
napt_ct_buffer_refresh(void)
{
HNAT_INFO_PRINTK("napt_ct_buffer_refresh\n");
napt_ct_buf_flush();
napt_ct_buffer_ct_status_update();
napt_ct_buffer_hw_status_update();
}
static void
napt_ct_buffer_refresh_check(a_uint32_t ct_buf_valid_cnt)
{
#define NAPT_CT_BUF_REFRESH_THRES 1000
HNAT_INFO_PRINTK("ct_buffer_hash_cnt:%d cnt:%d max:%d\n",
napt_ct_cnt_get(), ct_buf_valid_cnt/2, NAPT_CT_BUF_REFRESH_THRES);
if((napt_ct_cnt_get() - ct_buf_valid_cnt/2) > NAPT_CT_BUF_REFRESH_THRES)
{
napt_ct_buffer_refresh();
}
}
static void
napt_ct_hw_exit(void)
{
a_uint8_t napt_ct_valid[NAPT_TABLE_SIZE];
/*set all ct invalid to cleanup*/
memset(napt_ct_valid, 0, sizeof(napt_ct_valid));
napt_ct_hw_sync(napt_ct_valid);
}
void
napt_ct_scan(void)
{
a_uint32_t ct_buf_valid_cnt = 0;
ct_buf_valid_cnt = napt_ct_check_add();
napt_ct_buffer_refresh_check(ct_buf_valid_cnt);
}
void napt_wan_switch_prehandle(void)
{
if (wan_switch) {
napt_thread_pending = 1;
L3_STATUS_SET(0, A_FALSE);
napt_ct_hw_exit();
napt_hw_flush();
qcaswitch_hostentry_flush();
droute_del_acl_rules();
ipv6_droute_del_acl_rules();
if (nf_athrs17_hnat_wan_type == NF_S17_WAN_TYPE_PPPOE)
pppoe_del_acl_rules();
#ifdef MULTIROUTE_WR
ip_conflict_del_acl_rules();
#endif
if_mac_cleanup();
L3_STATUS_SET(0, A_TRUE);
napt_thread_pending = 0;
}
}
static a_int32_t
napt_ct_init(void)
{
if(nf_athrs17_hnat_sync_counter_en == 0)
{
napt_buffer_hash_size = NAPT_TABLE_SIZE;
napt_buffer_size = NAPT_TABLE_SIZE*8;
}
else
{
napt_buffer_hash_size = 8;
napt_buffer_size = NAPT_TABLE_SIZE;
}
napt_hw_mode_init();
if(napt_ct_buf_init() != 0)
{
HNAT_ERR_PRINTK("*****napt_ct_buf_init fail*******\n");
return -1;
}
return 0;
}
static a_int32_t
napt_ct_exit(void)
{
napt_hw_mode_cleanup();
napt_ct_hw_exit();
napt_ct_buf_exit();
return 0;
}
static a_int32_t
napt_ct_scan_thread(void *param)
{
#define NAPT_CT_AGING_SEC 20
#define ARP_CHECK_AGING_SEC 40
a_uint32_t times = (NAPT_CT_AGING_SEC/scan_period);
a_uint32_t arp_check_time = (ARP_CHECK_AGING_SEC/scan_period);
// a_bool_t l3_enable;
if(napt_ct_init() != 0)
{
return 0;
}
while(1)
{
if(!nat_sockopts_init) {
nat_ipt_sockopts_replace();
}
if (napt_thread_pending) {
NAPT_CT_TASK_SLEEP(scan_period);
continue;
}
if (scan_enable) {
if((--times) == 0)
{
HNAT_PRINTK("[ct hw aging start]\n");
napt_ct_hw_aging();
HNAT_PRINTK("[ct hw aging end]\n");
times = (NAPT_CT_AGING_SEC/scan_period);
}
if((--arp_check_time) == 0)
{
host_check_aging();
arp_check_time = (ARP_CHECK_AGING_SEC/scan_period);
}
if (times != NAPT_CT_AGING_SEC/scan_period) {
HNAT_PRINTK("[ct scan start]\n");
napt_ct_scan();
HNAT_PRINTK("[ct scan end]\n");
}
} else {
if (napt_need_clean) {
napt_need_clean = 0;
napt_ct_hw_exit();
}
}
#ifdef ISISC /* only for S17c */
#ifdef CONFIG_IPV6_HWACCEL
napt_set_ipv6_default_route();
#endif
#endif
if (NAPT_CT_TASK_SHOULD_STOP()) {
printk("should stop!\n");
break;
}
NAPT_CT_TASK_SLEEP(scan_period);
}
napt_ct_exit();
return 0;
}
void napt_helper_show(void)
{
a_uint16_t hw_index;
for(hw_index = 0; hw_index < NAPT_TABLE_SIZE; hw_index++) {
if (napt_ct_addr[hw_index]) {
printk("index[%d] exist: 0x%lx\n", hw_index, (a_ulong_t)napt_ct_addr[hw_index]);
}
}
printk("current hardware offload count: 0x%x\n", napt_ct_hw_cnt);
printk("interate:0x%x, care:0x%x, low_thresh=0x%x\n",
napt_ct_debug_info.interate_cnt,
napt_ct_debug_info.care_cnt,
napt_ct_debug_info.thresh_low_cnt);
}
void
napt_helper_init(void)
{
const char napt_thread_name[] = "napt_ct_scan";
NAPT_CT_TASK_START(napt_ct_scan_thread, napt_thread_name);
}
void
napt_helper_exit(void)
{
NAPT_CT_TASK_STOP();
}