[Linux][Firewall][VoIP] Voice Quality drop during P2P traffic

這次遇到的 VQ issue 頗為棘手 … … 

As title, 進行 P2P傳輸的過程中, VQ 會變得較差

如何處理此問題 … 

大致上的想法, 流程: 

a. 確認哪個版本就會有相同問題  (發現…舊版也有問題) 

b. 從 log, packet, abacus 找線索 (Abacus細節: ex: 間隔一段時間會發生VQ變差)

c. kill process (看能否縮小範圍, 看是被哪個 process 影響, ex: tr69 …) 

d. 鎖定 process > 確認是哪段 code 造成影響

Learning and Reviewing on this Issue: 

a. nf_conntrack

a-1. 打開 BitComet 可以發現 connection count 遽增, 然後可以看到 VQ 變差, delay 變大

a-2. 從這個線索, 想到透過 iptables rule 限制 connection count 遽增 的情況

a-2-1. 方法, 如下 iptables command 

Osix_syscmd("iptables -N limit-conntrack");
Osix_syscmd("iptables -A limit-conntrack -p udp -m conntrack --ctstate NEW -m limit --limit 100/s --limit-burst 100 -j RETURN");
Osix_syscmd("iptables -A limit-conntrack -p udp -m conntrack --ctstate NEW -j DROP");
Osix_syscmd("iptables -I FORWARD -p udp -m conntrack --ctstate NEW -j limit-conntrack");

a-2-2. -N: 創建新的Chain   -A: Append rule to Chain  -p: protocol

a-3. script to monitor nf_conntrack_count 

while true; do cat /proc/sys/net/net_filter/nf_conntrack_count; sleep 1; done

b. 如何在 C code 判斷判斷 iptables rule 已經存在

b-1.

Osix_syscmd("iptables-save | grep -- \"limit-conntrack\"; echo $?  > /tmp/check_limit_conntrack");
char ret_check_limit_conntrack;
int val_check_limit_conntrack;
FILE *fp;
fp = fopen("/tmp/check_limit_conntrack", "r+");
if (fp == NULL)
{
 fprintf(stderr, "open check_limit_conntrack fail\n");
 return NULL; // no such file?
}
else
{
 ret_check_limit_conntrack = getc(fp);
 
 if(ret_check_limit_conntrack != EOF)
 {
    val_check_limit_conntrack = ret_check_limit_conntrack - '0';
	printf("---[JEDBUG] val_check_limit_conntrack=%d \r\n", val_check_limit_conntrack); 
  	if(val_check_limit_conntrack == 1)
  	{
		printf("---[JEDBUG] val_check_limit_conntrack=%d \r\n", val_check_limit_conntrack); 
   		Osix_syscmd("iptables -N limit-conntrack");
   		Osix_syscmd("iptables -A limit-conntrack -p udp -m conntrack --ctstate NEW -m limit --limit 100/s --limit-burst 100 -j RETURN");
   		Osix_syscmd("iptables -A limit-conntrack -p udp -m conntrack --ctstate NEW -j DROP");
   		Osix_syscmd("iptables -I FORWARD -p udp -m conntrack --ctstate NEW -j limit-conntrack"); 
   }else if (val_check_limit_conntrack == 0){
		printf("---[JEDBUG] val_check_limit_conntrack=%d  Exists!! \r\n", val_check_limit_conntrack);
   }
 }
 fclose(fp);
}

Note:

    1. iptables -N limit-conntrack  

        //  新建立一條 Chain "limit-conntrack"

    2. iptables -A limit-conntrack -p udp -m conntrack --ctstate NEW -m limit --limit 100/s --limit-burst 100 -j RETURN"

       a. -m conntrack: 載入 conntrack module 

       b.  --ctstate  > ct: Connection Track 

       c.  -m limit --limit 100/s    > 1秒可以允許100 connection 

       d. --limit-burst 100 > 一開始的 100 個 connection 可以被允許, 後面的就要依照 limit 的限制

    3. iptables -A limit-conntrack -p udp -m conntrack --ctstate NEW -j DROP"

        > 加這條 rule 是因為 … 如果上面的限制已經上限了, 其它的封包怎麼處理, 如果沒有 DROP, 這樣就沒有達到限制的效果了! 

b-2. 因為上面的方法, 讀取 /tmp/check_limit_conntrack 的值會是 $, 不符合預期, 所以換個方法

	Osix_syscmd("iptables-save | grep -- limit-conntrack > /tmp/check_limit_conntrack");

	FILE *fp = NULL;
	fp = fopen("/tmp/check_limit_conntrack", "r+");

	if (fp == NULL)
	{
		fprintf(stderr, "open check_limit_conntrack fail\n");
	}
	else
	{
		char buffer[256];
		char *ret = NULL;
		buffer[0] = 0;
		fread(buffer, sizeof(buffer), 1, fp);
		ret = strstr(buffer, "limit-conntrack");
		if(ret == NULL)
		{
			Osix_syscmd("iptables -N limit-conntrack");
			Osix_syscmd("iptables -A limit-conntrack -p udp -m conntrack --ctstate NEW -m limit --limit 100/s --limit-burst 100 -j RETURN");
			Osix_syscmd("iptables -A limit-conntrack -p udp -m conntrack --ctstate NEW -j DROP");
			Osix_syscmd("iptables -I FORWARD -p udp -m conntrack --ctstate NEW -j limit-conntrack");	
		}
		fclose(fp);
	}
	Osix_syscmd("rm /tmp/check_limit_conntrack");