任务:在一台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 driver 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[url=http://www.pccode.net].net" class="wordstyle"源码的地址为:
http://isdn4linux.org
取得的文件为isdn4k-utils.v3.1pre1.tar.gz,放于/usr/src目录下。
进入/usr/src目录中,开始安装过程。
进入/usr/src:
cd /usr/src
解开压缩文件:
tar xzvf isdn4k-utils.v3.1pre1.tar.gz
命令完成后多了一个子目录isdn4k-utils,isdn4linux的[url=http://www.pccode.net].net" class="wordstyle"源码文件就在其中。
进入该子目录:
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
刚才的问题没有了,但还是未能通过,失败原因是capi20/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 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);
}
作者未定义它们,我也不知道这几个常量该是多少,怎么办?我从capi20/capi20.h文件中发现,这几个函数是" extentions functions (no standard functions)",扩展的非标准函数,我猜测,这几个函数即使功能不正确,也不会影响ISDN的使用,那么,我就可以把那几个未定义的符号注解起来,使编译通过。修改[url=http://www.pccode.net].net" class="wordstyle"源码如下:
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文件,而是一条命令一条命令地输入执行,仔细理解其意义,查看其执行结果。等全部试验成功后,再写成shell文件。现在看来,这种做法非常有效,我会同样来处理类似的问题。
echo 1 > /proc/sys/net/ipv4/ip_dynaddr
拨号上网大部分都是使用动态IP地址,比如我上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 Scope: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 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: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 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
有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: