Saturday, November 24, 2007

Maximum Simulation Time

0 Comments

在 NCTUns4.0 之前(含 4.0)都有個共同的問題,就是 simulatiopn time 最大限制在 4200 second 內,這問題存在已久,前一陣子(好大一陣子)學弟的任務要突破這依限制,才去認真的看了一下這問題的關鍵點。

4200怎麼算出來?
4200怎麼算出來,這問題有意思了,先知道怎麼算要解決問題就容易多了,許多人都以為因為使用 32bits 變數存放 tick 關係所致,的確看到 4200 很容易聯想到這裡,因為 32bits 最大可以存放 4,294,967,296 這麼大,但事實並不是如此簡單。

32bits 變數 + 最小 ticks 單位 1ns
當這兩樣資訊放在一起時,上面的推論就產生矛盾了,因為在 NCTUns 設計中,最小的 tick 等於 1ns,如此一來,32bits 的容量也不過可以存放 4.2 秒的大小老早就爆炸了,就算以預設值 1tick = 100ns 來計算也只有 429.4 秒而已,那 4200sec 怎麼出來的?

64bits Memory Mapping
答案當然是使用 64bits 的變數來儲存,在我接手的時候 engine 裡面就已經採用了大量的 64bits 變數做運算,當然包括 tick 也是,因此才可以應付使用 1tick = 1ns 時模擬 4200sec 的狀況。這時候問題又來了,既然已經使用 64bits 變數存放 tick ,為什麼還會有 4200sec 的限制?

關鍵:32bits overflow
因為在 kernel 裡面,早期 hacking timer 時並沒有考慮太多狀況,所以大多以 long (32bits) 變數做計算,而在較舊的 linux kernel 中 timer 刻度只有到 us ,但後來以版本已經支援到 ns 了,因此許多計算都是以 us 或者 ns 為基底計算,而非我們的 tick,所以 4200sec 就是因為 kernel 中 32bits 加上 overflow 所產生的限制。


然而在 32 位元的機器中,CPU 並無法直接支援 64bits 計算的指令,因此都是使用兩段式計算來完成除法運算,所以最主要的問題點就在於當發生乘、除法運算時,需要注意是否產生 overflow 或是 underflow 的情況,解決後就可以順利支援超過 4200sec 的模擬 case。


經過 wghuang 的努力,我再加以調整後列出所有必須注意的地方,以下 function 也已經在新版 kernel 處理完畢:

  • kernel/timer.c::schedule_timeout_us()
  • kernel/nctuns_callout.c::nctuns_callback()
  • kernel/nctuns_callout.c::nctuns_add_tun_event()
  • kernel/nctuns_syscall.c::sys_NCTUNS_usleep_After_Msg_Send()

我在修正問題時採用改寫 NCTUNS_ticks_to_xxx (include/linux/nctuns_ticks.h) 等系列的 macro,因此上面四個點並不是每個都需要更動,但需要注意。

... Read more.

HEMiDEMi Technorati Del.icio.us MyShare個人書籤 Yahoo
Friday, April 13, 2007

NCTUns Package Checksum Files

0 Comments

在我接手 NCTUns4.0 後開始,所有的 Package 都會包含同檔名的 *.md5sum, *.sha1sum 等兩個驗證檔,當然也希望該檔往後 release 時可以一半公告出來,讓 user 可以自行檢驗下載的 tarball 是否正確

在使用上非常容易,只要將檔案都放在同一個目錄如下:



接著使用下面指令就可以驗證 tarball file

md5sum -c <md5sum_file>
sha1sum -c <sha1sum_file>

如下圖,驗證正確就會得到個 OK 的結果,反之則會回報錯誤結果




... Read more.

HEMiDEMi Technorati Del.icio.us MyShare個人書籤 Yahoo
Tuesday, March 27, 2007

NCTUns Tunnel Architecture PartIII

0 Comments

PartIII 主要針對介紹 nctuns_tun 的架構,基本上 nctuns_tun 是從 原本 tun device driver 改寫而成,因此大部分都 function 和 tun 相去不遠,也因此往後的 porting 工作必須確保這些其他的 function 需要跟著 tun 一起更新,當然除了 tun device driver 有被改寫之外,否則應該不太需要大幅度翻新。

在 PartII 已經介紹過, nctuns_tun 採用 sysfs 的設計方式,搭配 udev 來實現動態建立 charactor node 的方式增加整體的彈性以及減少不同 linux distribution 不相容因素。源自於 tun 的架構,每個 device 都是透過向 fs 註冊,再用基本的 function 做操作,諸如 open, close, ioctl, read, write 等,因此 nctuns_tun 也不例外,所有一系列的 function,都和原本 tun 是對應的,而為了 NCTUns simulator 需要做調整如下:


Modified function list:
tun_net_xmit
tun_net_init
tun_get_user
tun_chr_aio_read
tun_set_iff
tun_chr_ioctl
tun_get_drvinfo


在上面最重要的莫過於 tun_get_user 及 tun_net_xmit 兩個 function, 另外額外在新增 tun_net_loopback function 用來判斷要經由 tunnel 流進 user-level 的 packet 是否為 loopback packet,如果是就不進 user-level 直接往上送。

tun_net_init
原本的 tun 會根據不同的 device type 判斷為 TUN_DEV 還是 TAP_DEV 而做不同的事情,但在 nctuns_tun 只會有 TUN_DEV mode 因此簡化原本 function,但增加不同 nctuns_tun 的用途判斷,根據 tunnel device name 區分為 event tun 和 data tun,其中 data tun 因需要配合 tcpdump 的解析,因此 net_device type 需指定為 ARPHRD_ETHER 且相關 header length 也需要指定如下:

dev->type = ARPHRD_ETHER;
dev->hard_header_len = ETH_HLEN;
dev->addr_len = 0;
...
...
tun->tid = i;
...
...
dev->flags = IFF_POINTOPOINT | IFF_NOARP;

在這裡我們 hack 原本 tun struct 增加欄位 tid,用來記錄 nctuns_tun 的編號,如此就不用每次得到 maptable 利用 tunnel name 去轉換成 tunnel id,將這動作於初始化就做好。

再者,為了讓 ip input procedure 可以判斷 packet 進來的 net_device 種類,因此我們在 dev->flags 指定刻意將原本的 IFF_MULTICAST 屬性拿掉,就可以搭配 IFF_POINTOPOINT 來檢查 net_device 是否屬於 nctuns_tun。當然可以如此拿掉是因此目前 NCTUns simulator 還沒有一套可以 full support multicast 的機制,因此預設狀態 multicast 的支援都是不會動,因此也不需要為這些功能預留 flags,除非往後設計出一套可以 support 的機制再做調整。

tun_set_iff, tun_get_drvinfo
主要將 tun 原本支援 multi-queue 及 pi header 功能拿掉,因為 nctuns_tun 不需要用到
而 tun_get_drvinfo 則是拿掉不同 device type 的判斷。

tun_chr_ioctl
ioctl 主要增加兩個 command,分別是 TUNSETUD, TUNGETUD 用來取得/設定 tunnel link 的 up/down 狀態如下:

case TUNSETUD: {
...
...
}
case TUNGETUD: {
...
...
}

新增的 command 主要是給 Daemon 使用(如 RIP, OSPF 等),在 NCTUns engine 裡的 phy module 會將 linkfail 設定讀進來後,利用 ioctl 將 flag 設定到 kernel,因此 daemon 可以藉由取得 flags 來判斷 link 是否 fail 了,當然這方法似乎不夠直覺,也許可以利用原本系統的方法來改進這些流程。

tun_chr_aio_read
在 read function 幾乎沒有任何動作,因為該做的事情,我們在 tun_net_xmit 已經完成了,在這裡主要針對 memory mapping 的 tunnel qlen 做校正。

tun_net_xmit, tun_get_user
這兩個 function 就是整個 nctuns_tun 的精神了,其中 tun_net_xmit 這 function 在 上層(Layer 3, 4)要流出 device 時就會間接輾轉的呼叫到這 function,接著 tun_net_xmit 需要做一些 S.S.D.D scheme 轉換之後就 enqueue 到 skb_queue 裡,等待 User-level 的程式來讀取。

而 tun_get_user 就剛好相反,這 function 是 User-level 程式要 write 到 tunnel 時會輾轉呼叫到,同樣我們也需要做一些 S.S.D.D scheme 轉換再往上層送,至於這兩個 function 怎麼做 S.S.D.D 轉換的流程在往後的篇幅會有詳細介紹。


... Read more.

HEMiDEMi Technorati Del.icio.us MyShare個人書籤 Yahoo
Friday, March 23, 2007

NCTUns Tunnel Architecture PartII

0 Comments

PartI 已經有整個 tunnel 與 NCTUns engine 初步架構說明,至於詳細運作流程與方式,等待 Part IV 再直接從 Code 角度來介紹。

動態配置 Device Major Number
將 nctuns_tun 從原本 tun device driver 獨立出來後,面臨的第一個問題就是 device major number 的指定,每個 major number 都可以有 256 個 minor number,因此之前的作法會佔用 238~253 這區段 16 個 major number,從 PartI 可以瞭解當然不需要佔用這麼多個 number,更何況這些 number 泰半幾乎已經預留給特定的 device driver ,因此我採用 Linux 2.6 所建議,採用 dynamic allocate 的方式由系統於載入時決定 device 使用的 major number,如下:

ret = alloc_chrdev_region(&nctuns_dev_t, 0, MAX_NCTUNS_TUN_MIXORS, DRV_NAME);
...
cdev_init(&nctuns_cdev, &nctuns_tun_fops);
ret = cdev_add(&nctuns_cdev, nctuns_dev_t, MAX_NCTUNS_TUN_MIXORS);

alloc_chrdev_region 必須指定一個 device 所需要使用的 region,在這邊將 MAX_NCTUNS_TUN_MIXORS 指定為 10,主要目的是預留未來使用,但實際上只需藥用到一個 minor number。要 unregister 只要呼叫對應的兩個 API 即可:

cdev_del(&nctuns_cdev);
unregister_chrdev_region(nctuns_dev_t, MAX_NCTUNS_TUN_MIXORS);

將 device number 改以動態配置所面臨的另一個問題就是在 mknod 建立這個 charactor device node 時無法知道真正使用的 major, minor number,雖然從 /proc/device 可以查到 major 但仍舊缺少 minor,這時就必須配合 sysfs 了。

sysfs 新機制
Linux 2.6 另一個重大的改變就是 sysfs 的支援,一個類似於 procfs 的新作法,詳細詳細介紹可以參考相關 Linux 書籍或網路資料,在這邊僅針對 device hot-plug 的支援。

在 Linux 2.6 特有的 sysfs 裡有個 class 的資料夾,裡面分門別類的註冊了眾多 device 的資訊,每個經過標準 API 向 sysfs 註冊的 device driver 都可以在 /sys/class 找到對應 device name 目錄,目錄之下存放了各式各樣跟這 device 相關的檔案提供一些資訊工 user-level 程式使用,其中一個檔名為 dev 的檔案就是存放了這個 device 所使用的 major, minor 資訊,就可以透過這資訊建立 charactor node 了。

整個機制看似複雜,但使用上並不困難,在 class 之下有分門別類區分了各式各樣的 device 各自成為一個 folder,因此我也建立名為 nctuns 的獨立 folder 如下:

nctuns_class = class_create(THIS_MODULE, "nctuns");
...
class_device_create(nctuns_class, NULL, nctuns_dev_t, NULL, DRV_NAME);

先使用 class_create 建立 class 分類 folder,再以 class_device_create 並傳入前面已經建立好的 device number (不管是動態建立或是靜態宣告),就完成了基本動作,重編後載入系統就可以在 /sys/class 看到 nctuns 的目錄,並在之下看到我們所註冊的 nctuns_tun,當然要 unregister 也是使用兩個 API 分別對應上面兩個 create:

class_device_destroy(nctuns_class, nctuns_dev_t);
class_destroy(nctuns_class);


udev 的配合
sysfs 也準備好,最後剩下的就是 user-level 相互搭配 hot-plug 的 daemon: udev,udev 本身也是一個龐大的計畫,他會監看 /sys/class 之下的改變,因此當有任何 device 被載入時就會根據預先設定好的 rule 做出對應的動作甚至執行相對應的 script。

我們只需要為 nctuns_tun 設定一條 rule ,讓 udev daemon 偵測到 nctuns_tun 時可以為我們在 /dev/net 將 charactor device node 建出來,一般 rule 預設路徑都存在 /etc/udev/rules.d 底下,類似於 開機程序 rc.d 的命名方式一般。所以我自己建立一個 55-nctuns.rules 的設定檔,內容如下:

KERNEL=="*", OWNER="root" GROUP="root", MODE="0600"
KERNEL=="nctuns_tun", NAME="net/%k"

指定了當發現 nctuns_tun 就在 /dev/net 建立一個同名的 node(即 nctuns_tun),而第一行就是指定建出來的 node 相關權限的設定。如此既可達到動態配置 device number,避免和系統中已經存在的 device 相衝突(大家搶同樣的 number 時),又可以免去手動 mknod 建立這些 tunnel device node 所衍生的問題,將一切動作交由 device driver 和 udev 去負責,降低安裝中的錯誤發生。



... Read more.

HEMiDEMi Technorati Del.icio.us MyShare個人書籤 Yahoo
Sunday, March 18, 2007

NCTUns Tunnel Architecture PartI

0 Comments

在有關 NCTUns 的 Design 最重要的一環,就是利用 tunnel interface 達到 Traffic flow 的 Packet 可以在 kernel 和 user-level 的 NCTUns engine 可以相互轉傳資料,主要原因在於整個 NCTUns 的 Layer3 以上都是使用真實世界 Linux 系統的 Protocol Stack,最大的好處莫過於可以更逼近於真實世界系統的運作情況,詳細設計不是這一篇的重點,在這主要針對底層 tunnel 的設計。

Tunnel interface device 原本就存在於 Linux Kernel 裡,我們主要是 hack 了原本的功能,使其擁有我們所需要的能力,但因為原本 hack 的方式異常暴力,幾近破壞了原本 tunnel 的服務,因此如果使用我們 NCTUns kernel 會面臨到需要使用 tunnel 能力的服務全部無法運作(諸如 VPN 等),在設計上,NCTUns 允許最多開啟 4096 個 tunnel 做為模擬 Host 的 interface,額外還有 event tunnel 專司用來傳送 kernel 上到 engine 的 event 以及 tcpdump packet tunnel 專司用來處理 tcpdump 的 packet flow,因此使用的架構如下圖:


在右圖中可以看到 Tunnel device driver 上面建立了 4098 個 tunnel 的 devfs node,但事實上 open 這些 devfs 都會呼叫到同樣下層的 tunnel device driver,然而透過 ioctl 之後才會創建出 net_device 並將 net_device 向 network device subsystem 註冊。

原本 NCTUns engine 架構會針對每個有可能用到的 net_device 都預先使用 mknod 指令建立 devfs node,使得一開始就先建好 4098 個對應到同一個 tunnel device driver 的 devfs node ,這動作就顯得多餘,因此我在這邊做了大幅度的改進,主要有兩點如下:

撰寫獨立 nctuns_tun device driver
從 tunndel 將模擬所需使用的功能獨立成為 nctuns_tun 和原本 tunnel device driver 分開並使用不同 device major number ,最大好處在於可以維持原本 tunnel 功能,又可兼顧 porting 的獨立性,關於 nctuns_tun 的架構在 PartIII 再做詳細介紹。

移除多餘 devfs 的使用
將原本註冊了 4098 個 devfs node 改成僅需一個 devfs node 即可,更進一步使用 dynamic allocate 未使用的 major number 給 nctuns_tun 使用,配合 udev 機制於 loading 時動態創建這個 devfs node,詳細 udev 運作機制於 PartII 再做說明。


如右圖一般,如此一來在 user-level 的 NCTUns engine 於模擬前設定 tunnel 動作也會變的簡單許多,安裝的時候也不需要在 creating tunnel 步驟等待一段時間,甚至不需要等待,只需將 udev rule file 放進 udev/rules.d 目錄裡即可。二來透過動態配置 major number 也可以避免使用者因為 device 過多而產生 major 衝突問題。

當然這部分因為 nctuns_tun 只需要一個 major, minor pair 即可,因此要註冊在 misc 類別底下也可以,原本的 tunnel device 就是這種作法,為了保留彈性才讓 nctuns_tun 以一個獨立的 major 來配置。

... Read more.

HEMiDEMi Technorati Del.icio.us MyShare個人書籤 Yahoo