2011年12月20日 星期二

Packet Buffer 操作的幾個內嵌函數

Packet buffer 由sk_buff結構描述, Packet buffer Data由其head和end成員界定, 而Packet Data則由Packet Data內data和tail界定的子區域來描述, 採用這種結構可以使添加或去除Packet 頭的操作變得非常方便.

skb_put(skb,len)  在Packet 尾部擴展長度為len的Data block, 返回擴展塊的地址, __skb_put()為未校驗版本
skb_push(skb,len) 在Packet 前部擴展長度為len的Data block, 返回擴展塊的地址, __skb_push()為未校驗版本
skb_pull(skb,len) 去除Packet 前部長度為len的Data block, 返回新Packet 的起始地址, __skb_pull()為未校驗版本
skb_headroom(skb) 返回Packet 前部距離Packet 區開始的長度
skb_tailroom(skb) 返回Packet 尾部距離Packet 區結束的長度
skb_reserve(skb,len) 設置Packet 起始位置為Packet 區開始的len字節
skb_trim(skb,len) 將Packet 截斷為len字節, __skb_trim()為未校驗版本

; include/linux/skbuff.h:

struct sk_buff {
...
unsigned int len; /* Length of actual data */
unsigned char *head; /* Head of buffer */
unsigned char *data; /* Data head pointer */
unsigned char *tail; /* Tail pointer */
unsigned char *end; /* End pointer */
...
}

/*
 * Add data to an sk_buff
 */

static inline unsigned char *__skb_put(struct sk_buff *skb, unsigned int len)
{
unsigned char *tmp=skb->tail;
skb->tail+=len;
skb->len+=len;
return tmp;
}

/**
 * skb_put - add data to a buffer
 * @skb: buffer to use
 * @len: amount of data to add
 *
 * This function extends the used data area of the buffer. If this would
 * exceed the total buffer size the kernel will panic. A pointer to the
 * first byte of the extra data is returned.
 */

static inline unsigned char *skb_put(struct sk_buff *skb, unsigned int len)
{
unsigned char *tmp=skb->tail;
skb->tail+=len;
skb->len+=len;
if(skb->tail>skb->end) {
skb_over_panic(skb, len, current_text_addr());
}
return tmp;
}

static inline unsigned char *__skb_push(struct sk_buff *skb, unsigned int len)
{
skb->data-=len;
skb->len+=len;
return skb->data;
}

/**
 * skb_push - add data to the start of a buffer
 * @skb: buffer to use
 * @len: amount of data to add
 *
 * This function extends the used data area of the buffer at the buffer
 * start. If this would exceed the total buffer headroom the kernel will
 * panic. A pointer to the first byte of the extra data is returned.
 */

static inline unsigned char *skb_push(struct sk_buff *skb, unsigned int len)
{
skb->data-=len;
skb->len+=len;
if(skb->datahead) {
skb_under_panic(skb, len, current_text_addr());
}
return skb->data;
}

static inline char *__skb_pull(struct sk_buff *skb, unsigned int len)
{
skb->len-=len;
return skb->data+=len;
}

/**
 * skb_pull - remove data from the start of a buffer
 * @skb: buffer to use
 * @len: amount of data to remove
 *
 * This function removes data from the start of a buffer, returning
 * the memory to the headroom. A pointer to the next data in the buffer
 * is returned. Once the data has been pulled future pushes will overwrite
 * the old data.
 */

static inline unsigned char * skb_pull(struct sk_buff *skb, unsigned int len)
{
if (len > skb->len)
return NULL;
return __skb_pull(skb,len);
}

/**
 * skb_headroom - bytes at buffer head
 * @skb: buffer to check
 *
 * Return the number of bytes of free space at the head of an &sk_buff.
 */

static inline int skb_headroom(const struct sk_buff *skb)
{
return skb->data-skb->head;
}

/**
 * skb_tailroom - bytes at buffer end
 * @skb: buffer to check
 *
 * Return the number of bytes of free space at the tail of an sk_buff
 */

static inline int skb_tailroom(const struct sk_buff *skb)
{
return skb->end-skb->tail;
}

/**
 * skb_reserve - adjust headroom
 * @skb: buffer to alter
 * @len: bytes to move
 *
 * Increase the headroom of an empty &sk_buff by reducing the tail
 * room. This is only allowed for an empty buffer.
 */

static inline void skb_reserve(struct sk_buff *skb, unsigned int len)
{
skb->data+=len;
skb->tail+=len;
}


static inline void __skb_trim(struct sk_buff *skb, unsigned int len)
{
skb->len = len;
skb->tail = skb->data+len;
}

/**
 * skb_trim - remove end from a buffer
 * @skb: buffer to alter
 * @len: new length
 *
 * Cut the length of a buffer down by removing data from the tail. If
 * the buffer is already under the length specified it is not modified.
 */

static inline void skb_trim(struct sk_buff *skb, unsigned int len)
{
if (skb->len > len) {
__skb_trim(skb, len);
}
}






1. socket buffer : struct sk_buff
    (1) 宣告在 include/linux/skbuff.h
    (2) 重要欄位有


  unsigned char *head;  // 配置空間起點
  unsigned char *data;   // 有效資料起點
  unsigned char *tail;     // 有效資料的最後一個 Octet
  unsigned char *end;    // 配置空間終點

2. 配置 sk_buff



(1) struct sk_buff *alloc_skb(unsigned int len, int priority);
比較原始. 配置完成會將 skb-> data 和 skb->tail 指向 skb->head

(2) struct sk_buff *dev_alloc_skb(unsigned int len);
供ISR使用而設計, 以GFP_ATOMIC優先度來配置, 並在 skb->head 和 skb->data 之間保留空間, 用以存放協定標頭.





3. void skb_reserve(struct sk_buff *skb, int len);
    保留前頭空間.
    大多數的Ethernet 介面在封包之前會保留 2-bytes空間,
    這樣可使得 IP header 對齊 16-bytes 邊緣
    ( 因為 IP header 之前的 Ethernet header 有 14-bytes )





4. unsigned char * skb_put(struct sk_buff *skb, int len);
    unsigned char * __skb_put(struct sk_buff *skb, int len);
    將資料加到tail room 區, 並將 skb->tail 往後移
    skb_put() : 會檢查是否有足夠空間
    __skb_put() : 會省略檢查

2011年12月18日 星期日

一些 C Macro 的技巧

(Part I:不定變數)

幾個學校課程不太會講到的技巧,第一個是不定變數的使用:
#ifdef DEBUG
#define debug_printf(str, ...)     do {         printf(str, __VA_ARGS__);     } while (0)
#else
#define debug_printf(str, ...)
#endif

關鍵是 __VA_ARGS__,這樣可以很愉快的使用 debug_printf()。

(Part II:將傳入的參數變成字串)

另外一個技巧是將傳入的參數變成字串:
#define print_var(var)     do {         printf("%s: %s\n", #var, var);     } while (0)

關鍵字是 #var。當輸入 print_var(argv[0]); 時就會把以上的 Macro 展開為 printf("%s: %s\n", "argv[0]", argv[0]);。

 (Part III:將傳入的參數名稱變化)




假設你想要把傳入參數名稱再變化,用 ## 穿插其中:
#define print_three_var(var)
     do {
         print_var(var);
         print_var(var##2);
         print_var(var##3);
     } while (0)

因為你不能用 var2 來表示 var + "2",所以你必須用 ##。當你傳入print_three_var(telephone) 時,他會展開成:
print_var(telephone);
print_var(telephone2);
print_var(telephone3);

補充一下:這邊的 print_var() 是 Part II 裡的 print_var()。





from:Gea-Suan Lin's BLOG for Work

2011年12月17日 星期六

How to delete Win7 the default 100mb partition 刪除預設100MB分割區方法

在 windows 7 系統下選擇->附屬應用程式->命令提式字元 (按右鍵 以系統管理員身份執行) 接著按下 Y會出現命令提式字元輸入視窗
c:\windows\system32>diskpart
DISKPART>list disk (列出硬碟以便確認) 筆者硬碟為磁碟0
DISKPART>select disk 0 (選擇磁碟0 ,0是剛剛確認的磁碟順序)
DISKPART>list partition (確認磁碟分區)DISKPART>select partition 1 (1是目前確認的100MB的磁碟分區代號)
DISKPART>inactive (將此磁區設為非活動)
DISKPART>select partition 2 (選擇系統C磁碟)
DISKPART>active (將C磁碟設為活動)
設定完成後打exit 離開
利用以下指令將開機資訊寫到C槽系統磁區
在命令提式字元下輸入
 bcdboot  c:windows /s c: /l zh-tw
再使用磁區分割工具將100MB磁區刪除,並將磁區合併到c槽系統磁區

2011年12月14日 星期三

Change Windows MTU value

Ethernet 的 MTU 是 1500, PPPoE 是 1492 (Windows 下的 PPPoE 是 1480)

Windows 2000/XP
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\{網卡的GUID}]
新增 DWORD 值, 名稱 MTU, 值 1492 (十進位)
網卡停用、啟用
Windows Vista/7
netsh interface ipv4 set subinterface "區域連線" mtu=1492 store=persistent
網卡停用、啟用
Linux/FreeBSD
ifconfig [interface] mtu 1492

Ubuntu PPPoE server

安裝 Server軟體


1) 抓rp-pppoe
wget -c http://easylinux.info/uploads/rp-pppoe-3.6.tar.gz
sudo tar zxvf rp-pppoe-3.6.tar.gz -C /opt/
sudo chown -R root:root /opt/rp-pppoe-3.6/

2) 安裝軟體 develop library:
sudo apt-get install libc6-dev

3) 產生需要的pppoe server程式
sudo /opt/rp-pppoe-3.6/go



設定組態



1)修改/etc/ppp/options,讓它的內容如下(下面的所有檔都一樣,如果你的系統中找不到,則create一個):
lock
crtscts
nobsdcomp
nodeflate
nopcomp
2)修改/etc/ppp/pppoe-server-options檔,內容如下:
auth
#
使用chap認證方式,如果想用pap認證方法則用require-pap
require-chap
default-mru
default-asyncmap
lcp-echo-interval 60
lcp-echo-failure 5
#
這兩條ms-dns參數是設置給PPPOE用戶端分配的DNS伺服器位址
ms-dns 1.1.1.1
ms-dns 2.2.2.2
noipdefault
noipx
nodefaultroute
#
如果您想讓PPPOE用戶端機器可以通過PPPOE伺服器上網,則得通過這個參數開開啟PPPOE伺服器的arp proxy功能
proxyarp
noktune
netmask 255.255.255.255
logfile /var/log/pppd.log

3)添加用戶端的用戶名和密碼檔,如果使用chap認證方式請更改/etc/ppp/chap-secrets檔,如果使用pap認證方式請更改/etc/ppp/chap-secrets檔,採用下面的格式:
“test" * "123456" *
每一行由四個欄位構成,第一個欄位為帳戶名稱,例子中設置為test,第二個欄位為伺服器,保持為*不做限制就是,第三個欄位為使用者密碼,例子中設置為123456,第四個欄位為允許從那個IP來撥號,設置為*不作限制就可以了。
4)啟動PPPOE伺服器。
/usr/local/ppp/sbin/pppoe-server -k -I eth0 -L 192.168.1.1 -R 192.168.1.10-200
上面命令在啟動PPPOE伺服器時指定了本地介面的IP為192.168.1.1,給用戶端分配的IP地址範圍是192.168.1.10-192.168.1.200。

2011年12月8日 星期四

IPSec NAT-T技術



在 NAT 技術和 IPsec 技術的應用都非常廣泛。但從本質上來說,兩者是存在著矛盾的。

1. 從 IPsec 的角度上說, IPsec 要保證資料的安全,因此它會加密和校驗資料。
2. 
從 NAT 的觀點來看,為了完成地址轉換,勢必會修改 IP 地址。

         IPSec 提供了點對點的 IP 通信的安全性,但在 NAT 環境下對 IPSec 的支持有限, AH  Protocol是肯定不能進行 NAT 的了,這和 AH 設計的理念是相違背的; ESP  Protocol在 NAT 環境下最多只能有一個 VPN 主機能建立 VPN 通道,無法實現多台機器同時在 NAT 環境下進行ESP 通信。關於 IPSec 在 NAT 環境下的需求問題在 RFC3715 中進行了描述。
       NAT 
穿越 (NAT Traversal , NAT-T) 就是為解決這個問題而提出的, RFC3947 , 3948 中定義,在 RFC4306 中也加入了 NAT-T 的說明,但並沒廢除 RFC3947 , 3948 ,只是不區分階段 1 和階段 2 。該方法將 ESP  Protocol封包封裝到 UDP 封包中(在原 ESP  Protocol的 IP 封包頭外添加新的 IP Header和 UDP Header),使之可以在 NAT 環境下使用的一種方法,這樣在 NAT 的內部網中可以有多個 IPSec 主機建立 VPN 通道進行通信。
   AH 
封裝: AH 封裝的校驗從 IP Header開始,如果 NAT 將 IP 的 Header改動, AH 的校驗就會失敗,因此我們得出結論,AH 是無法與 NAT 共存的。ESP 封裝的傳輸模式:對於 NAT 來說, ESP 封裝比 AH 的優勢在於,無論是加密還是完整性的校驗, IP Header部都沒有被封包括進去。但是還是有新的問題,對於 ESP 的傳輸模式, NAT  無法更新上層Check Sum。TCP  和  UDP   Header封包含一個Check Sum,它整合了源和目標 IP  地址和Port Number的值。

    當  NAT  改變了某個封包的  IP  地址和(或)Port Number時,它通常要更新  TCP  或  UDP  Check Sum。當  TCP  或  UDP  Check Sum使用了  ESP 來加密時,它就無法更新這個Check Sum。由於地址或端口已經被  NAT  更改,目的地的Check Sum檢驗就會失敗。雖然  UDP  Check Sum是Option的,但是  TCP  Check Sum卻是必需的。
       ESP 
封裝的 Tunnel模式:從 ESP  Tunnel模式的封裝中,我們可以發現, ESP  Tunnel模式將整個原始的 IP 封包整個進行了加 ​​密,且在 ESP的 Header外面新加了一層 IP Header,所以 NAT 如果只改變最前面的 IP 地址對後面受到保護的部分是不會有影響的。因此, IPsec 只有採用ESP 的 Tunnel模式來封裝資料時才能與 NAT 共存。

由於完整性Check牽涉到 IP Header部,所以 NAT 無法對其修改,不相容。
ESP 的傳輸模式,因為 TCP 部分被加密, NAT 無法對 TCP Check Sum進行修改,不相容。
ESP 
的 Tunnel模式,由於 NAT 改動外部的 IP 而不能改動被加密的原始 IP ,使得只有這種情況下才能與 NAT 共存。


NAT 穿越 (NAT Traversal , NAT-T)
       以前 NAT 和 IPsec 只能以 1 對 1 的形式共存, NAT-T 打破了這種形式。而且NAT-T 支持 ESP 的傳輸模式。NAT-T 的基本思想:
    將 ESP  Protocol封包封裝到 UDP 封包中(在原 ESP  Protocol的 IP 封包頭外添加新的 IP Header和 UDP Header)。使得 NAT 對待它就像對待一個普通的UDP 封包一樣。而且支持 ESP 的傳輸模式。NAT-T 的基本原理和執行步驟
1. 檢測通信中是否存在 NAT 設備和對方是否支持 NAT-T NC 
2.檢測對方是否支持 NAT-T 是通過交換 vendor ID 載荷來實現的,如果自身支持 NAT-T ,在 IKE 開始交互就要發送這種載荷,載荷內容是“ RFC 3947 ”的 MD5 值,也就是十六進制的“ 4a131c81070358455c5728f20e95452f ”

How to repair and clone disk with ddrescue

  ddrescue  is a tool that can be used to repair and clone disks on a  Linux system . This includes hard drives, partitions, DVD discs, flas...