PPPOE的用户空间实现
PPPOE的用户空间实现
一 终端
终端介绍
终端是一种字符型设备,它有多种类型,通常使用tty来简称各种类型的终端设备。看下面一副图展示了计算机系统与终端之间得联系。
【PPPOE的用户空间实现】
终端驱动程序的主要功能是在程序与相关设备之间进行数据传递。在一个LINUX内核自身的内部,终端基本上包括两个主要的软件部分:设备驱动程序和行规则(见下图)。
设备驱动程序是一个写到具体硬件上面的低级软件,该硬件允许计算机与终端进行交互。
行规则是执行一系列输入输出数据的逻辑处理,把一系列字符映射到其他的上面。
伪终端
伪终端(Pseudo Terminal)是成对的逻辑终端设备,在Linux系统上创建伪终端设备,使用了”pty master”方式,例如/dev/ptm3,它的对应端则会被自动地创建成/dev/pts/3。
伪终端从设备上的数据输入成为伪终端主设备的输入,反之亦然。
二 PPPOE 概述
PPPOE协议是基于PPP协议的协议,在PPPOE应用程序中并没有将PPP协议实现,PPP协议是由PPPD这个用户空间程序实现的,PPPOE程序只实现PPPOE协议部分代码,在适当的时候PPPD程序进行PPP协议。
在PPPOE用户程序与PPPOE服务器连接后,他将会建立一个PPP0设备,此设备是在PPPD程序中进行的,如果与服务器连接成功,他将会一直存在,此设备就与正常的网卡设备一样,但他是个虚拟设备,经过此设备的数据发出去时还是从真实存在的网卡设备接口发送出去的,但这已经在系统中做了转换。
三 PPPD 上文中说过PPPD是PPP协议的实现程序,PPP的实现由四部分组成
PPPD程序首先会进行一些初始化工作,读参数配置参数等,这部分工作非常重要,会在读代码时分析。
然后就开始走协议,代码流向见下:
main.c->main()
{
读参数
配置参数
从LCP协议开始,这是整个协议的开始部分,状态机开始发动了
lcp_lowerup(0);
lcp_open(0);
/* Start protocol */
下面
while (phase != PHASE_DEAD) {
handle_events();
get_input();
等待数据报文进入然后根据状态机处理报文
…………
…………
}
}
PPP协议与PPPOE有着重要的联系,但是PPP协议建立连接部分不是讨论重点,所以会着重讨论在pppoe连接成功后数据的报文流向,数据是如何被处理的。
四 PPPOE
PPPOE代码分析
PPPOE主文件为PPPOE.c,入口函数为main()
在main函数中主要做了两件事情
PPPOE协议中的DISCOVER阶段,处理函数为discovery(),此函数做的动作就是建立一个RAW SOCKET,然后发送报文,接着等待回应,继续处理,最后成功后设置状态进入SESSION阶段,代码片断见下:
do {
。。。。。。。。。。。
sendPADI(conn);
发送报文
conn->discoveryState = STATE_SENT_PADI;
waitForPADO(conn, timeout);
等待回应
。。。。。。。。。。。
} while (conn->discoveryState == STATE_SENT_PADI);
do {
。。。。。。。。。。。。。
sendPADR(conn);
发送报文
conn->discoveryState = STATE_SENT_PADR;
waitForPADS(conn, timeout);
等待回应
。。。。。。。。。。。。。
} while (conn->discoveryState == STATE_SENT_PADR);
conn->discoveryState = STATE_SESSION;
设置状态为SESSION,接下来进入SESSION阶段
PPPOE协议中的SESSION阶段,处理函数为session(),此函数做的动作是首先建立一个RAW SOCKET,然后从PPPD中读入数据,在加上PPPOE发送出去,再接受到数据发送到PPPD程序中,让PPPD进行处理,处理完后,就建立PPPOE连接了,PPP0端口也建立了,并且从SERVER端分配到了IP。从上面的分析我们知道,PPPOE在这个阶段只是做了转发工作,在建立连接后,他就一直做着转发的工作。
for (;
;
) {
。。。。。。。。。。。。。。。。。。。。。。。。。
从PPPD那边收到数据,然后转发出去
if (FD_ISSET(0, &readable)) {
if (conn->synchronous) {
syncReadFromPPP(conn, &packet);
} else {
asyncReadFromPPP(conn, &packet);
}
}
从网卡端口收到数据后通过,发送到PPPD中去
if (FD_ISSET(conn->sessionSocket, &readable)) {
do {
if (conn->synchronous) {
syncReadFromEth(conn, conn->sessionSocket, optClampMSS);
} else {
asyncReadFromEth(conn, conn->sessionSocket, optClampMSS);
}
} while (BPF_BUFFER_HAS_DATA);
}
}
PPPOE建立连接后数据报文走向
如果从eth收到PPPOE数据:
首先数据被PPPOE中RAW SOCKET数据接收,PPPOE解开到达数据包的PPPOE头,然后将数据传送到伪终端主设备,也就是传送到了伪终端的从设备,然后在内核中数据跑到了PPP0接口,这是个设备,他会通过驱动将这个包处理后往上层传送;
如果有数据要发送出去:
首先根据路由知道数据要发送的端口在PPP0设备端口,数据就到达了PPP0端口,PPP0将数据传送到伪终端从设备,也就是传送到了伪终端主设备,然后到达PPPOE,PPPOE程序打上PPPOE包头,然后通过RAW SOCKET从ETH口发送出去。
以上是PPP连接建立后数据报文的走向。
五 PPPOE实现中理解疑难 pppoe建立连接后协议栈协议栈如下
数据报文理应这样,报文下来后先经过PPP封装,然后再PPPOE封装,然后加上以太头出去,这样的处理既直接又高效,但是我们PPPOE用户空间程序上面的分析画出来的数据流程图不是这样的,数据从内核中PPP0接口传送到从终端设备,再传送到PPPOE中打包,发出去,为什么不从PPP0中出来就到PPPOE中去,封装PPPOE报文出去,多好的事情啊,但这里事实不是这样,在PPPOE内核空间实现中将会是上面说的理想状态。在用户空间中,这样处理是为了不破坏LINUX内核中原有的终端实现的接口,因为LINUX内核中是将串行设备作为终端设备来驱动的。
接下来讨论一下PPP0端口与从设备终端之间是如何联系到一起的
1)ppp设备是指在点对点的物理链路之间使用PPP帧进行分组交换的内核网络接口设备,由于Linux内核将串行设备作为终端设备来驱动,于是引入PPP终端规程来实现终端设备与PPP设备的接口. 根据终端设备的物理传输特性的不同,PPP规程分为异步规程(N_PPP)和同步规程(N_SYNC_PPP)两种, 对于普通串口设备使用异步PPP规程.
2)在PPP驱动程序中, 每一tty终端设备对应于一条PPP传输通道(chanell),每一ppp网络设备对应于一个PPP接口单元(unit).从终端设备上接收到的数据流通过PPP传输通道解码后转换成PPP帧传递到PPP网络接口单元,PPP接口单元再将PPP帧转换为PPP设备的接收帧. 反之, 当PPP设备发射数据帧时,发射帧通过PPP接口单元转换成PPP帧传递给PPP通道, PPP通道负责将PPP帧编码后写入终端设备.
从终端对应的文件描述符是ttyfd
ioctl(ttyfd, PPPIOCGCHAN, &chindex)
获取从终端的设备的CHANNEL
ppp_fd = open("/dev/ppp", O_RDWR);
ioctl(ppp_fd, PPPIOCATTCHAN, &chindex) < 0)
ppp_fd绑定到终端上去,就可以读写终端上的输入了
PPP0端口与ppp_dev_fd绑定到一起了,我们通过ppp_dev_fd读写PPP0的输出了
ioctl(ppp_fd, PPPIOCCONNECT, &ifunit)是将ppp_fd与ifunit所对应的接口连接在一起,这样后终端与设备就连接到一起了,从PPP0端口出来的报文就发送到从终端设备了,从终端设备上的数据也就传送到PPP0接口中去了
推荐阅读
- 热闹中的孤独
- JAVA(抽象类与接口的区别&重载与重写&内存泄漏)
- 放屁有这三个特征的,请注意啦!这说明你的身体毒素太多
- 一个人的旅行,三亚
- 布丽吉特,人生绝对的赢家
- 慢慢的美丽
- 尽力
- 一个小故事,我的思考。
- 家乡的那条小河
- 《真与假的困惑》???|《真与假的困惑》??? ——致良知是一种伟大的力量