1. 程式人生 > >iptables: Resource temporarily unavailable.問題

iptables: Resource temporarily unavailable.問題

執行iptables規則時失敗,出現錯誤提示 iptables: Resource temporarily unavailable.

問題

  出現此種怪問題,第一反應就是:“發生了什麼?我什麼都沒改。沒道理呀。”
   Resource temporarily unavailable.的錯誤列印對應錯誤碼EAGAIN,就出叫你出現這個錯了在試一遍的意思。我的感覺是,iptables規則作為系統全域性共享元件,在設定時,核心肯定會有鎖機制來解決併發問題。既然如此,那就寫個指令碼併發iptables試一試看有問題沒。

測試指令碼

#!/bin/sh

iptables -t filter -N TESTTTTTTTT1 &
iptables -t filter -N TESTTTTTTTT2 & iptables -t filter -N TESTTTTTTTT3 & iptables -t filter -N TESTTTTTTTT4 & iptables -t filter -N TESTTTTTTTT5 & iptables -t filter -N TESTTTTTTTT6 & iptables -t filter -X TESTTTTTTTT1 & iptables -t filter -X TESTTTTTTTT2 & iptables -t filter -X TESTTTTTTTT3 &
iptables -t filter -X TESTTTTTTTT4 & iptables -t filter -X TESTTTTTTTT5 & iptables -t filter -X TESTTTTTTTT6 &

  指令碼很簡單,建立鏈,然後刪除鏈。每條規則都放後臺執行,執行幾遍就能復現iptables: Resource temporarily unavailable.的問題。
  板子上跑起來有問題,想著虛擬機器跑下試試,虛擬機器跑也有問題,不過虛擬機器會有讓加-w引數的提示:

Another app is currently holding the xtables lock. Perhaps you want to use the -w option?
Another app is currently holding the xtables lock. Perhaps you want to use the -w option?
Another app is currently holding the xtables lock. Perhaps you want to use the -w option?

  這裡已經很明顯的告訴我們,多個程序爭奪xtables lock,iptables執行失敗,可以通過-w引數來讓程序在失敗時wait一下。

修改方法

  檢視虛擬機器裡面的iptables版本為1.4.21, 板子裡面的iptables版本為1.4.4,然後下載了1.4.21的原始碼下來

1.4.21的鎖機制
  通過建立unix域套接字然後bind來實現,但是我本地測試這段程式碼卻有問題。原因在於:域套接字在程序退出後不會銷燬該套接字對應的檔案,下次bind就會失敗,如果呼叫unlink來處理,套接字檔案又會裡面被清掉,bind總是成功,無法起到鎖的作用。(所以這裡不懂1.4.21這一版的原理究竟是啥)
  想來想去,沒什麼好辦法,又下載了最新的iptables原始碼

1.6.1的鎖機制
  通過flock實現。查了下flock,發現這就是我需要的。flock鎖的銷燬會隨著檔案描述符銷燬而銷燬,所以即便程序意外退出了,也不用擔心會有鎖殘留的問題。於是可以寫出iptables應用層加鎖的程式碼來:

void xtables_try_lock(void)
{
    int fd;
    int i = 0;
    int sleep_time_ms = 50;
    int wait_time_sec = 10;
    int wait_cnt = wait_time_sec*1000 / sleep_time_ms;

    fd = open(XT_LOCK_NAME, O_CREAT, 0600);
    if (fd < 0)
        return;

    for(i = 0; i < wait_cnt; i++)
    {
        if(!flock(fd, LOCK_EX | LOCK_NB))
            return;
        usleep(sleep_time_ms * 1000);
    }

    printf("## BUG! Another app is currently holding the xtables lock long time!\n");
    return;
}

  把這個鎖放在iptables就ok了。