转发:在Linux下使用ISDN拨号上网
|
1#
发表于 2001-07-31 10:04
转发:在Linux下使用ISDN拨号上网在Linux下使用ISDN拨号上网 张中华(mike_z@21cn.com) [本站会员] 张中华(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 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源码的地址为: 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的源码文件就在其中。 进入该子目录: 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的使用,那么,我就可以把那几个未定义的符号注解起来,使编译通过。修改源码如下: 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 Scopeink inet6 addr: fe80::20:aff1:a4a/10 Scopeink 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 encapocal 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: fe80::220:afff:fef1:a4a/10 Scopeink 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 encapoint-to-Point Protocol UP POINTOPOINT RUNNING NOARP 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 encapoint-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。 看到归看到,还得实际上去一把才知道行不行。 ping 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 isdnctrl l2_prot ippp0 hdlc isdnctrl l3_prot ippp0 trans isdnctrl encap ippp0 syncppp isdnctrl dialmode ippp0 manual isdnctrl dialmax ippp0 3 isdnctrl huptimeout ippp0 600 ifconfig ippp0 0.0.0.0 pointopoint 0.0.0.0 netmask 0.0.0.0 # Add to routing table route add default ippp0 # Run ipppd ipppd user zzh \ noipdefault \ ipcp-accept-local \ ipcp-accept-remote \ defaultroute \ mru 1500 \ mtu 1500 \ /dev/ippp0 & # Dial isdnctrl dial ippp0 /etc/ppp/isdn-stop文件内容如下: # Hangup isdnctrl hangup ippp0 # Stop ipppd kill ipppd # Remove from routing table route del default # Del ISDN interface ifconfig ippp0 down isdnctrl delif ippp0 # Unload module modprobe -r hisax 让它们成为可执行文件: chmod a+x /etc/ppp/isdn-start chomd a+x /etc/ppp/isdn-stop 以后事情就简单了。要拨号上网,下命令/etc/ppp/isdn-start;要下线挂断,下命令/etc/ppp/isdn-stop。 6、 下一步:共享ISDN上网 我这台Linux机器是在一个局域网上的,现在这台机器可以上网了,怎么让局域网内的其它Win98, Win2000机器 通过这台机器也能上网呢?其中一个简单的方案就是使用IP Masquerade(IP伪装)技术。我也做到了,操作过 程写在我的另一篇文章《用Linux+IPChains代替Windows+WinGate》中。 参考资料: Matt Welsh, Matthias Kalle Dalheimer, Lar Kaufman著,Linux权威指南,洪峰 译,中国电力出版社, 2000 http://zzh-cn.com 相关文件下载 ISDN拨号启动:/etc/ppp/isdn-start ISDN挂断下线:/etc/ppp/isdn-stop PAP认证的用户和口令:/etc/ppp/pap-secrets 域名解析:/etc/resolv.conf |