igmpproxy原始碼學習——配置資訊載入 loadConfig,loadconfig
在igmpproxy主程式運行之前需要先讀取設定檔,igmpproxy的設定檔通常為/etc/igmpproxy.conf或者/var/igmpproxy.conf其內容如下:
quickleave
mode 3
phyint ppp0 upstream ratelimit 0 threshold 1
phyint br0 downstream ratelimit 0 threshold 1
igmpproxy載入設定檔資訊由main函數中調用loadConfig()實現,載入設定檔的代碼主要在config.c中。在分析loadConfig的具體實現之前,我們先瞭解config.c中的結構體struct vifconfig。該結構體記錄了關於igmpproxy連接埠等配置資訊。
struct vifconfig {
char*name;//連接埠名稱 如eth0
shortstate;//連接埠狀態 0-無效連接埠 1
intratelimit;//訪問速率的限制值
intthreshold;//TTl閾值
// Keep allowed nets for VIF.
struct SubnetList* allowednets;//關於子網IP地址及子網路遮罩的結構體
// Next config in list...
struct vifconfig* next;//下一個節點
};
在config.c中,定義了struct vifconfig的全域變數vifconf。
這個全域變數儲存了我們從設定檔中讀取的配置資訊。laodConfig()的主要作用就是講設定檔中的資訊記錄了通過vifconf可以訪問的鏈表中。然後main函數在調用igmpProxyInit(),該函數訪問vifconf,進行相關配置。當然igmpProxyInit()還有其他作用。
loadConfig 接下來我們進入
loadConfig瞭解配置資訊載入的主要流程。
void initCommonConfig()
loadConfig首先調用initCommonConfig()函數,初始化一些公用常量
openConfigFile(configFile)
開啟設定檔,容易理解。除了開啟設定檔外,這個函數還為全域指標變數iBuffer分配記憶體空間。iBuffer用於儲存每次從設定檔中讀取的512個位元組的內容。igmpproxy的載入配置資訊的實現是:每次從設定檔中至多讀取512位元組(實際上可能是一行一行讀)的資料放在IBuffer中,然後再從iBuffer中解析出每一個token(關於iBuffer在nextConfigToken()會更詳細的解釋)。
nextConfigToken()
顧名思義,nextConfigToken()的作用是從igmpproxy.conf中解析出一個一個token。但是它不是每一個token都去訪問一次igmpproxy.conf的,而是一次性取出READ_BUFFER_SIZE大小的資料放在緩衝區iBuffer中,然後再從iBuffer中一個詞一個詞解析,這512位元組解析完了遇到'\0'的時候,(bufPtr == readSize)為真,再fread出512位元組的資料。
如果igmpproxy.conf讀完了,且讀出來的資料都在nextConfigToken中解析完了,則(readSize < READ_BUFFER_SIZE && bufPtr == readSize)為真,這時返回NULL。 因為nextConfigToken()使用了緩衝區iBuffer,它就不是每一次運行都去訪問一次igmpproxy.conf。第一次運行nextConfigToken的時候我們必須執行fread從次igmpproxy.conf中讀取512位元組的資料,第二次執行nextConfigToken的時候,因為我們第一次讀出來放在iBuffer中的資料還沒有用完,就不需要在fread一次了。
loadConfig的核心流程
loadConfig()函數中,在第一次執行nextConfigToken擷取一個token後,我們就進入了loadConfig的核心流程,也就是配置資訊的解析以及將配置資訊解析為結構體struct vifconfig的成員變數儲存在全域變數vifconf中(如連接埠資訊,IP地址等等)。 正確的設定檔可能包含三類資訊(不一定三類都有),分別是phyint、quickleave和mode。loadConfig通過token與這三個詞的匹配來判斷要進行什麼樣的處理。(我下載的官方源碼是不支援mode的,這可以通過自己修改源碼實現,我們也可以再設定檔igmpproxy.conf中寫入其他配置資訊,然後再loadConfig增加處理方法)
如果發現當前讀取到的是有關phyint的資訊,則調用parsePhyintToken()按照phyint的規則解析,將資訊已結構體struct vifconfig的形式儲存給指標tmpPtr,
再通過
currPtr
串到vifconf中。關於parsePhyintToken後面有做說明。 如果發現當前讀取到的是quickleave,則寫到公用配置中commonConfig.fastUpstreamLeave = 1;
parsePhyintToken
首先看返回值,是一個struct vifconfig 類型的結構體指標。 在剛剛進入 parsePhyintToken時,token必須是網路介面的名字如(eth0,ppp0,br0),因此第一個token大小不能超過sizeof( ((struct ifreq *)NULL)->ifr_name)。接下來對結構體指標tmpPtr的成員變數進行初始化 。
tmpPtr->next = NULL; // Important to avoid seg fault...
tmpPtr->ratelimit = 0;
tmpPtr->threshold = 1;
tmpPtr->state = IF_STATE_DOWNSTREAM;
tmpPtr->allowednets = NULL
初始化完成後,將token(此時為介面名稱)拷貝給tmpPtr->name
strcpy(tmpPtr->name, token);
然後使用altnet、upstream等關鍵詞逐個匹配後面的token。匹配到altnet是需要使用parseSubnetAddress進一步解析:
*anetPtr = parseSubnetAddress(token);
匹配到upstream、downstream、disabled、ratelimit、threshold時直接對tmpPtr的成員變數進行設定。如:
else if(strcmp("upstream", token)==0) {
// Upstream
IF_DEBUG log(LOG_DEBUG, 0, "Config: IF: Got upstream token.");
tmpPtr->state = IF_STATE_UPSTREAM;
parseSubnetAddress
將 a.b.c.d/n格式的子網IP解析為結構體struct SubnetList
struct SubnetList {
uint32 subnet_addr;
uint32 subnet_mask;
struct SubnetList* next;
}
getCurrentConfigToken
判斷token是否合法,沒什麼好解釋的。
總結:
loadConfig主迴圈。
nextConfigToken()
while(){
phyint:
parsePhyintToken()
quickleave/mode:
直接修改公用配置
}
parsePhyintToken 將phyint的資訊通過一個鏈表儲存在vifconf中。