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.1Fedora 78910下操作通過。

 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.1Fedora 78910下操作通過。

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伺服器時指定了本地介面的IP192.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 SumTCP    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 SumOption的,但是  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...