PPPoE英语:Point-to-Point Protocol Over Ethernet),以太网上的点对点协议,是将点对点协议(PPP)封装在以太网(Ethernet)框架中的一种网络隧道协议。本质上,它是一个允许在以太网 广播域中的两个以太网接口间创建点对点隧道的协议。
PPPoE是一种用于将多个客户端连接到远程场点的规范。PPPoE协议在1999年发布在RFC2516中规范。
PPPoE包含两个阶段:发现阶段,会话阶段。
pppoe报头定义的结构体为pppoe_hdr,
位于文件include/uapi/linux/if_pppox.h,定义如下
struct pppoe_hdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u8 type : 4;
__u8 ver : 4;
#elif defined(__BIG_ENDIAN_BITFIELD)
__u8 ver : 4;
__u8 type : 4;
#else
#error "Please fix <asm/byteorder.h>"
#endif
__u8 code;
__be16 sid;
__be16 length;
struct pppoe_tag tag[0];
} __packed;
报头结构体如下:
1. PPPoE初始化
PPPoE初始化由方法pppoe_init完成,注册两个PPPoE协议处理程序,一个用于处理PPPoE发现数据包,另一个用于处理PPPoE会话数据包。
static int __init pppoe_init(void)
{
……
err = register_pernet_device(&pppoe_net_ops);
……
dev_add_pack(&pppoes_ptype);
dev_add_pack(&pppoed_ptype);
register_netdevice_notifier(&pppoe_notifier);
……
}
static struct packet_type pppoes_ptype __read_mostly = {
.type = cpu_to_be16(ETH_P_PPP_SES),
.func = pppoe_rcv,
};
static struct packet_type pppoed_ptype __read_mostly = {
.type = cpu_to_be16(ETH_P_PPP_DISC),
.func = pppoe_disc_rcv,
};
static struct pernet_operations pppoe_net_ops = {
.init = pppoe_init_net,
.exit = pppoe_exit_net,
.id = &pppoe_net_id,
.size = sizeof(struct pppoe_net),
};
其中pppoe_disc_rcv是发现数据包处理程序,pppoe_rcv是会话数据包处理程序。
PPPoE模块导出了一个procfs条目/proc/net/pppoe。
register_netdevice_notifier(&pppoe_notifier)函数注册了一个通知链。
static struct notifier_block pppoe_notifier = {
.notifier_call = pppoe_device_event,
};
2. PPPoX套接字
PPPox套接字用结构pppox_sock表示,位于include/linux/if_pppox.h,实现了一个通用PPP封装套接字簇。除了PPPoE外,PPP第2层隧道协议L2TP也是用这种套接字。
struct pppox_sock {
/* struct sock must be the first member of pppox_sock */
struct sock sk;
struct ppp_channel chan;
struct pppox_sock *next; /* for hash table */
union {
struct pppoe_opt pppoe;
struct pptp_opt pptp;
} proto;
__be16 num;
};
其中pppoe_opt包含一个名为pa的成员,是一个pppoe_addr结构实例,表示会话id,远程对等体的MAC地址以及使用的网络设备名称。