Chinaunix首页 | 论坛 | 博客
  • 博客访问: 790170
  • 博文数量: 264
  • 博客积分: 592
  • 博客等级: 中士
  • 技术积分: 1574
  • 用 户 组: 普通用户
  • 注册时间: 2011-10-24 22:02
文章分类

全部博文(264)

文章存档

2019年(2)

2018年(1)

2017年(1)

2016年(4)

2015年(14)

2014年(57)

2013年(88)

2012年(97)

分类: LINUX

2013-08-12 19:42:25

转:http://blog.csdn.net/wt0427/article/details/7798508

一、PPPOE协议简介

1Discovery阶段

  此阶段用来建立连接,当一个用户主机想开始一个PPPoE会话时,首先必须进行发现阶段以识别PPPoE Server的以太网MAC地址,并建立一个PPPoE会话标识(Session ID)。


1-1 Discovery阶段的基本工作流程

 

如图1-1所示, Discovery阶段由四个步骤组成,下面将介绍它的基本工作流程。

 

  • PADI:如果要建立一条PPPoE连接,首先PPPoE客户端就要以广播的方式发送一个PADI(PPPoE Active Discovery Initiation)数据包,PADI数据包包括客户端请求的服务。
  • PADO:当PPPoE服务器收到一个PADI包之后,它会判断自己是否能够提供服务,如果能够提供服务的话,就会向客户端发送PADO(PPPoE Active Discovery Offer)数据包来进行回应。PADO数据包包括PPPoE服务器名称和与PADI数据包中相同的服务名。如果PPPoE服务器不能为PADI提供服务,则不允许用PADO数据包响应。
  •  PADR:由于PADI是以广播的形式发送出去的,PPPoE客户端可能收到不止一个PADO数据包,它将审查所有接收到的PADO数据包并根据其中的服务器名或所提供的服务选择一个PPPoE服务器,并向选中的服务器发送PADRPPPoE Active Discovery Request)数据包。PADR数据包包括客户端所请求的服务。
  • PADS:当PPPoE服务器收到客户端发送的PADR包时,它就准备开始一个PPPoE会话,它为PPPoE会话创建一个唯一的PPPoE会话ID,并向客户端发送PADS (PPPoE Active Discovery Session- confirmation)包作为响应。

 

  当发现阶段正常结束后,通信的两端都获得会话标识(Session ID)和对方的MAC地址,它们一起唯一定义一个PPPoE会话。

 

2PPP会话阶段

PPPoE进入PPP会话阶段后,客户端和服务器将进行标准的PPP协商,PPP协商通过后,数据通过PPP封装发送。PPP报文作为PPPoE帧的净荷被封装在以太网帧内,发送到PPPoE链路的对端。Session ID必须是Discovery阶段确定的ID,且在会话过程中保持不变,MAC地址必须是对端的MAC地址。

 

Rp_pppoeppp实现pppoe协议的过程

 

 

二、rp-pppoe代码简释

1、首先,我们会运行一个pppoe-server的程序,该程序主要建立pppoe链路,处理各种pppoe包。其中有以下代码:

for (i = 0; i

       interfaces[i].eh = Event_AddHandler(event_selector,

                                       interfaces[i].sock,

                                       EVENT_FLAG_READABLE,

                                       InterfaceHandler,

                                       &interfaces[i]);

#ifdef HAVE_L2TP

       interfaces[i].session_sock = -1;

#endif

       if (!interfaces[i].eh) {

           rp_fatal("Event_AddHandler failed");

       }

    }

为每个物理接口绑定事件,当有数据包到来时就执行InterfaceHandler函数,该函数调用serverProcessPacket函数,处理收到的各种pppoe相关的包。

 

switch(packet.code) {

    case CODE_PADI:

       processPADI(i, &packet, len);

       break;

    case CODE_PADR:

       processPADR(i, &packet, len);

       break;

    case CODE_PADT:

       /* Kill the child */

       processPADT(i, &packet, len);

       break;

    case CODE_SESS:

       /* Ignore SESS -- children will handle them */

       break;

    case CODE_PADO:

    case CODE_PADS:

       /* Ignore PADO and PADS totally */

       break;

    default:

       /* Syslog an error */

       break;

    }

 

其中对PADR处理,调用processPADR函数,向客户端发回一个PADS后,就调用PPPD:

 

sendPacket(NULL, sock, &pads, (int) (plen + HDR_SIZE));

startPPPD(cliSession);

 

rp-pppoe包中有个pppoe.c的文件,这个是pppoe的客户端,这个pppoe.c分两个阶段处理:discovery阶段和session阶段。Discovery阶段,客户端的pppoe跟服务器的pppoe-server打交道(发送PADI,接收PADO,发送PADR,接收PADS),让pppoe-server调用pppd建立ppp接口;session阶段,服务器在建立了ppp接口后,pppd根据协议会调用pppoe,生成一个服务器端的pppoe(根据判断,直接跳过discovery阶段),跟远端的pppoe进行session阶段的通信。

Pppoemain函数最后,调用了session函数,该函数实现session阶段数据的读写操作:

session(&conn);

 

session函数调用两个读函数来进行数据的传输工作:

asyncReadFromPPP:从ppp接口读取数据,并发送到以太网

asyncReadFromEth:从以太网读取数据,并发送到ppp

 

ppp——客户端pppoe——以太网——服务器pppoe——ppp

 

Pppoe好像起到转发的作用,其实际是打包与拆包。

 

 

Pppoe流程如下图:

2、现在说下pppd的过程

首先说说pppd的相关读写原理

应用程序通过socket 接口发送TCP/IP数据包,pppdmake_ppp_unit函数中 调用ioctrl(PPPIOCNEWUNIT)创建一个网络接口(如ppp0),内核中的PPP协议模块在处理PPPIOCNEWUNIT时,调用 register_netdev向内核注册ppp的网络接口,该网络接口的传输函数指向ppp_start_xmit

当应用程序发送数据时,内核根据IP 地址和路由表,找到ppp网络接口,然后调用ppp_start_xmit函数,此时控制就转移到PPP协议处理模块了。ppp_start_xmit 用函数ppp_xmit_process去发送队列中的所有数据包,ppp_xmit_process又调用ppp_send_frame去发送单个数据 包,ppp_send_frame根据设置,调用压缩等扩展处理之后,又经ppp_push调用pch->chan->ops-> start_xmit发送数据包。

pch ->chan->ops->start_xmit是具体的传输方式了,比如说对于串口发送方式,则是 ppp_async.c: ppp_asynctty_open中注册的ppp_async_send函数,ppp_async_sendppp_async_push函数调用 tty->driver->write把数据发送串口。

 

Ppp在调用tty时,会根据协议,调用其他协议脚本来进行封装,pppd就是在这里调用pppoe,让其产生服务器端的pppoe,把ppp封装成pppoe包,与客户端的pppoe进行session阶段的通信。

阅读(5212) | 评论(0) | 转发(2) |
给主人留下些什么吧!~~