L2tp flow

来源:互联网 发布:数据库的安全性设计 编辑:程序博客网 时间:2024/05/16 04:27

1 主要模块

       (1) l2tpd deamon

       (2) pppd deamon

       (3) ppp driver module

       (4) fast forward module

       (5) protocol stack

2 数据包收发流程

       router 作为LAC端,与L2TP server进行连接。

(1)   WAN------------------>LAN

1      l2tp协议控制封包

                   

图1

             l2tp  控制协议收发流程: 1->2->3->4

2      ppp相关协议封包

                                                                                                                                       图2

      ppp相关协议收发流程:1->2->3->4->5->6->7->8

3      data封包(封装在ppp)

             

图3

     Data封包收发流程如下:1->2->3->4->5->6

   Ethernet driver 收到封包后,通过Protocol Stack将封包送到socket buffer, L2TPD deamonsocketdata ptk。若ptkl2tp协议相关的,则l2tpd deamon自己处理,之后通过socket发送到Protocol Stack,最终通过Ethernet driver发送封包;若ptkdate封包和ppp控制封包,则将l2tp协议的header剥掉后,PTY device。之后,PPP DriverPTYptk,通过ppp头来区分具体是IP data ptk还是ppp相关的ptk。若是PPP协议相关的,则由PPPD deamon来处理,PPPD会将control ptk 通过PPP Driver writePTY,然后由L2TPD deamonPTY read ptk,打上l2tpheader,通过socketptk发送到protocol Stack,最终通过ethernet driver 来发送;若是IP data ptk,则将data ptk送到 Protocol Stack,最终通过Ethernet driver发送。

(2) LAN ----------------->WAN

图4

       LAN PC 发送ptk, router Ethernet driver收到ptk后,rxProtocol Stack,route,得到xmitdevicepppx,调用pppxmit函数,将ptk,打上ppp的头,之后将ptk writePTY deviceL2TPD deamonPTY read ptk,打上l2tp header,通过socket txProtocol Stack,最终通过Ethernet driver TX出去。这样子从LAN PC发送的ptk发到router后,就会打上一层l2tp的包。最终通过driver发送出去。

 

3 数据包收发流程(fast forward的数据收发流程)

 

(1)   WAN------------------> lan

1      l2tp协议控制封包

       如图1

2      ppp相关协议封包

       如图2

3      data封包(封装在ppp)

图5

       基于原来flow,Ethernet driver Rx后,先将ptk送到Fast forward 模块,其他流程维持不变。若Fast forward模块可以将ptk转发,做完相关的NATNAPT之后,直接调用Ethernet driver,将ptk发送;若Fast forward模块无法将ptk转发,则执行之前的流程。

 

(2) LAN ----------------->WAN

图6

case 1:若fast forward能转,则1->2

case 2:若fastforward不能转,ppp driver能转,则1->3->4->5

case 3:若fastforward不能转,ppp driver不能转,则1->3->4->6->7->8->9

LAN PC通过router Ethernet driver rxfast forward 模块。若fast forward能转发,则直接调用pppxmit函数。(l2tpwan device pppx,故会调用pppxmit函数)。在pppxmit函数会将l2tp相关的ptk添加l2tp headerudp headerip header,然后调用ethernet xmit函数发送ptk。这样子可以处理就不需要再将ptk收到上层的l2tpd deamon来处理,打l2tpheader,router 的转发效率会高很多。

 

---------------------------------------------------------------------------------------分割线--------------------------------------------------------------------------------------------------------------------

 

在上述实现过程中会用到raw socket,大概讲下自己的理解。

一定要在root下使用,原始套接字编程可以接收到本机网卡上的数据帧或者数据包,对监听网络的流量和分析是很有作用的.一共可以有3种方式创建这种socket

    1.socket(AF_INET, SOCK_RAW, IPPROTO_TCP|IPPROTO_UDP|IPPROTO_ICMP)发送接收ip数据包,不能用IPPROTO_IP,因为如果是用了IPPROTO_IP,系统根本就不知道该用什么协议。
    对于新的IP层协议,可以自定义新的类型,kernel可以自动识别。

    socket(AF_INET, SOCK_RAW, L2TP_Type),这样子就可以接受IP protocol字段为L2TP_Type的封包,当然不包含传输层。socket收上来是完整的IP封装。
         

   2.socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL))发送接收以太网数据帧
     socket(PF_PACKET,SOCK_RAW,Eth_PPPOE_Discovery),这样子就可以接受ethernet type是Eth_PPPOE_Discovery的封包,这种socket需要在上层应用程序中处理完整的封包。发送的时候需要上层封装完整的ptk。
    3.socket(AF_INET, SOCK_PACKET, htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL))过时了
 
    理解一下SOCK_RAW的原理, 比如网卡收到了一个 14+20+8+100+4(以太网头+ip头+udp头+数据+crc)的udp的以太网数据帧.

    1. socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
    能:该套接字可以接收协议类型为(tcp udp icmp等)发往本机的ip数据包,从上面看的就是20+8+100.
    不能:不能收到非发往本地ip的数据包(ip软过滤会丢弃这些不是发往本机ip的数据包).
    不能:不能收到从本机发送出去的数据包.
    发送的话需要自己组织tcp udp icmp等头部.可以setsockopt来自己包装ip头部,默认的话是kernel自动组装IP层头。
    这种套接字用来写个ping程序比较适合

    2. socket(PF_PACKET, SOCK_RAW, htons(x));
    这个套接字比较强大,创建这种套接字可以监听网卡上的所有数据帧.从上面看就是14+20+8+100.最后一个以太网crc从来都不算进来的,因为内核已经判断过了,对程序来说没有任何意义了.
    能: 接收发往本地mac的数据帧
    能: 接收从本机发送出去的数据帧(第3个参数需要设置为ETH_P_ALL)
    能: 接收非发往本地mac的数据帧(网卡需要设置为promisc混杂模式)
   
    协议类型一共有四个
    ETH_P_IP 0x800 只接收发往本机mac的ip类型的数据帧
    ETH_P_ARP 0x806 只接受发往本机mac的arp类型的数据帧
    ETH_P_ARP 0x8035 只接受发往本机mac的rarp类型的数据帧
    ETH_P_ALL 0x3 接收发往本机mac的所有类型ip arp rarp的数据帧, 接收从本机发出的所有类型的数据帧.(混杂模式打开的情况下,会接收到非发往本地mac的数据帧)
   
    发送的时候需要自己组织整个以太网数据帧.所有相关的地址使用struct sockaddr_ll 而不是struct sockaddr_in(因为协议簇是PF_PACKET不是AF_INET了),比如发送给某个机器,对方的地址需要使用struct sockaddr_ll.

    下面是一部分示例代码:
    ......
    int sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); // 创建Socket
  struct sockaddr_ll sll; // 注意结构体是sockaddr_ll
  memset( &sll, 0, sizeof(sll) );
  sll.sll_family = AF_PACKET;
  struct ifreq ifstruct;
  strcpy(ifstruct.ifr_name, "eth0");
  ioctl(sockfd, SIOCGIFINDEX, &ifstruct); //控制I/O设备
  sll.sll_ifindex = ifstruct.ifr_ifindex;
  sll.sll_protocol = htons(ETH_P_ALL);
  if(bind(fd, (struct sockaddr *) &sll, sizeof(sll)) == -1 ) { //在这里当然仍然需要绑定的
  perror("bind()");
  ......

 

CSDN上写的第一篇blog,mark一下!

原创粉丝点击