在Linux下使用ISDN撥號上網 (轉)

worldblog發表於2007-12-02
在Linux下使用ISDN撥號上網 (轉)[@more@] 

在下使用ISDN撥號上網

張中華(mike_z@21cn.com),2000.12.02

任務:在一臺Linux機器上,透過ISDN撥號上網。

經過嘗試,我成功地做到了這一點。我的為:藍點Linux 2.0,Kernel-2.2.16,isdn4linux v3.1pre1,上海貝爾生產的ISDN內建卡,型號為SBT6021。
下面介紹具體做法。介紹順序為:核心->isdn4linux->撥號配置->測試。

1、 核心
上海貝爾SBT6021型ISDN內建卡,使用的為Winbond W6692,這是一種與西門子HiSax相容的晶片,該卡是即插即用的,無需手工設定中斷請求號和I/O地址。要Linux核心支援這種卡,在編譯核心時,必須選中下列選項:

ISDN Support
Support synchronous PPP
(多數ISDN裝置撥號上網都是使用同步PPP方式)
Hisax SiemensChipset support
Hisax Support for DSS1
(絕大多數ISDN裝置使用的都是DSS1)
Hisax Support for Winbond W6692

但我不必急於編譯核心,如果現有的核心已經包含這些選項了,我又何必重複一遍呢。那我怎麼知道現有核心是否已經包含這些選項了呢?看核心編譯配置/usr/src/linux/.config(注意:檔名以"."開頭的檔案是隱藏檔案,要用ls -a才可看到。)我看到其中有下面這5行內容,對應於剛才的5個選項:

CONFIG_ISDN=m
CONFIG_ISDN_PPP=y
CONFIG_ISDN_DRV_HISAX=m
CONFIG_HISAX_EURO=y
CONFIG_HISAX_W6692=y

"=y"表示核心直接支援(程式碼已鏈入核心中),"=m"表示模組支援(程式碼在另外的核心中,可由核心載入)。於是這就表明,藍點Linux 2.0的原有核心已包含這些選項了,我就不用重新編譯核心啦。

2、 isdn4linux安裝
欲配置ISDN,要用到isdn4linux這一套。isdn4linux的主要檔案有isdnctrl, ipppd等。
獲取isdn4linux原始碼的地址為:

取得的檔案為isdn4k-utils.v3.1pre1.tar.gz,放於/usr/src目錄下。
進入/usr/src目錄中,開始安裝過程。
進入/usr/src:
cd /usr/src
解開檔案:
xzvf isdn4k-utils.v3.1pre1.tar.gz
命令完成後多了一個子目錄isdn4k-utils,isdn4linux的原始碼檔案就在其中。
進入該子目錄:
cd isdn4k-utils
該目錄中的README檔案詳細介紹瞭如何安裝isdn4linux,照章行事即可。
進行配置:
make config
不做任何改變,用預設的即可。
開始編譯:
make
未能透過,失敗原因是linux/autoconf.h檔案或目錄不存在。這個檔案就是/usr/include/linux/autoconf.h檔案,一查確實不存在。它是在編譯核心的時候生成的,而我並未編譯核心,所以當然沒有了。好吧,想辦法生成它吧。
到linux目錄並進行配置:
cd /usr/src/linux
make menuconfig
不做任何改變,退出,儲存即可。此時一看,/usr/include/linux/autoconf.h檔案出來了,這就夠了,不必真的去編譯核心。
回到isdn4linux目錄再編譯:
cd /usr/src/isdn4k-utils
make
剛才的問題沒有了,但還是未能透過,失敗原因是c20/capi20.c檔案中下面幾個符號未定義:

CAPI_GET_FLAGS
CAPI_SET_FLAGS
CAPI_CLR_FLAGS
CAPI_NCCI_GETUNIT
CAPI_NCCI_OPENCOUNT

對應的一段為:

int
capi20ext_get_flags(unsigned ApplID, unsigned *flagsptr)
{
if (ioctl(applid2fd(ApplID), CAPI_GET_FLAGS, flagsptr) < 0)
return CapiMsgOSReErr;
return CapiNoError;
}

int
capi20ext_set_flags(unsigned ApplID, unsigned flags)
{
if (ioctl(applid2fd(ApplID), CAPI_SET_FLAGS, &flags) < 0)
return CapiMsgOSResourceErr;
return CapiNoError;
}

int
capi20ext_clr_flags(unsigned ApplID, unsigned flags)
{
if (ioctl(applid2fd(ApplID), CAPI_CLR_FLAGS, &flags) < 0)
return CapiMsgOSResourceErr;
return CapiNoError;
}

char *
capi20ext_get_tty_devname(unsigned applid, unsigned ncci, char *buf, size_t size)
{
int unit;
unit = ioctl(applid2fd(applid), CAPI_NCCI_GETUNIT, &ncci);
if (unit < 0)
return 0;
snprintf(buf, size, "/dev/capi/%d", unit);
return buf;
}

char *
capi20ext_get_raw_devname(unsigned applid, unsigned ncci, char *buf, size_t size)
{
int unit;
unit = ioctl(applid2fd(applid), CAPI_NCCI_GETUNIT, &ncci);
if (unit < 0)
return 0;
snprintf(buf, size, "/dev/capi/r%d", unit);
return buf;
}

int capi20ext_ncci_opencount(unsigned applid, unsigned ncci)
{
return ioctl(applid2fd(applid), CAPI_NCCI_OPENCOUNT, &ncci);
}

作者未定義它們,我也不知道這幾個常量該是多少,怎麼辦?我從capi20/capi20.h檔案中發現,這幾個是" extentions functions (no standard functions)",擴充套件的非標準函式,我猜測,這幾個函式即使功能不正確,也不會影響ISDN的使用,那麼,我就可以把那幾個未定義的符號註解起來,使編譯透過。修改原始碼如下:

int
capi20ext_get_flags(unsigned ApplID, unsigned *flagsptr)
{
// if (ioctl(applid2fd(ApplID), CAPI_GET_FLAGS, flagsptr) < 0)
// return CapiMsgOSResourceErr;
return CapiNoError;
}

int
capi20ext_set_flags(unsigned ApplID, unsigned flags)
{
// if (ioctl(applid2fd(ApplID), CAPI_SET_FLAGS, &flags) < 0)
// return CapiMsgOSResourceErr;
return CapiNoError;
}

int
capi20ext_clr_flags(unsigned ApplID, unsigned flags)
{
// if (ioctl(applid2fd(ApplID), CAPI_CLR_FLAGS, &flags) < 0)
// return CapiMsgOSResourceErr;
return CapiNoError;
}

char *
capi20ext_get_tty_devname(unsigned applid, unsigned ncci, char *buf, size_t size)
{
int unit;
// unit = ioctl(applid2fd(applid), CAPI_NCCI_GETUNIT, &ncci);
if (unit < 0)
return 0;
snprintf(buf, size, "/dev/capi/%d", unit);
return buf;
}

char *
capi20ext_get_raw_devname(unsigned applid, unsigned ncci, char *buf, size_t size)
{
int unit;
// unit = ioctl(applid2fd(applid), CAPI_NCCI_GETUNIT, &ncci);
if (unit < 0)
return 0;
snprintf(buf, size, "/dev/capi/r%d", unit);
return buf;
}

int capi20ext_ncci_opencount(unsigned applid, unsigned ncci)
{
// return ioctl(applid2fd(applid), CAPI_NCCI_OPENCOUNT, &ncci);
return 0;
}

再次編譯:
make
透過了,接著安裝:
make install
命令完成後,isdnctrl和ipppd被拷到/sbin目錄下。

3、 撥號配置
我看了一些別人寫的介紹文章,取用他們寫好的例子,修改其中的電話號碼,名等必須修改的相關內容,然後執行。不幸的是,通常都不能成功。因為這些例子都比較完善而複雜,因此難免和我的配置情況不符合而出錯。我決定從簡單入手,只進行最基本最必要的配置,先不寫成檔案,而是一條命令一條命令地輸入,仔細理解其意義,檢視其執行結果。等全部試驗成功後,再寫成shell檔案。現在看來,這種做法非常有效,我會同樣來處理類似的問題。

echo 1 > /proc/sys//ip_dynaddr
撥號上網大部分都是使用動態,比如我上163網,只知道撥打163號碼,並不知道(即中國機房內的163撥號伺服器)的IP地址是多少,也不知道它會給我的機器分配什麼IP地址。往/proc/sys/net/ipv4/ip_dynaddr中寫入"1",就是告訴核心要使用動態IP地址。
modprobe hisax type=36 protocol=2
裝入ISDN卡的驅動程式。前面已經說過,上海貝爾SBT6021型ISDN內建卡中使用的晶片W6692是由HiSax驅動程式驅動的,type=36指明瞭是使用W6692晶片,protocol=2指明瞭ISDN協議是用DSS1,詳細內容可看/usr/src/linux/Documentation/isdn/README.HiSax檔案。
isdnctrl addif ippp0
ISDN透過同步PPP方式上網,其對應的Interface會是/dev/ippp0, /dev/ippp1, ...等。這條命令告訴核心,加入ippp0這個Interface,換句話說,告訴核心我有個ISDN裝置,準備透過同步PPP方式上網。從此以後,ippp0就代表了我的ISDN裝置。
isdnctrl addphone ippp0 out 163
指明撥出的電話號碼,我撥出的是163,在中國大陸,透過中國電信上網的大部分也都是163。
isdnctrl eaz ippp0 3382460
指明我自己這臺ISDN的電話號碼為3382460。
isdnctrl l2_prot ippp0 hdlc
第2層協議用的是hdlc。
isdnctrl l3_prot ippp0 trans
第3層協議用的是trans。
isdnctrl encap ippp0 syncppp
用同步PPP方式包裝。
isdnctrl dialmode ippp0 manual 
手工撥號方式,在此方式下,要撥號時下isdnctrl dial ippp0命令,要結束通話時下isdnctrl hangup ippp0命令。另外還有auto自動方式,不過,還是先用手工方式比較直觀穩妥一點吧。
isdnctrl dialmax ippp0 3
1次撥號有可能連不通,不要緊,程式會自動重撥的,這裡規定了最多重撥3次。
isdnctrl huptimeout ippp0 600
線路空閒一段時間後,會自動結束通話,預設是空閒10秒後結束通話,太短了,我改為600秒。

ISDN特定的基本配置就這麼多了,下面轉到普通的配置上來。Linux的普通網路配置主要有兩個方面:(1)對各Interface本身的配置。Interface指以太,Modem,ISDN等裝置,配置資訊包括網路地址,網路掩碼,閘道器等,相應的配置工具為ifconfig程式。(2)對錶的配置。路由表規定了到各個目的地的資料包應走哪個閘道器,使用哪個Interface等,相應的配置工具為route程式。

在進行配置之前,先看一下我目前的配置情況是什麼樣的。我這臺Linux機器在一個上,區域網的域名為thalia.com,地址為210.96.100.0,這臺機器的主機名為gate,地址為210.96.100.10。
看Interface配置:
ifconfig
得到類似下面的資訊:

eth0 Link encap:Ethernet HWaddr 00:20:AF:F1:0A:4A 
inet addr:210.96.100.10 Bcast:210.96.100.255 Mask:255.255.255.0
inet6 addr: fe80::220:afff:fef1:a4a/10 pe:Link
inet6 addr: fe80::20:aff1:a4a/10 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:14 errors:0 dropped:0 overruns:0 frame:0
TX packets:34 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:100 
Interrupt:10 Base address:0x6000 

lo Link encap:Local Lback 
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:3924 Metric:1
RX packets:38 errors:0 dropped:0 overruns:0 frame:0
TX packets:38 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0

有2個Interface,eth0代表了乙太網卡,lo是個“虛”的loopback裝置。
看路由表配置:
route
得到類似下面的資訊:

Kernel routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
gate.thalia.* 255.255.255.255 UH 0 0 0 eth0
210.96.100.0 * 255.255.255.0 U 0 0 0 eth0
127.0.0.0 * 255.0.0.0 U 0 0 0 lo

有3條路由資訊。
要用上ISDN(ippp0),必須用ifconfig對ippp0進行配置,並用route增加1條使用它的路由。
配置ippp0:
ifconfig ippp0 0.0.0.0 pointopoint 0.0.0.0 netmask 0.0.0.0
指明使用PPP(pointopoint)協議,pointopoint前面是本機地址,後面是遠端伺服器地址,這裡全是0.0.0.0,行嗎?行,因為前面已經作了配置echo 1 > /proc/sys/net/ipv4/ip_dynaddr,告訴核心將使用動態IP地址,因此在這裡用0.0.0.0並無所謂,當這個PPP連線建立起來後,它會自動用真正的IP地址代替這些0.0.0.0的。
這時候再用ifconfig看一下情況如何:
ifconfig
得到類似下面的資訊:

eth0 Link encap:Ethernet HWaddr 00:20:AF:F1:0A:4A 
inet addr:210.96.100.10 Bcast:210.96.100.255 Mask:255.255.255.0
inet6 addr: fe80::220:afff:fef1:a4a/10 Scope:Link
inet6 addr: fe80::20:aff1:a4a/10 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:64 errors:0 dropped:0 overruns:0 frame:0
TX packets:38 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:100 
Interrupt:10 Base address:0x6000 

ippp0 Link encap:Point-to-Point Protocol 
UP POINTOPOINT RUNNING NO MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:30 

lo Link encap:Local Loopback 
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:3924 Metric:1
RX packets:44 errors:0 dropped:0 overruns:0 frame:0
TX packets:44 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0

多了1個ippp0 Interface,但由於未曾建立真正的連線,它的本機地址和遠端伺服器地址都未給出。
增加1條使用ippp0的路由:
route add default ippp0
這裡指定預設(default)的資料包都經過ippp0傳送。預設的資料包?什麼意思?剛才我用route看時有3條路由資訊,這些資訊指定了發往3個目的地(Destination)的資料包該怎麼走,其它沒有指定怎麼走的資料包(在這裡,就是除了哪3種資料包之外的所有資料包),即為預設資料包,比如要到168.160.224.103(新浪網sina.com.cn)的資料包,就屬於預設資料包。於是這條命令就做到了這一點:如果我要上新浪網看新聞,就得經過ippp0(ISDN),這正是我所希望的。
這時候再用route看一下情況如何:
route
得到類似下面的資訊:

Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
gate.thalia.com * 255.255.255.255 UH 0 0 0 eth0
210.96.100.0 * 255.255.255.0 U 0 0 0 eth0
127.0.0.0 * 255.0.0.0 U 0 0 0 lo
default * 0.0.0.0 U 0 0 0 ippp0

多了1條default路由資訊。注意其對應的gateway是"*"。
需要有一個後臺服務程式ipppd來為ISDN PPP服務:
ipppd user zzh noipdefault ipcp-accept-local ipcp-accept-remote defaultroute mru 1500 mtu 1500 /dev/ippp0 &
前面的user zzh指定了使用者名稱為zzh,口令呢?多數163撥號上網使用的方法都是PAP認證,使用者名稱,口令資訊放在/etc/ppp/pap-secrets檔案中,我的使用者名稱為zzh,口令為51888(“我要發發發”,信不信由你,哈),於是修改/etc/ppp/pap-secrets檔案如下:

# Secrets for authentication using PAP
# client server secret IP addresses
zzh * 51888

後面的noipdefault ipcp-accept-local ipcp-accept-remote指明瞭接受遠端撥號伺服器分配的本機地址和遠端伺服器地址,這正是動態IP地址的含義。defaultroute指明瞭當連線建立起來後,增加default路由條目,這是很關鍵的。mru 1500 mtu 1500指定了最大傳送單元和最大接收單元均為1500位元組。這個程式為/dev/ippp0提供服務。

4、 測試
好啦,都準備好啦,試一下吧。
撥號:
isdnctrl dial ippp0
看看發生了什麼情況:
ifconfig
得到類似下面的資訊:

eth0 Link encap:Ethernet HWaddr 00:20:AF:F1:0A:4A 
inet addr:210.96.100.10 Bcast:210.96.100.255 Mask:255.255.255.0
inet6 addr: fe80::220:afff:fef1:a4a/10 Scope:Link
inet6 addr: fe80::20:aff1:a4a/10 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:29 errors:0 dropped:0 overruns:0 frame:0
TX packets:36 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:100 
Interrupt:10 Base address:0x6000 

ippp0 Link encap:Point-to-Point Protocol 
inet addr:61.142.84.71 P-t-P:202.105.161.206 Mask:255.0.0.0
UP POINTOPOINT RUNNING NOARP MTU:1500 Metric:1
RX packets:22 errors:0 dropped:0 overruns:0 frame:0
TX packets:21 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:30 

lo Link encap:Local Loopback 
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:3924 Metric:1
RX packets:43 errors:0 dropped:0 overruns:0 frame:0
TX packets:43 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0

ippp0中的本機地址(inet addr)是61.142.84.71,PPP遠端伺服器地址(P-t-P)是202.105.161.206,代替了原來指定的0.0.0.0,說明動態IP地址是起作用的。
route
得到類似下面的資訊:

Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
gate.thalia.com * 255.255.255.255 UH 0 0 0 eth0
210.96.100.0 * 255.255.255.0 U 0 0 0 eth0
127.0.0.0 * 255.0.0.0 U 0 0 0 lo
202.0.0.0 * 255.0.0.0 U 0 0 0 ippp0
default 202.105.161.206 0.0.0.0 UG 0 0 0 ippp0

default對應的gateway是202.105.161.206,代替了原來的"*",再看清楚點,它正好是ippp0的遠端伺服器地址。
怎麼知道ISDN的狀態呢?
imontty
得到類似下面的資訊:

ISDN channel status:
Channel Usage Type Number
----------------------------------------------------------------------
HiSax Out Net 163
HiSax Off 

HiSax1是連出去了(Out),撥打號碼是163。
看到歸看到,還得實際上去一把才知道行不行。
168.160.224.103 (新浪網sina.com.cn)
通的。
ping sina.com.cn
不行了。域名解析的問題,找到/etc/resolv.conf檔案,修改如下:

nameserver 127.0.0.1

指明域名伺服器為127.0.0.1(“虛”的loopback),實際上經由路由表中的default條目,轉發到遠端伺服器上,暗渡陳倉了。
再ping sina.com.cn,唔,OK了。
要結束通話,下命令:
isdnctrl hangup ippp0
如果想要乾淨地恢復原狀,繼續下面的動作,和剛才的撥號配置正好一一相反。
kill ipppd
停止ipppd服務。(看別人寫的,我試的時候卻殺不掉,不知是不是因為ipppd是在後臺執行的緣故。)
route del default
去掉路由表中增加的條目。
ifconfig ippp0 down
isdnctrl delif ippp0
關停並刪除ippp0 Interface。
modprobe -r hisax
卸出驅動程式。(我試的時候,有時也卸不掉。)

5、 形成shell檔案
逐行命令操作全部透過啦,下面把它們寫成shell檔案。
/etc/ppp/isdn-start檔案內容如下:

# Dyna address
echo 1 > /proc/sys/net/ipv4/ip_dynaddr

# Load module
modprobe hisax type=36 protocol=2

# Add and config ISDN interface
isdnctrl addif ippp0
isdnctrl addphone ippp0 out 163
isdnctrl eaz ippp0 3382460

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-987347/,如需轉載,請註明出處,否則將追究法律責任。

相關文章