USB 3G上网卡讲解之三

21 篇文章 0 订阅
7 篇文章 0 订阅

USB 3G卡热插拔那些事4——pppd

在上一节中我们知道3G卡设备驱动已经加载好了,并且和ttyUSB*已经绑定成功,意味着我们可以拨号了,和3G卡内部3G模块通信了,而我们知道3G模块通信是tty设备,通过串行设备,这里和3G卡的两种工作模式相吻合—modem模式.
首先我们这里先给出3G工作原理图:
这里写图片描述

我们万事俱备只欠东风了,就是pppd拨号了,当然pppd的源码自己可以去网上下载最新的,自己编译(这里我们只说linux环境的).安装好pppd程序后,我们运行pppd拨号,当然在拨号前我们需要配置下我们的pppd才能完美的工作,才能真正为我们对于的tty驱动和3g驱动运作起来.
主要看pppd运行参数文件/etc/ppp/options(这个参数文件名字,看开发者自己是可以修改的)该文件指定pppd运行的参数,若运行pppd时通过命令行指定参数同时出现时,则选择/etc/ppp/options中的配置.下面我们就看相关的参数:(这里贴上我的配置)

#Copyright (c) 2017 tangbin
#set debug ,send message to /var/log/messages
debug
#To keep pppd on the terminal
nodetach  #若指定updetach则拨号成功后放入后台运行,若为nodetach,则在前台执行
lock  #创建一个锁定文件,其他程序在发现存在这个文件后,就能得知相应的串口已经被使用
#set seriral
/dev/ttyUSB3  #指定连接使用的设备
# set baudrate
115200    #传输速率
user "tangbin"
password "tangbin"
# set flowrate
crtscts  #硬件流控,无硬件流控为nocrtscts
show-password
usepeerdns  #使用服务器端协商的DNS就可以设置参数usepeerdns
noauth
noipdefault  #不使用默认IP就可以加入参数noipdefault
novj  #选中这个选项,将关闭双方的Van Jacobson形式TCP/IP报文头压缩
novjccomp  #选中这个选项,将关闭Van Jacobson形式TCP/IP报文头压缩中的连接ID压缩。Pppd将忽略来自Van Jacobson形式压缩TCP/IP报文头中的连接ID字节,也不要求对方这样做。
noccp  #关闭压缩控制协议协商
# Accept the peer's idea of our local IP address
ipcp-accept-local
# ipcp-accept-remote
ipcp-accept-remote  #pppd将会接受彼端对於它的IP位址的意见,即使远端的IP位址已经在某个选项中指定
connect '/usr/sbin/chat -s -v -f /etc/ppp/peers/wcdma-chat-connect'
disconnect '/usr/sbin/chat -s -v -f /etc/ppp/peers/wcdma-chat-disconnect'

这些信息我们都可以从pppd源码包的帮助文件中获取信息,pppd/tty.c中的函数connect_tty()将会执行这个脚本。主要是对客户端的拨号的应答。
connect '/usr/sbin/chat -s -v -f /etc/ppp/peers/wcdma-chat-connect'
在pppd运行开始,要初始化tty. 就是调用tty_init()
下面给出pppd相关代码:

    /*
     * Initialize each protocol.
     */
    for (i = 0; (protp = protocols[i]) != NULL; ++i)
        (*protp->init)(0);
    /*
     * Initialize the default channel.
     */
    tty_init();

展开tty_init():

void tty_init()
{
    add_notifier(&pidchange, maybe_relock, 0);
    the_channel = &tty_channel;
    xmit_accm[3] = 0x60000000;
}

我们看到里面the_channel = &tty_channel;的操作,struct channel *the_channel;(main.c中)是一个全局变量.

/*
 * This struct contains pointers to a set of procedures for
 * doing operations on a "channel".  A channel provides a way
 * to send and receive PPP packets - the canonical example is
 * a serial port device in PPP line discipline (or equivalently
 * with PPP STREAMS modules pushed onto it).
 */
struct channel {
    /* set of options for this channel */
    option_t *options;
    /* find and process a per-channel options file */
    void (*process_extra_options) __P((void));
    /* check all the options that have been given */
    void (*check_options) __P((void));
    /* get the channel ready to do PPP, return a file descriptor */
    int  (*connect) __P((void));
    /* we're finished with the channel */
    void (*disconnect) __P((void));
    /* put the channel into PPP `mode' */
    int  (*establish_ppp) __P((int));
    /* take the channel out of PPP `mode', restore loopback if demand */
    void (*disestablish_ppp) __P((int));
    /* set the transmit-side PPP parameters of the channel */
    void (*send_config) __P((int, u_int32_t, int, int));
    /* set the receive-side PPP parameters of the channel */
    void (*recv_config) __P((int, u_int32_t, int, int));
    /* cleanup on error or normal exit */
    void (*cleanup) __P((void));
    /* close the device, called in children after fork */
    void (*close) __P((void));
};

extern struct channel *the_channel;

在ppp.h中定义,并声明.
下面看看如何初始化的

struct channel tty_channel = {
    tty_options,
    &tty_process_extra_options,
    &tty_check_options,
    &connect_tty,
    &disconnect_tty,
    &tty_establish_ppp,
    &tty_disestablish_ppp,
    &tty_do_send_config,
    &tty_recv_config,
    &cleanup_tty,
    &tty_close_fds
};

同理这里我们只关注&connect_tty, 这个初始化在tty.c中.

/*
 * connect_tty - get the serial port ready to start doing PPP.
 * That is, open the serial port, set its speed and mode, and run
 * the connector and/or welcomer.
 */
int connect_tty()
{
    char *connector;
    int fdflags;
#ifndef __linux__
    struct stat statbuf;
#endif
    char numbuf[16];

    /*
     * Get a pty master/slave pair if the pty, notty, socket,
     * or record options were specified.
     */
    strlcpy(ppp_devnam, devnam, sizeof(ppp_devnam));
    pty_master = -1;
    pty_slave = -1;
    real_ttyfd = -1;
    if (using_pty || record_file != NULL) {
        if (!get_pty(&pty_master, &pty_slave, ppp_devnam, uid)) {
            error("Couldn't allocate pseudo-tty");
            status = EXIT_FATAL_ERROR;
            return -1;
        }
        set_up_tty(pty_slave, 1);
    }

    /*
     * Lock the device if we've been asked to.
     */
    status = EXIT_LOCK_FAILED;
    if (lockflag && !privopen) {
        if (lock(devnam) < 0)
            goto errret;
        locked = 1;
    }

    /*
     * Open the serial device and set it up to be the ppp interface.
     * First we open it in non-blocking mode so we can set the
     * various termios flags appropriately.  If we aren't dialling
     * out and we want to use the modem lines, we reopen it later
     * in order to wait for the carrier detect signal from the modem.
     */ 
    got_sigterm = 0;
    connector = doing_callback? callback_script: connect_script;
    if (devnam[0] != 0) {
        for (;;) {
            /* If the user specified the device name, become the
               user before opening it. */
            int err, prio;

                 ...

这里我们要特别注意/**/中的注释部分. 就是在这里我们的ppp和tty联系了起来,才真正work. 当然还加载了前面的脚本文件,来获取必要的配置信息.
tty_init()完成后,就是来和ppp内核协议通信来建立必要的ppp网络接口,以供上层应用程序用

     /*
     * If we're doing dial-on-demand, set up the interface now.
     */
    if (demand) {
    /*
     * Open the loopback channel and set it up to be the ppp interface.
     */
    fd_loop = open_ppp_loopback();
    set_ifunit(1);
    /*
     * Configure the interface and mark it up, etc.
     */
    demand_conf();
    }

在open_ppp_loopback()这个函数中调用 make_ppp_unit()建立ppp0接口(当然这个接口名我们也可以自定义)

  /*
 * make_ppp_unit - make a new ppp unit for ppp_dev_fd.
 * Assumes new_style_driver.
 */
static int make_ppp_unit()
{
    int x, flags;

    if (ppp_dev_fd >= 0) {
        dbglog("in make_ppp_unit, already had /dev/ppp open?");
        close(ppp_dev_fd);
    }
    ppp_dev_fd = open("/dev/ppp", O_RDWR);
    if (ppp_dev_fd < 0)
        fatal("Couldn't open /dev/ppp: %m");
    flags = fcntl(ppp_dev_fd, F_GETFL);
    if (flags == -1
        || fcntl(ppp_dev_fd, F_SETFL, flags | O_NONBLOCK) == -1)
        warn("Couldn't set /dev/ppp to nonblock: %m");

    ifunit = req_unit;
    x = ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit);
    if (x < 0 && req_unit >= 0 && errno == EEXIST) {
        warn("Couldn't allocate PPP unit %d as it is already in use", req_unit);
        ifunit = -1;
        x = ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit);
    }
    if (x < 0)
        error("Couldn't create new ppp unit: %m");
    return x;
}

这里我们看到open函数打开了/dev/ppp ,然后发送一个ioctl给ppp协议
在ppp协议里

  static struct file_operations ppp_device_fops = {
    .owner      = THIS_MODULE,
    .read       = ppp_read,
    .write      = ppp_write,
    .poll       = ppp_poll,
    .ioctl      = ppp_ioctl,
    .open       = ppp_open,
    .release    = ppp_release
};

ppp_ioctl调用ppp_unattached_ioctl(),在这个函数里

    switch (cmd) {
    case PPPIOCNEWUNIT:
        /* Create a new ppp unit */
        if (copy_from_user(&req, p, sizeof req))
            break;
        ppp = ppp_create_interface(req.unit, &err, req.ifname);

ppp_create_interface来真正创建这个接口,以供将来应用程序使用.
既然tty和ppp都初始化了,也建立了某种联系,并且能一起协同工作了,那么我们还犹豫什么?
当然是start_link了
start_link(0);
在这个函数里我们才真正调用我们上面长篇大论的connect_tty().
建立连接的过程分两个阶段:LCP,PCAP.
pppd建立连接过程中chat辅助程序起到不可磨灭的作用.
pppd运行后作为一个守护进程来运行,它必然有监控程序,而这个就是handle_events.
有人认为我这里说这么多是扯淡,我感觉不是,为什么呢,虽然我们一般人直接用pppd拨号就可以,但是很多人并不知道为什么,为什么就把pppd、ppp协议、tty、3G模块联系起来的.这里分析下我想功夫不会是白费的.如果一旦出现问题,那么我们很容易定位问题的所在,知其然还要知其所以然.以后我们应该讲讲ppp相关的数据包收发流程.

USB 3G卡热插拔那些事5——PPP协议

或许我们把pppd拨号原理讲完已经万事大吉了,但是这里我想还是需要再说说ppp协议,我们知道3G拨号上网是通过ppp协议,利用的是ppp帧格式,就说3G卡吧,我知道3G卡首先是通过usb接口连接到设备,ppp–>tty–>Gsm/cdma/wcdma,我们在看Gsm/cdma/wcdma的驱动函数的时候,我们会发现usb相关的收发函数,为什么呢?因为在是通过usb接口连接的,那么在设备收发的时候,必须是urb包的形式才能被识别,然后才是Gsm/cdma/wcdma模块驱动,它是串行接口传输.所以在ppp协议到tty部分其实我们不用太关注其他的事情,用正常的理解就可以了.但是到了Gsm/cdma/wcdma模块的驱动部分就复杂了,为什么这么说,首先我们知道它关联了串行驱动tty,还有usb驱动.从硬件上它有一个usb转串的芯片.
前面说了这么多,那么我们就来分析下ppp协议,网上和书上已经有很多相关资料,我这里就偷工减料了^^
首先可以参考《tcp/ip协议卷1》、《linux网络体系结构-linux内核中网络协议的设计与实现》还可以参考网上一些前辈的文章加速理解.

链路控制协议LCP(Link Control Protocol);
网络控制协议NCP(Network Control Protocol);
认证协议:口令验证协议PAP(Password Authentication Protocol)和挑战握手验证协议CHAP(Challenge-Handshake Authentication Protocol)。

这里写图片描述
这里写图片描述
1、LCP协商,协商内容包括除RFC1661中所定义的选项之外,还要考虑PPPOA和PPPOE协议中规定的内容。
2、LCP协商过 后就到了Establish阶段,开始PAP或CHAP认证。PAP为两次握手认证,口令为明文。PAP认证过程如下:发送用户名同口令到认证方,认证方查看是否有此用户,口令是否正确,然后发送相应的响应。CHAP为三次握手认证,口令为密文(密钥)CHAP认证由认证方发送一些随机产生的报文,交给被认证方,被认证方用自己的口令字用MD5算法进行加密,传回密文,认证方用自己保存的口令字及随机报文用MD5算法加密,比较二者的密文,根据比较结果返回响应的响应。
3、认证成功即进行Network阶段协商(NCP),在IP接入中主要是IPCP协商(如IP地址和DNS地址的协商等)。任何阶段的协商失败都将导致链路的拆除。
4、协商成功,则链路建立成功,可以开始传输网络层数据报文。


1、PPP协议组成:
a) 链路控制协议(LCP-Link Control Protocol),完成线路的启动、测试、任选参数的协商和最终线路断开功能
b) 认证协议,最常用的包括口令验证协议PAP(Password Authentication Protocol)和挑战握手验证协议CHAP(Challenge-Handshake Authentication Protocol)
c) 用户认证,主要通过LCP协商采用何种认证协议,但认证协议本身不是PPP协议的范围
d) IP控制协议IPCP(网络控制协议(NCP)),最常用的NCP协议为。它的一个重要功能就是动态分配IP地址;
2、认证方式
2.1口令验证协议(PAP)
PAP是一种简单的明文验证方式。NAS(网络接入服务器,Network Access Server)要求用户提供用户名和口令,PAP以明文方式返回用户信息。很明显,这种验证方式的安全性较差,第三方可以很容易的获取被传送的用户名和口令,并利用这些信息与NAS建立连接获取NAS提供的所有资源。所以,一旦用户密码被第三方窃取,PAP无法提供避免受到第三方攻击的保障措施。
2.2挑战-握手验证协议(CHAP)
CHAP是一种加密的验证方式,能够避免建立连接时传送用户的真实密码。NAS向远程用户发送一个挑战口令(challenge),其中包括会话ID和一个任意生成的挑战字串(arbitrary challengestring)。远程客户必须使用MD5单向哈希算法(one-way hashing algorithm)返回用户名和加密的挑战口令,会话ID以及用户口令,其中用户名以非哈希方式发送。
CHAP对PAP进行了改进,不再直接通过链路发送明文口令,而是使用挑战口令以哈希算法对口令进行加密。因为服务器端存有客户的明文口令,所以服务器可以重复客户端进行的操作,并将结果与用户返回的口令进行对照。CHAP为每一次验证任意生成一个挑战字串来防止受到再现攻击(replay attack)。在整个连接过程中,CHAP将不定时的向客户端重复发送挑战口令,从而避免第3方冒充远程客户(remote client impersonation)进行攻击。
目前大多数模拟拨号连接都采用PPP做为数据链路协议,主要有下面特点:
错误检测
自动协商网络层地址
CHAP or PAP 认证
数据压缩
符合ISO标准
PPP和串行线路Internet协议(SLIP)常常使人混淆,SLIP仅仅支持IP协议,PPP支持IP、IPX、以及AppleTalk等多协议。
PPP将数据链路层为为3个子层:
NCP 建立和协商网络层协议及相应的地址
LCP 建立链路、认证用户和检测链路质量
HDLC 在链路上封装数据包
表2-1 概述PPP及其子层
OSI层 常见协议
第3层 IP、IPX、AppleTalk
第2层 NCP、LCP、HDLC
第1层 EIA/TIA-232、X.24、V.23、V.35和ISDN等
PPP的大多数扩展功能如数据纠错及支持多种网络协议等,都是由LCP和NCP来控制的。LCP用于配置和测试数

据链路,工作方式如下:
第1步 链路建立阶段―――首先打开连接,然后确定相关通信参数(包括MTU、compress type、及链路认证类型。链路设置完后确认帧,然后是可选的链路质量确认阶段,LCP确定链路质量
第2步 可选(必要)的认证阶段―――两种认证方式:质询应答握手认证协议(CHAP)和密码认证协议(PAP)。PPP本身不需要认证,cisco路由器异步线路需要认证,建议使用CHAP认证方式。
第3步 网络层协议阶段―――LCP引导NCP激活和配置网络层协议。这一阶段结束后即可传输数据包。
第4步 链路终止阶段―――LCP指导NCP关闭layer 3。
LCP使用3种类型LCP数据帧完成上述步骤:
链路建立帧(Link establishment frames)―――建立链路
链路终止帧(Link terminateon frames)―――关闭链路
链路维护帧(Link maintenance frames)―――维护链路
PPP 主要由以下几部分组成:
封装:一种封装多协议数据报的方法。PPP 封装提供了不同网络层协议同时在同一链路传输的多路复用技
术。PPP 封装精心设计,能保持对大多数常用硬件的兼容性。克服了SLIP不足之处的一种多用途、点到点协
议,它提供的WAN数据链接封装服务类似于LAN所提供的封闭服务。所以,PPP不仅仅提供帧定界,而且提供
协议标识和位级完整性检查服务。
链路控制协议:PPP 提供的 LCP 功能全面,适用于大多数环境。LCP 用于就封装格式选项自动达成一致
,处理数据包大小限制,探测环路链路和其他普通的配置错误,以及终止链路。LCP 提供的其他可选功能有
:认证链路中对等单元的身份,决定链路功能正常或链路失败情况。
网络控制协议:一种扩展链路控制协议,用于建立、配置、测试和管理数据链路连接。
配置:使用链路控制协议的简单和自制机制。该机制也应用于其它控制协议,例如:网络控制协议(NCP
)。
为了建立点对点链路通信,PPP 链路的每一端,必须首先发送 LCP 包以便设定和测试数据链路。在链路
建立,LCP 所需的可选功能被选定之后,PPP 必须发送 NCP 包以便选择和设定一个或更多的网络层协议。
一旦每个被选择的网络层协议都被设定好了,来自每个网络层协议的数据报就能在链路上发送了。
链路将保持通信设定不变,直到有 LCP 和 NCP 数据包关闭链路,或者是发生一些外部事件的时候(如,
休止状态的定时器期满或者网络管理员干涉)。
PPP工作流程:
当用户拨号接入 ISP 时,路由器的调制解调器对拨号做出确认,并建立一条物理连接。 PC 机向路由器发送一系列的 LCP 分组(封装成多个 PPP 帧)。
这些分组及其响应选择一些 PPP 参数,和进行网络层配置,NCP 给新接入的 PC机分配一个临时的 IP 地址,使 PC 机成为因特网上的一个主机。
通信完毕时,NCP 释放网络层连接,收回原来分配出去的 IP 地址。接着,LCP 释放数据链路层连接。最后释放的是物理层的连接。
PPP和HDLC之间最主要的区别是,PPP是面向字符的,HDLC是面向位的。
与PPP相关的RFC:
RFC1144 TCP/IP数据包压缩
RFC1220 PPP在网桥上的扩充
RFC1334 PPP认证协议
RFC1378 PPP AppleTalk控制协议(ATCP)
RFC1552 PPP互联网数据包交换控制协议(IPXCP)
RFC1570 PPP LCP协议扩充
RFC1661 PPP协议(PPP)
RFC1662 PPP中的HDLC封装
RFC1990 PPP多链路协议(MP)
上面是我从网上摘的一些片段,大致就是这个,这里我们不再具体分析ppp协议数据包具体收发函数流程.
下面我说一下pppoe相关的,因为这里我有点困惑,才搞明白.
首先我们知道ppp协议是针对串行传输的,那么它也有它自己的固定的帧格式,当然是不能直接在以太网上传输的,比如3G拨号,它没有通过以太网口,而是通过3g模块,以串行方式把数据发出去了,最后转行成无线信号. 但是我们知道:
以太网接口(WAN)支持三种连接模式:
·自动模式:即配置接口作为DHCP客户端,使用DHCP方式获取IP地址。
·手动模式:即手动为接口配置IP地址和子网掩码。
·PPPoE:即配置接口作为PPPoE客户端。PPPoE是Point-to-Point Protocol over Ethernet的简称,它利用以太网将大量主机组成网络,通过一个远端接入设备连入因特网,并对接入的每个主机实现控制、计费功能,极高的性能价格比使PPPoE在包括小区组网建设等一系列应用中被广泛采用。
它是和3G是不同的,那么这个时候就用到了pppoe模块,它把ppp协议的帧经过处理封装到以太网帧中传输到网络中.
两者的区别基本有两点:
1.3G不经过pppoe再封装.直接串口传输.
2.wan呢,则是需要pppoe封装的,因为它物理接口是以太网.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值