所分析的代码:rp_pppoeV3.11; ppp 2.4.4;
rp-pppoe 会创建sock与client进行会话;这个sock是普通的sock,而没有用到pppoe内核模块提供的新的sock。 收到PADS后会调用stratPPPD()来启动pppd,这有两种方式,一种是用户态调用,另一种是kernel态调用,由启动pppoe时的选项-k控制。
pppoe调用的pppd和我们平时用的不一样,并不是pppdttyS0 …这种。 而是pppdplug-in “nic-eth0” rp-pppoe-session。 这样其实是用到了pppd的plugin功能。ppp2.4.4里面有个plugin目录里面有pppoe、radius等。rp-pppoe也有一个plugin.c。 比较了一下和pppd里面的plugin几乎一样,如果编译pppd时打开plugin的话应该用的是pppd的plugin。
在linuxkernel里也提供了对pppoe的支持。具体的在pppoe.c文件中。 在这个文件里提供了一套pppoe的socket; 同时还把对pppoe报文的处理函数(pppoe_rcv())挂到了ptypeall上。ip_rcv()也是挂在ptypeall上的。
pppoe_rcv()会根据pppoe的头,根据po = get_item(ph->sid,eth_hdr(skb)->h_source, dev->ifindex);找到相应的sock,然后调用sk_receive_skb(); 这个函数里:
if(!sock_owned_by_user(sk)) {
/*
* trylock + unlock semantics:
*/
mutex_acquire(&sk->sk_lock.dep_map,0, 1, _RET_IP_);
rc= sk->sk_backlog_rcv(sk, skb);
mutex_release(&sk->sk_lock.dep_map,1, _RET_IP_);
}else
sk_add_backlog(sk,skb);
!sock_owned_by_user(sk)) 是什么意思还没完全清楚!! rc = sk->sk_backlog_rcv(sk, skb); 就是pppoe 内核模块初始化时定义的sk->sk_backlog_rcv= pppoe_rcv_core;
pppoe_rcv_core() 会根据这个sock的状态调相应的处理函数:
如果sk->sk_state& PPPOX_BOUND则进入ppp_input();
如果sk->sk_state& PPPOX_RELAY,则__pppoe_xmit()转发出去;
其它情况调用sock_queue_rcv_skb()进入标准的socket处理(其实就是放到sock的rx队列尾部,即skb_queue_tail(&sk->sk_receive_queue,skb);标准的sock函数会读取这个数据 )。
PPPOX_BOUND这个是在要建立相应的ppp interface时设置的。如ppp0,ppp1时。也就是说:只要pppx接口建立了,数据就全部走ppp_input()了。
ppp2.4.5的plugin里面的pppoe提供了类似pppd里面tty.c的功能。 它用了pppoe内核模块创建的sock(AF_PPPOX),是标准的sock操作,如先创建sock,在connect,然后rx/tx等; 而tty.c则是open一个ttyS0这种串口来建立连接。
下面就是这个plugin函数提供的类似tty.c的功能。
struct channel pppoe_channel = {
options: Options,
process_extra_options: &PPPOEDeviceOptions,
check_options:pppoe_check_options,
connect:&PPPOEConnectDevice,//创建sock,并connect。其实调用pppoe_connect。
disconnect:&PPPOEDisconnectDevice,
establish_ppp:&generic_establish_ppp,
disestablish_ppp:&generic_disestablish_ppp,
send_config: NULL,
recv_config:&PPPOERecvConfig,
close: NULL,
cleanup: NULL
};
PPPOEConnectDevice,//创建sock,并connect。其实调用pppoe_connect。在pppoe_connect()中调用ppp_register_channel()把pppoe_chan_ops注册到pppgeneric层中。
static struct ppp_channel_ops pppoe_chan_ops = {
.start_xmit= pppoe_xmit,
};
pppoe_xmit最终会调用dev_queue_xmit(skb);来发送数据。
rp_pppoe V3.11 里面的pppoe_server.c是pppoe的server端的代码。下面是pppoe server main()的主要的代码:
main()
{
初始化;
参数解析:
-i: 指明使用哪个interface 一般类似 eth0;
-k: 是否用ppp的内核模式在调用pppd是会有些不同;
/* Open all theinterfaces */
for (i=0;i<NumInterfaces; i++) {
interfaces[i].mtu= 0;
interfaces[i].sock= openInterface(interfaces[i].name, Eth_PPPOE_Discovery, interfaces[i].mac,&interfaces[i].mtu); //创建sock
}
………….
/* Create event handler for each interface */
for (i = 0; i<NumInterfaces; i++) {
interfaces[i].eh= Event_AddHandler(event_selector,
interfaces[i].sock,
EVENT_FLAG_READABLE,
InterfaceHandler,
&interfaces[i]);//处理函数InterfaceHandler
}