#include"aicwf_tcp_ack.h"
|
//#include"rwnx_tx.h"
|
//#include "aicwf_tcp_ack.h"
|
#include"rwnx_defs.h"
|
extern int intf_tx(struct rwnx_hw *priv,struct msg_buf *msg);
|
struct msg_buf *intf_tcp_alloc_msg(struct msg_buf *msg)
|
{
|
//printk("%s \n",__func__);
|
int len=sizeof(struct msg_buf) ;
|
msg = kzalloc(len , GFP_KERNEL);
|
if(!msg)
|
printk("%s: alloc failed \n", __func__);
|
memset(msg,0,len);
|
return msg;
|
}
|
|
void intf_tcp_drop_msg(struct rwnx_hw *priv,
|
struct msg_buf *msg)
|
{
|
//printk("%s \n",__func__);
|
if (msg->skb)
|
dev_kfree_skb_any(msg->skb);
|
|
kfree(msg);
|
}
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
|
void tcp_ack_timeout(unsigned long data)
|
#else
|
void tcp_ack_timeout(struct timer_list *t)
|
#endif
|
{
|
//printk("%s \n",__func__);
|
struct tcp_ack_info *ack_info;
|
struct msg_buf *msg;
|
struct tcp_ack_manage *ack_m = NULL;
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
|
ack_info = (struct tcp_ack_info *)data;
|
#else
|
ack_info = container_of(t,struct tcp_ack_info,timer);
|
#endif
|
|
ack_m = container_of(ack_info, struct tcp_ack_manage,
|
ack_info[ack_info->ack_info_num]);
|
|
write_seqlock_bh(&ack_info->seqlock);
|
msg = ack_info->msgbuf;
|
if (ack_info->busy && msg && !ack_info->in_send_msg) {
|
ack_info->msgbuf = NULL;
|
ack_info->drop_cnt = 0;
|
ack_info->in_send_msg = msg;
|
write_sequnlock_bh(&ack_info->seqlock);
|
intf_tx(ack_m->priv, msg);//send skb
|
//ack_info->in_send_msg = NULL;//add by dwx
|
//write_sequnlock_bh(&ack_info->seqlock);
|
//intf_tx(ack_m->priv, msg);
|
return;
|
}
|
write_sequnlock_bh(&ack_info->seqlock);
|
}
|
|
void tcp_ack_init(struct rwnx_hw *priv)
|
{
|
int i;
|
struct tcp_ack_info *ack_info;
|
struct tcp_ack_manage *ack_m = &priv->ack_m;
|
|
printk("%s \n",__func__);
|
memset(ack_m, 0, sizeof(struct tcp_ack_manage));
|
ack_m->priv = priv;
|
spin_lock_init(&ack_m->lock);
|
atomic_set(&ack_m->max_drop_cnt, TCP_ACK_DROP_CNT);
|
ack_m->last_time = jiffies;
|
ack_m->timeout = msecs_to_jiffies(ACK_OLD_TIME);
|
|
for (i = 0; i < TCP_ACK_NUM; i++) {
|
ack_info = &ack_m->ack_info[i];
|
ack_info->ack_info_num = i;
|
seqlock_init(&ack_info->seqlock);
|
ack_info->last_time = jiffies;
|
ack_info->timeout = msecs_to_jiffies(ACK_OLD_TIME);
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
|
setup_timer(&ack_info->timer, tcp_ack_timeout,
|
(unsigned long)ack_info);
|
#else
|
timer_setup(&ack_info->timer,tcp_ack_timeout,0);
|
#endif
|
}
|
|
atomic_set(&ack_m->enable, 1);
|
ack_m->ack_winsize = MIN_WIN;
|
}
|
|
void tcp_ack_deinit(struct rwnx_hw *priv)
|
{
|
int i;
|
struct tcp_ack_manage *ack_m = &priv->ack_m;
|
struct msg_buf *drop_msg = NULL;
|
|
printk("%s \n",__func__);
|
atomic_set(&ack_m->enable, 0);
|
|
for (i = 0; i < TCP_ACK_NUM; i++) {
|
drop_msg = NULL;
|
|
write_seqlock_bh(&ack_m->ack_info[i].seqlock);
|
del_timer(&ack_m->ack_info[i].timer);
|
drop_msg = ack_m->ack_info[i].msgbuf;
|
ack_m->ack_info[i].msgbuf = NULL;
|
write_sequnlock_bh(&ack_m->ack_info[i].seqlock);
|
|
if (drop_msg)
|
intf_tcp_drop_msg(priv, drop_msg);//drop skb
|
}
|
}
|
|
int tcp_check_quick_ack(unsigned char *buf,
|
struct tcp_ack_msg *msg)
|
{
|
int ip_hdr_len;
|
unsigned char *temp;
|
struct ethhdr *ethhdr;
|
struct iphdr *iphdr;
|
struct tcphdr *tcphdr;
|
|
ethhdr = (struct ethhdr *)buf;
|
if (ethhdr->h_proto != htons(ETH_P_IP))
|
return 0;
|
iphdr = (struct iphdr *)(ethhdr + 1);
|
if (iphdr->version != 4 || iphdr->protocol != IPPROTO_TCP)
|
return 0;
|
ip_hdr_len = iphdr->ihl * 4;
|
temp = (unsigned char *)(iphdr) + ip_hdr_len;
|
tcphdr = (struct tcphdr *)temp;
|
/* TCP_FLAG_ACK */
|
if (!(temp[13] & 0x10))
|
return 0;
|
|
if (temp[13] & 0x8) {
|
msg->saddr = iphdr->daddr;
|
msg->daddr = iphdr->saddr;
|
msg->source = tcphdr->dest;
|
msg->dest = tcphdr->source;
|
msg->seq = ntohl(tcphdr->seq);
|
return 1;
|
}
|
|
return 0;
|
}
|
|
int is_drop_tcp_ack(struct tcphdr *tcphdr, int tcp_tot_len,
|
unsigned short *win_scale)
|
{
|
//printk("%s \n",__func__);
|
int drop = 1;
|
int len = tcphdr->doff * 4;
|
unsigned char *ptr;
|
|
if(tcp_tot_len > len) {
|
drop = 0;
|
} else {
|
len -= sizeof(struct tcphdr);
|
ptr = (unsigned char *)(tcphdr + 1);
|
|
while ((len > 0) && drop) {
|
int opcode = *ptr++;
|
int opsize;
|
|
switch (opcode) {
|
case TCPOPT_EOL:
|
break;
|
case TCPOPT_NOP:
|
len--;
|
continue;
|
default:
|
opsize = *ptr++;
|
if (opsize < 2)
|
break;
|
if (opsize > len)
|
break;
|
|
switch (opcode) {
|
/* TODO: Add other ignore opt */
|
case TCPOPT_TIMESTAMP:
|
break;
|
case TCPOPT_WINDOW:
|
if (*ptr < 15)
|
*win_scale = (1 << (*ptr));
|
printk("%d\n",*win_scale);
|
break;
|
default:
|
drop = 2;
|
}
|
|
ptr += opsize - 2;
|
len -= opsize;
|
}
|
}
|
}
|
|
return drop;
|
}
|
|
|
/* flag:0 for not tcp ack
|
* 1 for ack which can be drop
|
* 2 for other ack whith more info
|
*/
|
|
int tcp_check_ack(unsigned char *buf,
|
struct tcp_ack_msg *msg,
|
unsigned short *win_scale)
|
{
|
int ret;
|
int ip_hdr_len;
|
int tcp_tot_len;
|
unsigned char *temp;
|
struct ethhdr *ethhdr;
|
struct iphdr *iphdr;
|
struct tcphdr *tcphdr;
|
|
ethhdr =(struct ethhdr *)buf;
|
if (ethhdr->h_proto != htons(ETH_P_IP))
|
return 0;
|
|
iphdr = (struct iphdr *)(ethhdr + 1);
|
if (iphdr->version != 4 || iphdr->protocol != IPPROTO_TCP)
|
return 0;
|
|
ip_hdr_len = iphdr->ihl * 4;
|
temp = (unsigned char *)(iphdr) + ip_hdr_len;
|
tcphdr = (struct tcphdr *)temp;
|
/* TCP_FLAG_ACK */
|
if (!(temp[13] & 0x10))
|
return 0;
|
|
tcp_tot_len = ntohs(iphdr->tot_len) - ip_hdr_len;// tcp total len
|
ret = is_drop_tcp_ack(tcphdr, tcp_tot_len, win_scale);
|
//printk("is drop:%d \n",ret);
|
|
if (ret > 0) {
|
msg->saddr = iphdr->saddr;
|
msg->daddr = iphdr->daddr;
|
msg->source = tcphdr->source;
|
msg->dest = tcphdr->dest;
|
msg->seq = ntohl(tcphdr->ack_seq);
|
msg->win = ntohs(tcphdr->window);
|
}
|
|
return ret;
|
}
|
|
/* return val: -1 for not match, others for match */
|
int tcp_ack_match(struct tcp_ack_manage *ack_m,
|
struct tcp_ack_msg *ack_msg)
|
{
|
int i, ret = -1;
|
unsigned start;
|
struct tcp_ack_info *ack_info;
|
struct tcp_ack_msg *ack;
|
|
for (i = 0; ((ret < 0) && (i < TCP_ACK_NUM)); i++) {
|
ack_info = &ack_m->ack_info[i];
|
do {
|
start = read_seqbegin(&ack_info->seqlock);
|
ret = -1;
|
|
ack = &ack_info->ack_msg;
|
if (ack_info->busy &&
|
ack->dest == ack_msg->dest &&
|
ack->source == ack_msg->source &&
|
ack->saddr == ack_msg->saddr &&
|
ack->daddr == ack_msg->daddr)
|
ret = i;
|
} while(read_seqretry(&ack_info->seqlock, start));
|
}
|
|
return ret;
|
}
|
|
|
void tcp_ack_update(struct tcp_ack_manage *ack_m)
|
{
|
int i;
|
struct tcp_ack_info *ack_info;
|
|
if (time_after(jiffies, ack_m->last_time + ack_m->timeout)) {
|
spin_lock_bh(&ack_m->lock);
|
ack_m->last_time = jiffies;
|
for (i = TCP_ACK_NUM - 1; i >= 0; i--) {
|
ack_info = &ack_m->ack_info[i];
|
write_seqlock_bh(&ack_info->seqlock);
|
if (ack_info->busy &&
|
time_after(jiffies, ack_info->last_time +
|
ack_info->timeout)) {
|
ack_m->free_index = i;
|
ack_m->max_num--;
|
ack_info->busy = 0;
|
}
|
write_sequnlock_bh(&ack_info->seqlock);
|
}
|
spin_unlock_bh(&ack_m->lock);
|
}
|
}
|
|
/* return val: -1 for no index, others for index */
|
int tcp_ack_alloc_index(struct tcp_ack_manage *ack_m)
|
{
|
int i, ret = -1;
|
struct tcp_ack_info *ack_info;
|
unsigned start;
|
|
spin_lock_bh(&ack_m->lock);
|
if (ack_m->max_num == TCP_ACK_NUM) {
|
spin_unlock_bh(&ack_m->lock);
|
return -1;
|
}
|
|
if (ack_m->free_index >= 0) {
|
i = ack_m->free_index;
|
ack_m->free_index = -1;
|
ack_m->max_num++;
|
spin_unlock_bh(&ack_m->lock);
|
return i;
|
}
|
|
for (i = 0; ((ret < 0) && (i < TCP_ACK_NUM)); i++) {
|
ack_info = &ack_m->ack_info[i];
|
do {
|
start = read_seqbegin(&ack_info->seqlock);
|
ret = -1;
|
if (!ack_info->busy) {
|
ack_m->free_index = -1;
|
ack_m->max_num++;
|
ret = i;
|
}
|
} while(read_seqretry(&ack_info->seqlock, start));
|
}
|
spin_unlock_bh(&ack_m->lock);
|
|
return ret;
|
}
|
|
|
/* return val: 0 for not handle tx, 1 for handle tx */
|
int tcp_ack_handle(struct msg_buf *new_msgbuf,
|
struct tcp_ack_manage *ack_m,
|
struct tcp_ack_info *ack_info,
|
struct tcp_ack_msg *ack_msg,
|
int type)
|
{
|
int quick_ack = 0;
|
struct tcp_ack_msg *ack;
|
int ret = 0;
|
struct msg_buf *drop_msg = NULL;
|
|
//printk("%s %d",__func__,type);
|
write_seqlock_bh(&ack_info->seqlock);
|
|
ack_info->last_time = jiffies;
|
ack = &ack_info->ack_msg;
|
|
if (type == 2) {
|
if (U32_BEFORE(ack->seq, ack_msg->seq)) {
|
ack->seq = ack_msg->seq;
|
if (ack_info->psh_flag &&
|
!U32_BEFORE(ack_msg->seq,
|
ack_info->psh_seq)) {
|
ack_info->psh_flag = 0;
|
}
|
|
if (ack_info->msgbuf) {
|
//printk("%lx \n",ack_info->msgbuf);
|
drop_msg = ack_info->msgbuf;
|
ack_info->msgbuf = NULL;
|
del_timer(&ack_info->timer);
|
}else{
|
//printk("msgbuf is NULL \n");
|
}
|
|
ack_info->in_send_msg = NULL;
|
ack_info->drop_cnt = atomic_read(&ack_m->max_drop_cnt);
|
} else {
|
printk("%s before abnormal ack: %d, %d\n",
|
__func__, ack->seq, ack_msg->seq);
|
drop_msg = new_msgbuf;
|
ret = 1;
|
}
|
} else if (U32_BEFORE(ack->seq, ack_msg->seq)) {
|
if (ack_info->msgbuf) {
|
drop_msg = ack_info->msgbuf;
|
ack_info->msgbuf = NULL;
|
}
|
|
if (ack_info->psh_flag &&
|
!U32_BEFORE(ack_msg->seq, ack_info->psh_seq)) {
|
ack_info->psh_flag = 0;
|
quick_ack = 1;
|
} else {
|
ack_info->drop_cnt++;
|
}
|
|
ack->seq = ack_msg->seq;
|
|
if (quick_ack || (!ack_info->in_send_msg &&
|
(ack_info->drop_cnt >=
|
atomic_read(&ack_m->max_drop_cnt)))) {
|
ack_info->drop_cnt = 0;
|
ack_info->in_send_msg = new_msgbuf;
|
del_timer(&ack_info->timer);
|
} else {
|
ret = 1;
|
ack_info->msgbuf = new_msgbuf;
|
if (!timer_pending(&ack_info->timer))
|
mod_timer(&ack_info->timer,
|
(jiffies + msecs_to_jiffies(5)));
|
}
|
} else {
|
printk("%s before ack: %d, %d\n",
|
__func__, ack->seq, ack_msg->seq);
|
drop_msg = new_msgbuf;
|
ret = 1;
|
}
|
|
write_sequnlock_bh(&ack_info->seqlock);
|
|
if (drop_msg)
|
intf_tcp_drop_msg(ack_m->priv, drop_msg);// drop skb
|
|
return ret;
|
}
|
|
int tcp_ack_handle_new(struct msg_buf *new_msgbuf,
|
struct tcp_ack_manage *ack_m,
|
struct tcp_ack_info *ack_info,
|
struct tcp_ack_msg *ack_msg,
|
int type)
|
{
|
int quick_ack = 0;
|
struct tcp_ack_msg *ack;
|
int ret = 0;
|
struct msg_buf *drop_msg = NULL;
|
struct msg_buf * send_msg = NULL;
|
//printk("",);
|
write_seqlock_bh(&ack_info->seqlock);
|
|
ack_info->last_time = jiffies;
|
ack = &ack_info->ack_msg;
|
|
if(U32_BEFORE(ack->seq, ack_msg->seq)){
|
if (ack_info->msgbuf) {
|
drop_msg = ack_info->msgbuf;
|
ack_info->msgbuf = NULL;
|
//ack_info->drop_cnt++;
|
}
|
|
if (ack_info->psh_flag &&
|
!U32_BEFORE(ack_msg->seq, ack_info->psh_seq)) {
|
ack_info->psh_flag = 0;
|
quick_ack = 1;
|
} else {
|
ack_info->drop_cnt++;
|
}
|
|
ack->seq = ack_msg->seq;
|
|
if(quick_ack || (!ack_info->in_send_msg &&
|
(ack_info->drop_cnt >=
|
atomic_read(&ack_m->max_drop_cnt)))){
|
ack_info->drop_cnt = 0;
|
send_msg = new_msgbuf;
|
ack_info->in_send_msg = send_msg;
|
del_timer(&ack_info->timer);
|
}else{
|
ret = 1;
|
ack_info->msgbuf = new_msgbuf;
|
if (!timer_pending(&ack_info->timer))
|
mod_timer(&ack_info->timer,
|
(jiffies + msecs_to_jiffies(5)));
|
}
|
|
//ret = 1;
|
}else {
|
printk("%s before ack: %d, %d\n",
|
__func__, ack->seq, ack_msg->seq);
|
drop_msg = new_msgbuf;
|
ret = 1;
|
}
|
|
/*if(send_msg){
|
intf_tx(ack_m->priv,send_msg);
|
ack_info->in_send_msg=NULL;
|
}*/
|
|
//ack_info->in_send_msg=NULL;
|
|
write_sequnlock_bh(&ack_info->seqlock);
|
|
/*if(send_msg){
|
intf_tx(ack_m->priv,send_msg);
|
//ack_info->in_send_msg=NULL;
|
}*/
|
|
if (drop_msg)
|
intf_tcp_drop_msg(ack_m->priv, drop_msg);// drop skb
|
|
return ret;
|
|
}
|
|
void filter_rx_tcp_ack(struct rwnx_hw *priv,
|
unsigned char *buf, unsigned plen)
|
{
|
int index;
|
struct tcp_ack_msg ack_msg;
|
struct tcp_ack_info *ack_info;
|
struct tcp_ack_manage *ack_m = &priv->ack_m;
|
|
if (!atomic_read(&ack_m->enable))
|
return;
|
|
if ((plen > MAX_TCP_ACK) ||
|
!tcp_check_quick_ack(buf, &ack_msg))
|
return;
|
|
index = tcp_ack_match(ack_m, &ack_msg);
|
if (index >= 0) {
|
ack_info = ack_m->ack_info + index;
|
write_seqlock_bh(&ack_info->seqlock);
|
ack_info->psh_flag = 1;
|
ack_info->psh_seq = ack_msg.seq;
|
write_sequnlock_bh(&ack_info->seqlock);
|
}
|
}
|
|
/* return val: 0 for not filter, 1 for filter */
|
int filter_send_tcp_ack(struct rwnx_hw *priv,
|
struct msg_buf *msgbuf,
|
unsigned char *buf, unsigned int plen)
|
{
|
//printk("%s \n",__func__);
|
int ret = 0;
|
int index, drop;
|
unsigned short win_scale = 0;
|
unsigned int win = 0;
|
struct tcp_ack_msg ack_msg;
|
struct tcp_ack_msg *ack;
|
struct tcp_ack_info *ack_info;
|
struct tcp_ack_manage *ack_m = &priv->ack_m;
|
|
if (plen > MAX_TCP_ACK)
|
return 0;
|
|
tcp_ack_update(ack_m);
|
drop = tcp_check_ack(buf, &ack_msg, &win_scale);
|
//printk("drop:%d win_scale:%d",drop,win_scale);
|
if (!drop && (0 == win_scale))
|
return 0;
|
|
index = tcp_ack_match(ack_m, &ack_msg);
|
if (index >= 0) {
|
ack_info = ack_m->ack_info + index;
|
if ((0 != win_scale) &&
|
(ack_info->win_scale != win_scale)) {
|
write_seqlock_bh(&ack_info->seqlock);
|
ack_info->win_scale = win_scale;
|
write_sequnlock_bh(&ack_info->seqlock);
|
}
|
|
if (drop > 0 && atomic_read(&ack_m->enable)) {
|
win = ack_info->win_scale * ack_msg.win;
|
if ((win_scale!=0) && (win < (ack_m->ack_winsize * SIZE_KB)))
|
{
|
drop = 2;
|
printk("%d %d %d",win_scale,win,(ack_m->ack_winsize * SIZE_KB));
|
}
|
ret = tcp_ack_handle_new(msgbuf, ack_m, ack_info,
|
&ack_msg, drop);
|
}
|
|
goto out;
|
}
|
|
index = tcp_ack_alloc_index(ack_m);
|
if (index >= 0) {
|
write_seqlock_bh(&ack_m->ack_info[index].seqlock);
|
ack_m->ack_info[index].busy = 1;
|
ack_m->ack_info[index].psh_flag = 0;
|
ack_m->ack_info[index].last_time = jiffies;
|
ack_m->ack_info[index].drop_cnt =
|
atomic_read(&ack_m->max_drop_cnt);
|
ack_m->ack_info[index].win_scale =
|
(win_scale != 0) ? win_scale : 1;
|
|
//ack_m->ack_info[index].msgbuf = NULL;
|
//ack_m->ack_info[index].in_send_msg = NULL;
|
ack = &ack_m->ack_info[index].ack_msg;
|
ack->dest = ack_msg.dest;
|
ack->source = ack_msg.source;
|
ack->saddr = ack_msg.saddr;
|
ack->daddr = ack_msg.daddr;
|
ack->seq = ack_msg.seq;
|
write_sequnlock_bh(&ack_m->ack_info[index].seqlock);
|
}
|
|
out:
|
return ret;
|
}
|
|
void move_tcpack_msg(struct rwnx_hw *priv,
|
struct msg_buf *msg)
|
{
|
struct tcp_ack_info *ack_info;
|
struct tcp_ack_manage *ack_m = &priv->ack_m;
|
int i = 0;
|
|
if (!atomic_read(&ack_m->enable))
|
return;
|
|
//if (msg->len > MAX_TCP_ACK)
|
// return;
|
|
for (i = 0; i < TCP_ACK_NUM; i++) {
|
ack_info = &ack_m->ack_info[i];
|
write_seqlock_bh(&ack_info->seqlock);
|
if (ack_info->busy && (ack_info->in_send_msg == msg))
|
ack_info->in_send_msg = NULL;
|
write_sequnlock_bh(&ack_info->seqlock);
|
}
|
}
|