2011年12月29日 星期四

Linux下以pptp方式撥入VPN網路中


  1、下載pptp用戶端

  wget http://nchc.dl.sourceforge.net/sourceforge/pptpclient/pptp-1.7.1.tar.gz

  2、解壓

  tar zxvf pptp-1.7.1.tar.gz

  3、編譯和安裝

  make; make install

  4、編輯設定檔,設定撥號名為mypptp

  vim /etc/ppp/peers/mypptp

  內容如下:
remotename mypptp
linkname mypptp
ipparam mypptp
pty "pptp vpnserverip --nolaunchpppd "
name myaccount
usepeerdns
require-mppe
refuse-eap
noauth
file /etc/ppp/options.pptp

其中,myaccount為用戶名

  5、編輯/etc/ppp/chap-secrets,加入用戶名和帳號,這裡假設myaccount的密碼為mypassword

myaccount * mypassword *
  6、撥號,運行以下命令

/usr/sbin/pppd call mypptp logfd 1 updetach

  如果以上設定檔正確無誤,則可正常撥入虛擬網管的pptp VPN網路中了,此時如果用ifconfig查看連接情況,可以看到多了一條ppp連接,並能正確分到IP位址了。

  7、添加路由

  雖然已經撥號上來了,但此時,如果你要訪問你的虛擬區域網路資源,你必需添加一條路由才行,這裡假設你撥號上來的連接名為ppp0,並此你的虛擬區域網路的IP段為192.168.163.0,那麼,你需要加入以下命令:

route add -net 192.168.163.0 netmask 255.255.255.0 dev ppp0

  至此,在Linux系統下以pptp方式撥入虛擬網管的VPN網路中了。

  以上操作過程在Ubutun 8.1、Fedora 7、8、9、10下操作通過。

 PS:如果在撥號時報以下錯誤:

/usr/sbin/pppd:pty option precludes specifying device name

請檢查pppd的版本,不可低於2.3.7。

檢查etc/ppp/optoins檔,該檔不能為空。


------------------------------------------------------------------------------------------------------------
若出現Couldn't set tty to PPP discipline: Invalid argument 錯誤訊息
則須在kernel 加入 ppp_mppe , ppp_async 這兩個module後即可



    Device Drivers --->
        Network device support --->
            <*>   PPP (point-to-point protocol) support
            <*>     PPP support for async serial ports
            <*>     PPP MPPE compression (encryption) (EXPERIMENTAL)

Linux下以l2tp方式撥入VPN網路

  1、下載l2tp用戶端

  wget ftp://ftp.xelerance.com/xl2tpd/xl2tpd-1.1.12.tar.gz

  2、解壓

  tar zxvf xl2tpd-1.1.12.tar.gz

  3、進入解壓目錄,並編譯和安裝

  cd xl2tpd-1.1.12

  make; make install

  4、在/etc目錄下建立xl2tpd目錄,並在該目錄下建立l2tp的設定檔xl2tpd.conf

  mkdir /etc/xl2tpd

  vim /etc/xl2tpd.conf

  xl2tpd.conf檔的內容如下:

[global]
port = 1701

[lac myvpn]
lns = vpnserver ip or domain name
refuse pap = yes
require authentication = no
name = myaccount
ppp debug = yes
pppoptfile = /etc/ppp/options.myvpn_l2tp

這裡假設你的vpn的帳號為myaccount。

  5、在/etc/ppp目錄下建立options.myvpn_l2tp文件。

  vi /etc/ppp/options.myvpn_l2tp

該檔的內容如下:

asyncmap 0
noauth
crtscts
lock
hide-password
modem
netmask 255.255.255.0
proxyarp
lcp-echo-interval 30
lcp-echo-failure 4
ipcp-accept-local
ipcp-accept-remote

  6、在/etc/ppp/chap-secrets檔中加入myaccount的密碼:

myaccount * mypassword *

  7、運行xl2tpd程式。

  xl2tpd

  如果你需要看到程式的輸出資訊,可以在後面加上參數D,如:

  xl2tpd -D

此時輸出上可能會出現This binary does not support kernel L2TP. 不用理會它,沒什麼影響,但是如果報告:Open_controlfd: Unable to open /var/run/xl2tpd/l2tp-control for reading.的錯誤,則需要手動的在/var/run目錄下建立一個名為xl2tpd的目錄:

mkdir /var/run/xl2tpd

  8、新開一終端,使用以下方法開始撥號:

echo "c vpnlife" > /var/run/xl2tpd/l2tp-control

如果配置正確無誤,則可正常撥入虛擬網管的l2tp VPN網路中了,此時如果用ifconfig查看連接情況,可以看到多了一條ppp連接,並能正確分到IP位址了。

  9、添加路由

  雖然已經撥號上來了,但此時,如果你要訪問你的虛擬區域網路資源,你必需添加一條路由才行,這裡假設你撥號上來的連接名為ppp0,並此你的虛擬區域網路的IP段為192.168.163.0,那麼,你需要加入以下命令:

route add -net 192.168.163.0 netmask 255.255.255.0 dev ppp0

  至此,在Linux系統下以l2tp方式撥入虛擬網管的VPN網路中了。

  以上操作過程在Ubutun 8.1、Fedora 7、8、9、10下操作通過。

2011年12月24日 星期六

C/C++之指標 (pointer),參考 (reference) 觀念整理與常見問題



目錄
  1. 何謂指標 (pointer)? 何謂參考 (reference)?
  2. call by value? call by address (或call by pointer)? call by reference? -- swap(int* a, int* b) v.s. swap (int &a, int &b)
  3. pointer to pointer, reference to pointer (int** v.s. int*&)
  4. function pointer
  5. void ** (*d) (int &, char **(*)(char *, char **))....如何看懂複雜的宣告…
1. 何謂指標 (pointer)? 何謂參考 (reference)?
我們先談指標 (pointer)。指標,其實也只是一個變數,只是這個變數的意義是:指向某個儲存位址。很玄嗎? 一點也不。下面這張圖就可以輕易的看出指標為何物。

圖中,a, b, c, d, p1, p2都是一般的變數,儲存在記憶體 (memory) 中。其中,p1變數所記載的值是變數a的記憶體 (memory) 位址,而p2則記載著b的記憶體位址,像這樣的狀況,我們就稱p1是一個指向a的指標,相同的,p2是一個指向b的指標。
在C/C++中,我們用下面的式子來表示這個關係:
int *p1 = &a;
int *p2 = &b;

其中的&,稱為address of (取址)。即,p1 = address of a,p2 = address of b。
另一個符號*,代表的意義是指標。
int *p1
要由後往前閱讀來瞭解它的意義:p1 is a pointer points to an integer。因此,
int *p1 = &a;
這整行,我們可以看成:p1 is a pointer points to integer variable a,即:p1是一個指標,指向整數變數a。

且讓我們暫時打住指標的討論,轉頭看看參考 (reference)。
參考,可以想像成是一個變數或物件的別名 (alias)。通常,當函式 (function) 的參數 (parameter) 在函式中會被修改,而且要把這個修改結果讓呼叫函式的部份繼續使用,我們會用參考來當參數傳入函式中。
讓我們看看下面的例子:
void swap(int &a, int &b){
int tmp = a;
a = b;
b = tmp;
}

當其他程式呼叫此交換程式時,只要直接寫swap(x, y)就能交換x與y的值。在這裡,a和b為x與y的別名,即:a就是x,b就是y,如同美國國父就是華盛頓一樣。a和b不是x和y的複製品,任何做用在a與b上的動作都會反應在x與y上面,反之亦然。

指標和參考之所以難懂,有很大一部份的原因是符號上的陌生所致。加上&既能用於取址又能用於參考,容易造成初學者的混淆。下面我們提供幾個建議來幫助各位看懂這些符號。
  • 把int *p視為 int* p。
    把int和*連在一起看,當作是一種型態叫做 "指向整數之指標",要比int *p自然得多。同樣的方式也可以套在char* p或void* p等。但要注意的是下面的狀況:
    int* p, q;

    不要把這行誤解成p, q都是指向int之指標,事實上,q只是一個int變數。上面這行相當於
    int *p, q;
    或
    int *p; int q;

    如果p, q都要宣告成指向int之指標,應寫成:
    int *p, *q

    或者干脆分兩行寫:
    int* p;
    int* q;
  • 若&前面有資料型態 (ex: int &),則為參考,&前面有等號 (ex: int* p = &a),則為取址。
    由於&同時具有多種意義,因此容易造成混淆。這裡列出的這個方法,可以幫助弄清楚每個&的意義。
2. call by value? call by address (或call by pointer)? call by reference? -- swap(int* a, int* b) v.s. swap (int &a, int &b)
JAVA中的reference與C++的reference意義上並不相同,卻使用同一個字,這也是reference容易造成混淆的原因。在此,我們暫不考慮JAVA中reference的觀念 (關於java中reference的觀念,請參考Reference in JAVA -- 淺談java的指標),純粹把主題放在C/C++上。
呼叫副函式時,call by value, address, 或reference是三種不同的參數傳遞方式。其意義如下:
  • call by value
    假設函式A呼叫函式B(p, q),則B中的p和q是「複製」自函式A所傳入的參數,B中對p, q所做的任何運算都不會影響到A中的p和q,因為B執行完後,並不會把複製的p, q存回到A中。這種參數傳遞方式,我們稱之為call by value。
    以swap這個常見的函式為例,若swap寫成下面的樣子:
    void swap(int a, int b){
    int tmp = a;
    a = b;
    b = tmp;
    }

    則呼叫
    swap(x, y)
    後,x和y的值並不會有變化。
  • call by address (或call by pointer)
    利用指標來做參數傳遞,這種方法骨子裡仍是call by value,只不過call by value的value,其資料型態為指標罷了。我們同樣看看用call by address來寫swap交換兩個integer的例子。
    void swap(int* a, int* b){
    int tmp = *a;
    *a = *b;
    *b = tmp;
    }

    呼叫swap時,要寫成swap(&x, &y)。呼叫swap時,x的指標 (x的儲存位置) 與y的指標 (y的儲存位置) 會被複製一份到swap中,然後把該位置內所記載的值做更換。swap結束後,&x (address of x) 和&y (address of y) 依然沒變,只是address of x所記錄之變數值與address of y所記錄之變數值交換了。因為&x 和&y 其實是利用call by value在傳,因此,call by address其實骨子裡就是call by value。
  • call by reference
    這是C++才加進來的東西,C本身並沒有call by reference。call by reference基本上是把參數做個別名 (alias),以下面的swap為例:
    swap(int &a, int &b){
    int tmp = a;
    a = b;
    b = tmp;
    }

    未來使用時,只要呼叫swap(x, y),就可以讓x和y的值交換。在這個例子中,a 就是 x, b就是 y。這個觀念在上一節已經提過,在此不再贅述。
3. pointer to pointer, reference to pointer (int** v.s. int*&)
當我們用call by pointer (或address) 來傳遞參數時,被呼叫的函式複製一份pointer的值過去。但是,當我們想在函式內改變pointer的值 (而非pointer所指向之變數的值),而且改變的效果要能在函式外看得到時,call by pointer就不足夠用了。此時應該用的是"call by pointer to pointer"或"call by reference to pointer"。我們先看下面的例子:
int g_int = 0;
void changePtr(int* pInt){
pInt = &g_int;
}
void main(){
int localInt = 1;
int* localPInt = &localInt;
changePtr(localPInt);
printf("%d\n", *localPInt);
}

在這個例子中,印出來的數字仍然會是localInt的1,因為changPtr中的pInt是由localPInt「複製」過去的,對pInt做改變並不會反應到localPInt身上。
我們先用pointer to pointer對localPInt做改變,請看下例。
int g_int = 0;
void changePtr(int** pInt){
*pInt = &g_int;
}
void main(){
int localInt = 1;
int* localPInt = &localInt;
changePtr(&localPInt);
printf("%d\n", *localPInt);
}

本例中,印出來的數字會是g_int的0。changePtr函式中的pInt是由&localPInt複製所得,因此對pInt做改變並不會影響main中的&localPInt (資料型態:pointer to pointer to integer)。但在changePtr函式中我們改變的對象是pInt所指向的內容,因此這項改變在main中會顯示出來。

同樣的功能,我們也可改用reference to pointer來完成。但同樣切記,reference是C++才有的功能,因此reference to pointer也只能在支援C++的環境中使用。
int g_int = 0;
void changePtr(int* &refPInt){
refPInt = &g_int;
}
void main(){
int localInt = 1;
int* localPInt = &localInt;
changePtr(localPInt);
printf("%d\n", *localPInt);
}

這段程式印出來的數字會是0。因為在changePtr中,我們宣告的參數型態為int* &,即:reference to pointer to integer。因此,main中的localPInt與changePtr函式中的refPInt其實是「同一件東西」。

另一種常見的混淆是pointer array (指標陣列) 與pointer to pointers,因為兩種都可以寫成**的型式。如,int**可能是pointer to pointer to integer,也可能是integer pointer array。但pointer array的觀念相對來講要簡單且直觀許多,這裡我們就暫不花篇幅敘述。常見的例子:main(int argc, char** argv)其實應該是main(int argc, char* argv[])。

4. function pointer
變數的指標指向變數的位址,同樣的,function pointer (函式指標) 也是指向函式的位址的指標。
函式指標的加入,讓C/C++的符號更複雜,也使更多人望之而卻步。在說明函式指標的用途前,我們先直接由語法來看看函式指標該怎麼宣告、怎麼理解。
假設有個函式長成下面的樣子:
void func1(int int1, char char1);

我們想宣告一個能指向func1的指標,則寫成下面這樣:
void (*funcPtr1)(int, char);

這樣的寫法應理解成:funcPtr1是一個函數指標,它指向的函數接受int與char兩個參數並回傳void。如果今天有另一個函式長成
void func2(int int2, char char2);

則funcPtr1也能指向func2。
指標指向的方法,寫成下面這樣:
funcPtr1 = &func1;

取址符號省略亦可,效果相同:
funcPtr1 = func1;

若欲在宣告時就直接給予初值,則寫成下面這樣:
void (*funcPtr1)(int, char) = &func1;    //&亦可省略


stdlib.h中提供的qsort函式是函式指標最常見的應用之一。此函式之prototype長得如下:
void qsort(void* base, size_t n, size_t size, int (*cmp)(const void*, const void*));

其中的int (*cmp)(const void*, const void*) 就使用到函式指標。

函式指標常見的使用時機是multithread時。函數指標負責把函數傳進建立執行緒的API中。
另外,callback function也是常使用函式指標的地方。所謂callback function即:發生某事件時,自動執行某些動作。在event driven的環境中,便時常使用callback function來實現此機制。
事實上,函式指標還能讓C語言實作polymorphism。但礙於篇幅,在此不再詳述。

5. void ** (*d) (int &, char **(*)(char *, char **))....如何看懂複雜的宣告…
在這裡,我們介紹兩種方式來看懂複雜的宣告。第一種要判斷的是:常數與指標混合使用時,到底const修飾的是指標還是指標所指的變數? 第二種是面對如標題所示這種複雜的宣告時,我們要怎麼讀懂它。

5.1 常數與指標的讀法
const double *ptr;
double *const ptr;
double const* ptr;
const double *const ptr;

以上幾個宣告,到底const修飾的對象是指標,還是指標所指向的變數呢?
其實,關鍵在於:*與const的前後關係!
當*在const之前,則是常數指標,反之則為常數變數。因此,
const double *ptr;    // ptr指向常數變數
double *const ptr;    // ptr是常數指標
double const* ptr;    // ptr指向常數變數
const double *const ptr;    // 指向常數變數的常數指標

事實上,在The C++ Programming Language中有提到一個簡單的要訣:由右向左讀!!讓我們用這個要訣再來試一次。
const double *ptr;    // ptr is a pointer points to double, which is a constant
double *const ptr;    // ptr is a constant pointer points to double
double const* ptr;    // ptr is a pointer points to constant double
const double *const ptr;    // ptr is a constant pointer points to double, which is a constant

結果完全相同 :-)

5.2 複雜宣告的讀法 void ** (*d) (int &, char **(*)(char *, char **)).......
其實閱讀C/C++中複雜的宣告有點像是讀英文的長句子,看多了,自然知道句子是怎麼構造出來的。
但對於句子還不熟的人,難免得藉助文法來拆解一個句子。關於C語言複雜宣告的解析文法,最令我印象深刻的,莫過於印度工程師Vikram的"The right-left rule"。他是這麼說的:
「從最內層的括號讀起,變數名稱,然後往右,遇到括號就往左。當括號內的東西都解讀完畢了,就跳出括號繼續未完成的部份,重覆上面的步驟直到解讀完畢。」
舉個例子:void ** (*d) (int &, char*)依下面方式解讀:
1. 最內層括號的讀起,變數名稱: d
2. 往右直到碰到) : (空白)
3. 往左直到碰到( :是一個函數指標
4. 跳出括號,往右,碰到(int &, char*): 此函式接受兩個參數:第一個參數是reference to integer,第二個參數是character pointer。
5. 往左遇上void **: 此函式回傳的型態為pointer to pointer to void。
==> d是一個函式指標,指向的函式接受int&和char*兩個參數並回傳void**的型態。
如何,是不是好懂很多了呢?

標題中的void ** (*d) (int &, char **(*)(char *, char **))其實和上面的例子幾乎一樣,只是函式的第二個參數又是一個函式指標,接受char*和char**兩個參數並回傳char**的型態。

行文至此,把指標和參考的常見問題與混淆大致地提了一些。希望能讓使用C/C++的人在面對或使用指標、參考、或取址時,不再有疙瘩在心中。文中若有不足或錯誤之處,也請高手不吝指教囉。:-)

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...