精华内容
下载资源
问答
  • Linux TCP/IP 协议栈源码分析 - 数据 发送/接收 流程图


    http://lib.csdn.net/article/computernetworks/18626

    http://www.cnblogs.com/my_life/articles/4691254.html


    一.linux内核网络栈代码的准备知识
     
    1. linux内核ipv4网络部分分层结构
     

    BSD socket层: 这一部分处理BSD socket相关操作,每个socket在内核中以struct socket结构体现。这一部分的文件
     
    主要有:/net/socket.c /net/protocols.c etc

    INET socket层:BSD socket是个可以用于各种网络协议的接口,而当用于tcp/ip,即建立了AF_INET形式的socket时,
     
    还需要保留些额外的参数,于是就有了struct sock结构。文件主要
     
    有:/net/ipv4/protocol.c /net/ipv4/af_inet.c /net/core/sock.c etc

    TCP/UDP层:处理传输层的操作,传输层用struct inet_protocol和struct proto两个结构表示。文件主要
     
    有:/net/ipv4/udp.c /net/ipv4/datagram.c /net/ipv4/tcp.c /net/ipv4/tcp_input.c /net/ipv4//tcp_output.c /net/ipv4/tcp_minisocks.c /net/ipv4/tcp_output.c /net/ipv4/tcp_timer.c
     
    etc  
         
    IP层:处理网络层的操作,网络层用struct packet_type结构表示。文件主要有:/net/ipv4/ip_forward.c
    ip_fragment.c ip_input.c ip_output.c etc.

    数据链路层和驱动程序:每个网络设备以struct net_device表示,通用的处理在dev.c中,驱动程序都在/driver/net目
     
    录下。
     
    2. 两台主机建立udp通信所走过的函数列表
     
    ^
    |       sys_read                fs/read_write.c
    |       sock_read               net/socket.c
    |       sock_recvmsg            net/socket.c
    |       inet_recvmsg            net/ipv4/af_inet.c
    |       udp_recvmsg             net/ipv4/udp.c
    |       skb_recv_datagram       net/core/datagram.c
    |       -------------------------------------------
    |       sock_queue_rcv_skb      include/net/sock.h
    |       udp_queue_rcv_skb       net/ipv4/udp.c
    |       udp_rcv                 net/ipv4/udp.c
    |       ip_local_deliver_finish net/ipv4/ip_input.c
    |       ip_local_deliver        net/ipv4/ip_input.c
    |       ip_recv                 net/ipv4/ip_input.c
    |       net_rx_action           net/dev.c
    |       -------------------------------------------
    |       netif_rx                net/dev.c
    |       el3_rx                  driver/net/3c309.c
    |       el3_interrupt           driver/net/3c309.c

    ==========================

    |       sys_write               fs/read_write.c
    |       sock_writev             net/socket.c                    
    |       sock_sendmsg            net/socket.c
    |       inet_sendmsg            net/ipv4/af_inet.c
    |       udp_sendmsg             net/ipv4/udp.c
    |       ip_build_xmit           net/ipv4/ip_output.c
    |       output_maybe_reroute    net/ipv4/ip_output.c
    |       ip_output               net/ipv4/ip_output.c
    |       ip_finish_output        net/ipv4/ip_output.c
    |       dev_queue_xmit          net/dev.c
    |       --------------------------------------------
    |       el3_start_xmit          driver/net/3c309.c
    V
     
     
    3. 网络路径图、重要数据结构sk_buffer及路由介绍
     
        linux-net.pdf 第2.1章 第2.3章 第2.4章
        
    4. 从连接、发送、到接收数据包的过程
     
        linux-net.pdf 第4、5、6章详细阐述
     
     
    二.linux的tcp-ip栈代码的详细分析
     
    1.数据结构(msghdr,sk_buff,socket,sock,proto_ops,proto)
     
    bsd套接字层,操作的对象是socket,数据存放在msghdr这样的数据结构:
     
    创建socket需要传递family,type,protocol三个参数,创建socket其实就是创建一个socket实例,然后创建一个文件描述符结构,并且互相建立一些关联,即建立互相连接的指针,并且初始化这些对文件的写读操作映射到socket的read,write函数上来。
     
    同时初始化socket的操作函数(proto_ops结构),如果传入的type参数是STREAM类型,那么就初始化为SOCKET->ops为inet_stream_ops,如果是DGRAM类型,则SOCKET-ops为inet_dgram_ops。对于inet_stream_ops其实是一个结构体,包含了stream类型的socket操作的一些入口函数,在这些函数里主要做的是对socket进行相关的操作,同时通过调用下面提到的sock中的相关操作完成socket到sock层的传递。比如在inet_stream_ops里有个inet_release的操作,这个操作除了释放socket的类型空间操作外,还通过调用socket连接的sock的close操作,对于stream类型来说,即tcp_close来关闭sock
    释放sock。
     
    创建socket同时还创建sock数据空间,初始化sock,初始化过程主要做的事情是初始化三个队列,receive_queue(接收到的数据包sk_buff链表队列),send_queue(需要发送数据包的sk_buff链表队列),backlog_queue(主要用于tcp中三次握手成功的那些数据包,自己猜的),根据family、type参数,初始化sock的操作,比如对于family为inet类型的,type为stream类型的,sock->proto初始化为tcp_prot.其中包括stream类型的协议sock操作对应的入口函数。
     
    在一端对socket进行write的过程中,首先会把要write的字符串缓冲区整理成msghdr的数据结构形式(参见linux内核2.4版源代码分析大全),然后调用sock_sendmsg把msghdr的数据传送至inet层,对于msghdr结构中数据区中的每个数据包,创建sk_buff结构,填充数据,挂至发送队列。一层层往下层协议传递。一下每层协议不再对数据进行拷贝。而是对sk_buff结构进行操作。
     
    inet套接字及以下层 数据存放在sk_buff这样的数据结构里:
     
    路由:
        
        在linux的路由系统主要保存了三种与路由相关的数据,第一种是在物理上和本机相连接的主机地址信息表,第二种是保存了在网络访问中判断一个网络地址应该走什么路由的数据表;第三种是最新使用过的查询路由地址的缓存地址数据表。
        1.neighbour结构  neighbour_table{ }是一个包含和本机所连接的所有邻元素的信息的数据结构。该结构中有个元素是neighbour结构的数组,数组的每一个元素都是一个对应于邻机的neighbour结构,系统中由于协议的不同,会有不同的判断邻居的方式,每种都有neighbour_table{}类型的实例,这些实例是通过neighbour_table{}中的指针next串联起来的。在neighbour结构中,包含有与该邻居相连的网络接口设备net_device的指针,网络接口的硬件地址,邻居的硬件地址,包含有neigh_ops{}指针,这些函数指针是直接用来连接传输数据的,包含有queue_xmit(struct * sk_buff)函数入口地址,这个函数可能会调用硬件驱动程序的发送函数。
     
        2.FIB结构 在FIB中保存的是最重要的路由规则,通过对FIB数据的查找和换算,一定能够获得路由一个地址的方法。系统中路由一般采取的手段是:先到路由缓存中查找表项,如果能够找到,直接对应的一项作为路由的规则;如果不能找到,那么就到FIB中根据规则换算传算出来,并且增加一项新的,在路由缓存中将项目添加进去。
        3.route结构(即路由缓存中的结构)
     
     
     
    数据链路层:
      
       net_device{}结构,对应于每一个网络接口设备。这个结构中包含很多可以直接获取网卡信息的函数和变量,同时包含很多对于网卡操作的函数,这些直接指向该网卡驱动程序的许多函数入口,包括发送接收数据帧到缓冲区等。当这些完成后,比如数据接收到缓冲区后便由netif_rx(在net/core/dev.c各种设备驱动程序的上层框架程序)把它们组成sk_buff形式挂到系统接收的backlog队列然后交由上层网络协议处理。同样,对于上层协议处理下来的那些sk_buff。便由dev_queue_xmit函数放入网络缓冲区,交给网卡驱动程序的发送程序处理。
     
       在系统中存在一张链表dev_base将系统中所有的net_device{}结构连在一起。对应于内核初始化而言,系统启动时便为每个所有可能支持的网络接口设备申请了一个net_device{}空间并串连起来,然后对每个接点运行检测过程,如果检测成功,则在dev_base链表中保留这个接点,否则删除。对应于模块加载来说,则是调用register_netdev()注册net_device,在这个函数中运行检测过程,如果成功,则加到dev_base链表。否则就返回检测不到信息。删除同理,调用
    unregister_netdev。
     
     
    2.启动分析
     
        2.1 初始化进程 :start-kernel(main.c)---->do_basic_setup(main.c)---->sock_init(/net/socket.c)---->do_initcalls(main.c)
     
    void __init sock_init(void)
    {
     int i;
     
     printk(KERN_INFO "Linux NET4.0 for Linux 2.4/n");
     printk(KERN_INFO "Based upon Swansea University Computer Society NET3.039/n");
     
     /*
      * Initialize all address (protocol) families. 每一项表示的是针对一个地址族的操作集合,例如对于ipv4来说,在net/ipv4/af_inet.c文件中的函数inet_proto_init()就调用sock_register()函数将inet_families_ops初始化到属于IPV4的net_families数组中的一项。
      */
     
     for (i = 0; i < NPROTO; i++)
      net_families[i] = NULL;  
     
     /*
      * Initialize sock SLAB cache.初始化对于sock结构预留的内存的slab缓存。
      */
     
     sk_init();
     
    #ifdef SLAB_SKB
     /*
      * Initialize skbuff SLAB cache 初始化对于skbuff结构的slab缓存。以后对于skbuff的申请可以通过函数kmem_cache_alloc()在这个缓存中申请空间。
      */
     skb_init();
    #endif
     
     /*
      * Wan router layer.
      */
     
    #ifdef CONFIG_WAN_ROUTER 
     wanrouter_init();
    #endif
     
     /*
      * Initialize the protocols module. 向系统登记sock文件系统,并且将其安装到系统上来。
      */
     
     register_filesystem(&sock_fs_type);
     sock_mnt = kern_mount(&sock_fs_type);
     /* The real protocol initialization is performed when
      *  do_initcalls is run. 
      */

     /*
      * The netlink device handler may be needed early.
      */
     
    #ifdef CONFIG_NET
     rtnetlink_init();
    #endif
    #ifdef CONFIG_NETLINK_DEV
     init_netlink();
    #endif
    #ifdef CONFIG_NETFILTER
     netfilter_init();
    #endif
     
    #ifdef CONFIG_BLUEZ
     bluez_init();
    #endif
     
    /*yfhuang ipsec*/
    #ifdef CONFIG_IPSEC            
     pfkey_init();
    #endif
    /*yfhuang ipsec*/
    }
     
     
        2.2 do_initcalls() 中做了其它的初始化,其中包括
     
                    协议初始化,路由初始化,网络接口设备初始化
     
    (例如inet_init函数以_init开头表示是系统初始化时做,函数结束后跟module_init(inet_init),这是一个宏,在include/linux/init.c中定义,展开为_initcall(inet_init),表示这个函数在do_initcalls被调用了)
     
        2.3 协议初始化
    此处主要列举inet协议的初始化过程。
     
    static int __init inet_init(void)
    {
     struct sk_buff *dummy_skb;
     struct inet_protocol *p;
     struct inet_protosw *q;
     struct list_head *r;
     
     printk(KERN_INFO "NET4: Linux TCP/IP 1.0 for NET4.0/n");
     
     if (sizeof(struct inet_skb_parm) > sizeof(dummy_skb->cb)) {
      printk(KERN_CRIT "inet_proto_init: panic/n");
      return -EINVAL;
     }
     
     /*
      * Tell SOCKET that we are alive... 注册socket,告诉socket inet类型的地址族已经准备好了
      */
      
       (void) sock_register(&inet_family_ops);
     
     /*
      * Add all the protocols. 包括arp,ip、ICMP、UPD、tcp_v4、tcp、igmp的初始化,主要初始化各种协议对应的inode和socket变量。
     
    其中arp_init完成系统中路由部分neighbour表的初始化
     
    ip_init完成ip协议的初始化。在这两个函数中,都通过定义一个packet_type结构的变量将这种数据包对应的协议发送数据、允许发送设备都做初始化。

      */
     
     printk(KERN_INFO "IP Protocols: ");
     for (p = inet_protocol_base; p != NULL;) {
      struct inet_protocol *tmp = (struct inet_protocol *) p->next;
      inet_add_protocol(p);
      printk("%s%s",p->name,tmp?", ":"/n");
      p = tmp;
     }
     
     /* Register the socket-side information for inet_create. */
     for(r = &inetsw[0]; r < &inetsw[SOCK_MAX]; ++r)
      INIT_LIST_HEAD(r);
     
     for(q = inetsw_array; q < &inetsw_array[INETSW_ARRAY_LEN]; ++q)
      inet_register_protosw(q);
     
     /*
      * Set the ARP module up 
      */
     
     arp_init();
     
       /*
        * Set the IP module up
        */
     
     ip_init();
     
     tcp_v4_init(&inet_family_ops);
     
     /* Setup TCP slab cache for open requests. */
     tcp_init();

     /*
      * Set the ICMP layer up
      */
     
     icmp_init(&inet_family_ops);
     
     /* I wish inet_add_protocol had no constructor hook...
        I had to move IPIP from net/ipv4/protocol.c :-( --ANK
      */
    #ifdef CONFIG_NET_IPIP
     ipip_init();
    #endif
    #ifdef CONFIG_NET_IPGRE
     ipgre_init();
    #endif
     
     /*
      * Initialise the multicast router
      */
    #if defined(CONFIG_IP_MROUTE)
     ip_mr_init();
    #endif
     
     /*
      * Create all the /proc entries.
      */
    #ifdef CONFIG_PROC_FS
     proc_net_create ("raw", 0, raw_get_info);
     proc_net_create ("netstat", 0, netstat_get_info);
     proc_net_create ("snmp", 0, snmp_get_info);
     proc_net_create ("sockstat", 0, afinet_get_info);
     proc_net_create ("tcp", 0, tcp_get_info);
     proc_net_create ("udp", 0, udp_get_info);
    #endif  /* CONFIG_PROC_FS */
     
     ipfrag_init();
     
     return 0;
    }   
    module_init(inet_init);
                                                     
     
         2.4 路由初始化(包括neighbour表、FIB表、和路由缓存表的初始化工作)
     
                 2.4.1 rtcache表 ip_rt_init()函数 在net/ipv4/ip_output中调用,net/ipv4/route.c中定义
     
                 2.4.2 FIB初始化 在ip_rt_init()中调用 在net/ipv4/fib_front.c中定义
     
               2.4.3 neigbour表初始化  arp_init()函数中定义 
     
         2.5 网络接口设备初始化
                 
                 在系统中网络接口都是由一个dev_base链表进行管理的。通过内核的启动方式也是通过这个链表进行操作的。在系统启动之初,将所有内核能够支持的网络接口都初始化成这个链表中的一个节点,并且每个节点都需要初始化出init函数指针,用来检测网络接口设备。然后,系统遍历整个dev_base链表,对每个节点分别调用init函数指针,如果成功,证明网络接口设备可用,那么这个节点就可以进一步初始化,如果返回失败,那么证明该网络设备不存在或是不可用,只能将该节点删除。启动结束之后,在dev_base中剩下的都是可以用的网络接口设备。
     
                2.5.1 do_initcalls---->net_dev_init()(net/core/dev.c)------>ethif_probe()(drivers/net/Space.c,在netdevice{}结构的init中调用,这边ethif_probe是以太网卡针对的调用)
     
     
     
    3.网络设备驱动程序(略)
            
     
    4.网络连接
     
         4.1 连接的建立和关闭
     
                tcp连接建立的代码如下:
                        server=gethostbyname(SERVER_NAME);
                        sockfd=socket(AF_INET,SOCK_STREAM,0);
                        address.sin_family=AF_INET;
                        address.sin_port=htons(PORT_NUM);
                        memcpy(&address.sin_addr,server->h_addr,server->h_length);
                        connect(sockfd,&address,sizeof(address));
     
           连接的初始化与建立期间主要发生的事情如下:
                          
           1)sys_socket调用:调用socket_creat(),创建出一个满足传入参数family、type、和protocol的socket,调用sock_map_fd()获取一个未被使用的文件描述符,并且申请并初始化对应的file{}结构。
            
           2)sock_creat():创建socket结构,针对每种不同的family的socket结构的初始化,就需要调用不同的create函数来完成。对应于inet类型的地址来说,在网络协议初始化时调用sock_register()函数中完成注册的定义如下:
            struct net_proto_family inet_family_ops={
                    PF_INET;
                    inet_create
            };所以inet协议最后会调用inet_create函数。
            
           3)inet_create: 初始化sock的状态设置为SS_UNCONNECTED,申请一个新的sock结构,并且初始化socket的成员ops初始化为inet_stream_ops,而sock的成员prot初始化为tcp_prot。然后调用sock_init_data,将该socket结构的变量sock和sock类型的变量关联起来。
     
           4)在系统初始化完毕后便是进行connect的工作,系统调用connect将一个和socket结构关联的文件描述符和一个sockaddr{}结构的地址对应的远程机器相关联,并且调用各个协议自己对应的connect连接函数。对应于tcp类型,则sock->ops->connect便为inet_stream_connect。
     
     
           5)inet_stream_connect: 得到sk,sk=sock->sk,锁定sk,对自动获取sk的端口号存放在sk->num中,并且用htons()函数转换存放在sk-&amp;gt;sport中。然后调用sk->prot->connect()函数指针,对tcp协议来说就是tcp_v4_connect()函数。然后将sock->state状态字设置为SS_CONNECTING,等待后面一系列的处理完成之后,就将状态改成SS_CONNECTTED。
     
           6) tcp_v4_connect():调用函数ip_route_connect(),寻找合适的路由存放在rt中。ip_route_connect找两次,第一次找到下一跳的ip地址,在路由缓存或fib中找到,然后第二次找到下一跳的具体邻居,到neigh_table中找到。然后申请出tcp头的空间存放在buff中。将sk中相关地址数据做一些针对路由的变动,并且初始化一个tcp连接的序列号,调用函数tcp_connect(),初始化tcp头,并设置tcp处理需要的定时器。一次connect()建立的过程就结束了。
     
           连接的关闭主要如下:
     
            1)close: 一个socket文件描述符对应的file{}结构中,有一个file_operations{}结构的成员f_ops,它的初始化关闭函数为sock_close函数。
     
            2)sock_close:调用函数sock_release(),参数为一个socket{}结构的指针。
     
            3)sock_release:调用inet_release,并释放socket的指针和文件空间
     
            4)inet_release: 调用和该socket对应协议的关闭函数inet_release,如果是tcp协议,那么调用的是tcp_close;最后释放sk。
     
            4.2 数据发送流程图
     
     
     
    各层主要函数以及位置功能说明:
            1)sock_write:初始化msghdr{}结构 net/socket.c
            2)sock_sendmsg:net/socket.c
            3)inet_sendmsg:net/ipv4/af_net.c
            4)tcp_sendmsg:申请sk_buff{}结构的空间,把msghdr{}结构中的数据填入sk_buff空间。net/ipv4/tcp.c
            5)tcp_send_skb:net/ipv4/tcp_output.c
            6)tcp_transmit_skb:net/ipv4/tcp_output.c
            7)ip_queue_xmit:net/ipv4/ip_output.c
            8)ip_queue_xmit2:net/ipv4/ip_output.c
            9)ip_output:net/ipv4/ip_output.c
            10)ip_finish_output:net/ipv4/ip_output.c
            11)ip_finish_output2:net/ipv4/ip_output.c
            12)neigh_resolve_output:net/core/neighbour.c
            13)dev_queue_xmit:net/core/dev.c
     
     
            4.3 数据接收流程图
     
    各层主要函数以及位置功能说明:
     
            1)sock_read:初始化msghdr{}的结构类型变量msg,并且将需要接收的数据存放的地址传给msg.msg_iov->iov_base.      net/socket.c
            2)sock_recvmsg: 调用函数指针sock->ops->recvmsg()完成在INET Socket层的数据接收过程.其中sock->ops被初始化为inet_stream_ops,其成员recvmsg对应的函数实现为inet_recvmsg()函数. net/socket.c
            3)sys_recv()/sys_recvfrom():分别对应着面向连接和面向无连接的协议两种情况. net/socket.c
            4)inet_recvmsg:调用sk->prot->recvmsg函数完成数据接收,这个函数对于tcp协议便是tcp_recvmsg net/ipv4/af_net.c
            5)tcp_recvmsg:从网络协议栈接收数据的动作,自上而下的触发动作一直到这个函数为止,出现了一次等待的过程.函数tcp_recvmsg可能会被动地等待在sk的接收数据队列上,也就是说,系统中肯定有其他地方会去修改这个队列使得tcp_recvmsg可以进行下去.入口参数sk是这个网络连接对应的sock{}指针,msg用于存放接收到的数据.接收数据的时候会去遍历接收队列中的数据,找到序列号合适的.
            但读取队列为空时tcp_recvmsg就会调用tcp_v4_do_rcv使用backlog队列填充接收队列.
            6)tcp_v4_rcv:tcp_v4_rcv被ip_local_deliver函数调用,是从IP层协议向INET Socket层提交的"数据到"请求,入口参数skb存放接收到的数据,len是接收的数据的长度,这个函数首先移动skb->data指针,让它指向tcp头,然后更新tcp层的一些数据统计,然后进行tcp的一些值的校验.再从INET Socket层中已经建立的sock{}结构变量中查找正在等待当前到达数据的哪一项.可能这个sock{}结构已经建立,或者还处于监听端口、等待数据连接的状态。返回的sock结构指针存放在sk中。然后根据其他进程对sk的操作情况,将skb发送到合适的位置.调用如下:
     
            TCP包接收器(tcp_v4_rcv)将TCP包投递到目的套接字进行接收处理. 当套接字正被用户锁定,TCP包将暂时排入该套接字的后备队列(sk_add_backlog).这时如果某一用户线程企图锁定该套接字(lock_sock),该线程被排入套接字的后备处理等待队列(sk->lock.wq).当用户释放上锁的套接字时(release_sock,在tcp_recvmsg中调用),后备队列中的TCP包被立即注入TCP包处理器(tcp_v4_do_rcv)进行处理,然后唤醒等待队列中最先的一个用户来获得其锁定权. 如果套接字未被上锁,当用户正在读取该套接字时, TCP包将被排入套接字的预备队列(tcp_prequeue),将其传递到该用户线程上下文中进行处理.如果添加到sk->prequeue不成功,便可以添加到 sk->receive_queue队列中(用户线程可以登记到预备队列,当预备队列中出现第一个包时就唤醒等待线程.)   /net/tcp_ipv4.c
     
            7)ip_rcv、ip_rcv_finish:从以太网接收数据,放到skb里,作ip层的一些数据及选项检查,调用ip_route_input()做路由处理,判断是进行ip转发还是将数据传递到高一层的协议.调用skb->dst->input函数指针,这个指针的实现可能有多种情况,如果路由得到的结果说明这个数据包应该转发到其他主机,这里的input便是ip_forward;如果数据包是给本机的,那么input指针初始化为ip_local_deliver函数./net/ipv4/ip_input.c
     
            8)ip_local_deliver、ip_local_deliver_finish:入口参数skb存放需要传送到上层协议的数据,从ip头中获取是否已经分拆的信息,如果已经分拆,则调用函数ip_defrag将数据包重组。然后通过调用ip_prot->handler指针调用tcp_v4_rcv(tcp)。ip_prot是inet_protocol结构指针,是用来ip层登记协议的,比如由udp,tcp,icmp等协议。 /net/ipv4/ip_input.c

    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    1.1 内核源代码的组织

    表1是本文要使用的Linux Net/4网络源代码的,其中大部分位于目录/usr/src/linux-2.2.x/net,列表如下,

    插口层 
    BSD Socket 
    /net/socket.c 
    /net/protocols.c 
    INET Socket 
    /ipv4/protocol.c 
    /ipv4/af_inet.c 
    /net/ipv4/core/sock.c 
    协议层 
    TCP/UDP 
    /net/ipv4/udp.c 
    /net/ipv4/datagram.c 
    /net/ipv4/tcp_input.c 
    /net/ipv4//tcp_output.c 
    /net/ipv4/tcp.c 
    /net/ipv4/tcp_minisocks.c 
    /net/ipv4/tcp_timer.c etc... 
    IP 
    /net/ipv4/ip_forward.c 
    /net/ipv4/ip_fragment.c 
    /net/ipv4/ip_input.c 
    /net/ipv4/ip_output.c 
    接口层 
    Ethernet 
    ......

    1.2  Linux中TCP/IP网络层次结构与实现 
    Linux通过一组相邻的软件层实现了TCP/IP模型,它由BSD Socket层、INET Socket层、传输层、网络层,和链路层构成。应用程序使用系统调用向内核函数传递参数和数据从而进入内核空间,由内核中注册的内核函数对相应的数据结构进行处理。

    Linux的TCP/IP层次结构和实现方式如图 1 所示。





    展开全文
  • MMS实现详细分析(BENQ M22模块*--(3)------MMS发送的无线网络连接协议分析MMS在进行发送前,要对无线模块进行无线网络连接,实现GPRS的拨号上网,无线网络连接成功后,才能利用无线模块基于WAP的方式对MMS进行...
     MMS实现详细分析(BENQ M22模块*--(3)------MMS发送的无线网络连接协议分析
    

    MMS在进行发送前,要对无线模块进行无线网络连接,实现GPRS的拨号上网,无线网络连接成功后,才能利用无线模块基于WAP的方式对MMS进行发送,通过移动交换网络,最终发送到终端用户手机上。本节将对无线模块的拨号上网以及建立PPP连接链路的流程图和所用到的协议,如AT指令集、PPP连接协议以及本课题中捕获的无线网络连接的数据进行分析。
         无线网络连接的流程图分析
        本课题中,无线网络采用的是中国移动GPRS网络,无线模块选用的BENQ M22的GSM/GPRS模块。无线网络连接的步骤为:控制模块通过串口利用AT指令集向无线模块发送连接命令,无线模块进行拨号连接到中国移动GPRS 的WAP网关,获得临时分配的IP地址、DNS地址和临时端口号,拨号成功,完成无线网络的连接。利用AT命令无线模块拨号上网以及建立PPP连接的流程图如图4.3所示。
        无线模块的无线网络连接可分为比较明显的两阶段:
        第一阶段,配置无线模块参数,进行一系列初始化,然后发送AT指令拨上中国移
    动GPRS的WAP网关;
        第二阶段,同GPRS的WAP网关通过三阶段协商(即LCP连接协商、CHAP密码
    认证、NCP网络层协议配置)建立PPP连接链路。
        连接成功后,无线模块就会从GPRS的WAP网关上获得一个动态IP地址,该动态
    IP地址一般是10.188.xxx.xxx。接下来,无线模块就可以进行TCP/IP数据包传输,访问
    GPRS网内的主机,完成无线网络的连接,发送封装好的MMS信息。

    AT命令分析
        AT命令集是调制解调器可以识别并执行的命令,专门用于控制GSM Modem,它是由美国Hayes公司所发展的数据传输通信接口。计算机和单片机通过串行端口对Modem发出相应的AT命令,可以调整Modem各种工作方式。AT命令简单、容易掌握,GSM07.07协议对AT指令做了详细的介绍。GSM07.07中定义的AT Command接口,提供了一种移动平台与数据终端设备之间的通用接口。GSM模块提供的命令接口符合GSM07.07规范,在短消息模块收到网络发来的短消息时,能够通过串口发送指示消息,数据终端设备可以向GSM模块发送各种命令。AT指令在当代手机通讯中起着重要的作用,用户能够通过AT指令控制手机的许多行为,包括进行呼叫、拨叫号码、按键控制、短信、电话本、数据业务、补充业务、传真、GPRS等方面的控制。本课题中是控制模块利用AT命令来控制无线模块拨号连接无线网络。

        AT命令的语法是[[351:在每行命令的开头都必须有“AT”或者“at”作前缀,用输入<CR>来结束这个命令。"ATXX”及少量 “AT十XXX”为V.25标准命令集;"AT+CXXX "为GSM标准所扩展的AT命令;"AT+SXXX”为SIEMENS定义扩展的AT命令。
        发送AT指令时,应注意以下几点:
        (1)每个AT命令字符串的最后面必须加上CR(也就是键盘上的Enter键)字符,否则无线模块将不识别此命令。这个字符就是结束符。
        (2)除了“A/”及“+++”命令外,其它的命令必须加上AT两个字符。
        (3)命令字符串可以合成一个字符串后再一次送给调制解调器,但总的字符串长度
    不得超过40个字符,而且所有的字符必须一律大写或小写。
        本课题中,无线模块采用的是BENQ M22的GSM/GPRS模块,因为选用的是专业厂家生产的GPRS模块,设计中可以不再考虑GPRS附着和PDP上下文激活等复杂过程,只需用AT指令操作专用的GPRS模块上网即可。用AT命令设置无线模块的参数时,必须先要将命令写入串口,读取串口的应答数据来判断是否成功。一般命令发送成功时,模块会返回数据“OK",表示命令执行成功。如果没有成功,要继续发送命令,
    发送3次后还是没有成功,就重新初始化串口
        对无线模块GPRS拨号上网,需要用到AT+CGDCONT和ATD这两条指令

        1) AT+CGDCONT=l,"IP”,“CMWAP”
        此命令用于设置GPRS接入网关,其中第二个数表示PDP (Packet Data Protocol)
    类型为:IP或PPP,第三个数表示接入网关为移动梦网,APN是CMWAP,表示中国移动网应用接口。目前只有中国移动全球通卡才可以使用CMNET功能,而动感地带只能使用CMWAP功能。
        2) ATD *99***1#
        ATD用于设置拨号方式,进行拨号,并设法进入在线(on-line)状态。与传统的电话线Modem相比较,ATD指令在GPRS中有些变化,采用如下两种格式:
       格式一:ATD*99[*[<called address>][*[<L2P>][*[<cid>]]]]#
        其中99是个服务码,也可用98的(见格式二),国内一般用99: <called address>是w.x.y.z格式的IP V4地址;<L2P>是用于TE和ME连接的Layer2协议或用于PPP协议的Layer2协议;<cid>是表明PDP上下文的一个数字编号,用AT +CGDCONT命令创建,一般为1, 20
        格式二:ATD*98[*<cid>]#
        含义同上。
        举例如下:
        ATD*99# //用服务码99建立一个连接
        ATD*99* 123.124.125.126*PPP* 1#//用服务码99建立一个连接
                                      HIP地址123.124.125.124, L2P=PPP
                                        //用CID 1, CID的值由AT十CGDCONT事先
                                          指定
        ATD*99***1# //用服务码99建立连接,CID =1,其它用事先指定的值
        ATD*98*1#//用服务码98建立一个IP连接,CID =1
        国内最常用的是:ATD *99***1#,本设计中就采用了此种格式。
        用ATD指令若能拨号上线则返回“CONNECT",否则返回“NO CARRIER"。
    这样,就完成了拨号上GPRS网络的功能,即接收到无线模块返回的“CONNECT",说明无线模块已经连上了移动GPRS的WAP网关,之后即为通过三阶段协商建立PPP链路的过程。
    PPP协议格式分析
        利用AT指令无线模块连接上了中国移动的GPRS的WAP网关之后,接着是进行PPP连接。GPRS拨号采用的是点对点协议(Point to Point Protocol, PPP),该协议是是TCP/IP网络协议包的一个成员。其主要作用是无线模块通过拨号连接上终端服务器后,由PPP取得一个临时的IP地址,使该设备成为网络上的一个节点。只要PPP链路顺利建立成功,获得临时的IP地址,则即可在其上嵌入IP等数据包进行数据传送,完成无线模块的无线网络连接。
        PPP协议(Point-to-Point Protocol点到点协议)是为在同等单元之间传输数据包这样的简单链路设计的链路层协议。这种链路提供全双工操作,并按照顺序传递数据包。其目的主要是用来通过拨号或专线方式建立点对点连接发送数据,使其成为各种主机、网桥和路由器之间简单连接的一种共通的解决方案。

    PPP协议中提供了一整套方案来解决链路建立、维护、拆除、上层协议协商、认证等问题。PPP协议包含了以下几个部分:
        .链路控制协议LCP (Link Control Protocol):LCP负责创建,维护或终止一次物理连接;
        .网络控制协议NCP (Network Control Protocol):NCP是一族协议,负责解决物理连接上运行什么网络协议,以及解决上层网络协议发生的问题;
        .认证协议,PPP协议支持两种验证协议:口令验证协议PAP (PasswordAuthentication Protocol)和挑战握手验证协议CHAP (Challenge-Handshake AuthenticationProtocol)。验证过程在PPP协议中为可选项,在连接建立后进行连接者身份验证的目的是为了防止有人在未经授权的情况下成功连接,从而导致泄密。PAP是一种简单的明文验证方式,安全性较差。而CHAP是对PAP的改进,是一种加密的验证方式,使用挑战口令以哈希算法对口令进行加密,用三次握手的方法周期性地检验对端的节点,优点在于密钥不在网络中传送,不会被窃听。本课题中采用的是挑战握手验证协议CHAP.
        PPP连接的建立主要经过三个阶段,分别是LCP连接协商、CHAP密码认证以及
    NCP网络层协议配置。
        第一阶段:LCP连接协商
        在这个阶段,将对基本的通讯方式进行选择。链路两端设备通过LCP向对方发送LCP数据报配置请求(Configure-Request),对方同意接收后双方互发LCP数据报配置应答(Configure-Ack )。一旦一个配置成功信息包(Configure-Ack packet)被发送且被接收,就完成了交换,进入LCP开启状态。
        第二阶段:CHAP密码认证
        在这个阶段,客户端会将自己的身份发送给远端的接入服务器。服务器向用户发PPP CHAP安全性认证挑战,接着用户给服务器送PPP CHAP安全性认证响应,服务器再向用户发送 PPP CHAP安全性认证成功。
        该阶段使用一种安全验证方式避免第三方窃取数据或冒充远程客户接管与客户端的连接。在认证完成之前,禁止从认证阶段前进到网络层协议阶段。如果认证失败,认证者应该跃迁到链路终止阶段。
        第三阶段:NCP网络层协议配置
        认证阶段完成之后,PPP将调用在链路创建阶段(阶段一)选定的各种网络控制协议(NCP )。选定的NCP解决PPP链路之上的高层协议问题,例如,在该阶段IP控制协议(IPCP)可以向拨入用户分配动态IP地址。在这个阶段,先是用户向服务器发送PPP NCP网络控制数据包(网络协议配置,要求服务器提供IP地址和DNS,NBNS信息),接着服务器向用户发送配置请求PPP NCP网络控制数据包(为用户分配IP地址),用户向服务器发送配置应答PPP NCP网络控制数据包(接受所分配的IP地址),最后服务器向用户发送配置应答PPP NCP网络控制数据包(同意用户的IP地址和DNS地址)。
        这样,经过三个阶段以后,一条完整的PPP链路就建立起来了,用户即可向服务器
    发送IP数据包。


    展开全文
  • 3G模块PPPD拨号流程分析

    千次阅读 2013-11-07 13:52:28
    现在公司的前端产品很多都使用3G模块来作为网络连接的接口,而3G拨号使用PPPD程序,了解拨号程序的流程对解决3G连接网络的问题会有很大的帮助,鉴于此重要性,对PPPD的拨号流程做一个简要的分析。 因为PPPD程序不...

    1       案例描述

    现在公司的前端产品很多都使用3G模块来作为网络连接的接口,而3G拨号使用PPPD程序,了解拨号程序的流程对解决3G连接网络的问题会有很大的帮助,鉴于此重要性,对PPPD的拨号流程做一个简要的分析。

    因为PPPD程序不依赖与硬件平台,这里没有指定具体应用的场景,目的在于说明这个拨号程序的通用性。

    目前公司平台采用的这个程序的版本是ppp-2.4.3。

    本文只对PPPD的拨号流程进行说明,对拨号程序所用的详细参数以及所使用协议的不做具体说明。

     2       案例分析

    总体说明

    PPPD 是一个用户空间的后台服务进程(daemon),负责与3G模块进行通信会话来进行必要的初始化设置,然后开始按照协议所要求的步骤进行拨号。初始化设置是由PPPD自带的一个辅助工具CHAT程序来完成的,这个程序利用AT指令和3G模块进行通信,主要是交互一些拨号的参数设置、进行拨号的用户名和密码、是否采用数据加密等连网必不可少的参数;之后PPPD开始正常的拨号工作。下面开始分析这个拨号的流程。

    补充说明:AT指令是配置3G模块的通用指令,一般通过RS232等串行线路发送到3G等无线模块,格式以及举例如下:

    AT+CGMI?    -------给出模块厂商的标识

    AT---------是AT指令的开始符号,后面的+号表示后面有具体的配置参数;

    CGMI-----这就是具体的配置参数,这个参数后面的?号标示是向3G查询;

    关于3G的AT指令,业界推出了一些标准的指令集合,这些指令在任何厂家的任何模块上都可以使用,也有一些是3G模块生产厂家自己规范的一些AT指令,这些指令就只能在特定的模块上才能使用。

    2.2     PPPD拨号流程

    下面结合PPPD程序的代码对拨号流程做一下分析。

    主程序是pppd_start,这个函数首先是做一些初始化的工作:

    void *pppd_start(void *arg)

    {

     ……………

      for (i = 0; (protp = protocols[i]) != NULL; ++i)

                    (*protp->init)(0);

    tty_init();

    ………

    首先是对几个所使用的协议进行初始化,我们这里所用到的分别是lcp_protent、chap_protent、ipcp_protent。初始化的工作基本一致,就是建立各个协议的状态机进行默认设置,为以后网络的维护做好准备;tty_init()是对3G模块所使用串口进行设置,包括所使用的默认拨特率、停止位、是否校验等,在这个函数里还有一个操作就是设置好网络连接所用的通道:

    the_channel = &tty_channel(这是在tty_init函数里),这个所谓的连接通道是3G连接的重要函数,后面有这个通道的使用说明;

    在完成一些初始化后,正式开始进行协议的协商:

    ……

    pppd_start ->lcp_open();: 打开LCP协议状态机,设置状态为STARTING;

    各个协议的状态机分别是-----

    STARTING 、INITIAL、CLOSED、CLOSING、STOPPED、OPENED。在上面的初始化时把所有的协议状态机都设置为INITIAL。这里打开LCP协议就是把状态设置为STARTING。接着往下看程序,还是在pppd_start()主函数里:

    pppd_start ->start_link();这个函数比较重要,我们把他展开:

    void start_link(unit)

    {        

    char *msg;

            new_phase(PHASE_SERIALCONN);

            devfd = the_channel->connect();

            msg = "Connect script failed";

            if (devfd < 0)

                    goto fail;

     

            /* set up the serial device as a ppp interface */

            /*

             * N.B. we used to do tdb_writelock/tdb_writeunlock around this

             * (from establish_ppp to set_ifunit).  However, we won't be

             * doing the set_ifunit in multilink mode, which is the only time

             * we need the atomicity that the tdb_writelock/tdb_writeunlock

             * gives us.  Thus we don't need the tdb_writelock/tdb_writeunlock.

             */

            fd_ppp = the_channel->establish_ppp(devfd);

            msg = "ppp establishment failed";

            if (fd_ppp < 0) {

                    status = EXIT_FATAL_ERROR;

                    goto disconnect;

            }

            /*

             * Start opening the connection and wait for

             * incoming events (reply, timeout, etc.).

             */

            if (ifunit >= 0)

                    notice("Connect: %s <--> %s", ifname, ppp_devnam);

            else

                    notice("Starting negotiation on %s", ppp_devnam);

            add_fd(fd_ppp);

            status = EXIT_NEGOTIATION_FAILED;

            new_phase(PHASE_ESTABLISH);

            lcp_lowerup(0);

            return;

    disconnect:

            new_phase(PHASE_DISCONNECT);

            if (the_channel->disconnect)

                    the_channel->disconnect();

    fail:

            new_phase(PHASE_DEAD);

            if (the_channel->cleanup)

                    (*the_channel->cleanup)();

    }

    首先调用the_channel->connect(),这是个函数指针,实际调用的是connect_tty(),(这个函数在上面pppd_start->tty_init里进行了设置)他设置一些TTY通道的参数,然后开辟一个线程chat_main,发送AT指令对3G进行配置;一切正常后从connect_tty返回到start_link继续往下执行;

    start_link->the_channel->establish_ppp(devfd)

    下面把establish_ppp()函数展开:

    int tty_establish_ppp (int tty_fd)

    {

    ………

           ret_fd = generic_establish_ppp(tty_fd);

    ………

    }

    一个重要的函数generic_establish_ppp()执行,然后generic_establish_ppp->make_ppp_unit(),在make_ppp_unit()函数里:

    ……

    ppp_dev_fd = open("/dev/ppp", O_RDWR),打开/dev/ppp

    ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit)

    建立一个网络接口ppp0, 如果网络接口成功建立后会显示:

    notice("Connect: %s <--> %s", ifname, ppp_devnam)==Connect: serial0 <--> /dev/ttyUSB0 ,说明网络接口已经建立; (这个时候可以在SHELL下ifconfig看到这个接口);之后再次返回到start_link执行;

     

    接下来开始发起LCP通信:

    start_link->lcp_lowerup():

    这个函数首先和内核的PPP协议进行协商,主要是MTU等链路参数,协商后设置LCP的DELAYED_UP标志,把lcp_delayed_up函数设置给一个定时器,这个定时器会根据DELAYED_UP标志周期执行之前赋给他的函数,就是这个lcp_delayed_up,在这里函数里将主动发起LCP协议请求,之后等待对端的回应。start_link函数结束返回到 pppd_start()主函数继续执行:

    接下来,主函数pppd_start会进入一个无限循环,这个循环主要执行两个函数handle_events()、get_input();

    handle_events主要处理接收到的信号,还有一个功能就是执行定时器的函数,前面曾经设置过一个lcp_delayed_up函数,那么建立连接首先执行这个函数;

    get_input主要是负责处理接收到的数据包,这个函数下面会介绍。先来看lcp_delayed_up:

     

    调用lcp_delayed_up->fsm_lowerup,在上面的lcp_open()函数里LCP的状态已经是STARTING,所以根据这个状态调用fsm_sconfreq(),发送LCP初始化配置请求,然后LCP状态设置为:REQSENT,函数返回;

    fsm_sconfreq先组建一些连接用的参数,然后调用fsm_sdata(f, CONFREQ, f->reqid, outp, cilen),通过/dev/ppp->串口发送请求给3G;然后设置一个超时函数给定时器,一旦发送的这个LCP请求没有回应,则重新发送。

      如果没有超时,那么会收到一个LCP请求的回应:rcvd [LCP ConfReq id=0x0,这个时候数据的接收get_input函数来处理的,那么是如何收到对端发来的数据呢?就是这个get_input(),现在来看一下:

    首先阻塞的读/dev/ppp,一旦收到一个数据包,检查包里的协议,看是哪种协议数据(在Establishment Phase阶段,只接收LCP的协议包,其他的就丢弃);然后调用lcp_input进行实际的数据接收处理;lcp_input->fsm_input,在这个函数里根据收到包的请求命令进行分别处理:比如:rcvd [LCP ConfReq id=。。。。。。;那么就调用fsm_rconfreq()进一步处理,如果接收正确,则会调用fsm_sdata()发送CONFACK命令;然后设置LCP为ACKSENT

    然后等待数据包接收;当接收到一个ConfAck的包后,调用fsm_rconfack时,设置LCP为OPENED,然后lcp_up->lcp_echo_lowerup,注意,此时会调用ipcp_lowerup,设置IPCP为CLOSED!!!之后发送sent [LCP EchoReq id;之后返回到lcp_up,执行link_established,这个函数设置PPP为PHASE_AUTHENTICATE认证阶段,开始加密等认证;之后返回;

    当接收到一个rcvd [CHAP Challenge时,调用chap_input-》chap_respond,发送一个CHAP_RESPONSE,sent [CHAP Response id=0x1,然后继续接收数据包;

    当收到一个rcvd [CHAP Success后,调用chap_handle_status,这个函数打印CHAP authentication succeeded

    然后执行auth_withpeer_success,他继续打印notice("%s authentication succeeded", prot);

    执行network_phase,network_phase->start_networks,改变PPP为PHASE_NETWORK状态,依次执行各个协议的open函数:这个时候IPCP已经为CLOSED的,所以会发送sent [IPCP ConfReq,并设置IPCP为REQSENT

    当接收到一个rcvd [IPCP ConfNak时,会发送sent [IPCP ConfReq id=0x2,再次请求

    当再次收到rcvd [IPCP ConfReq,接着发送sent [IPCP ConfNak id=0x0 <addr 0.0.0.0>]

    当接收到rcvd [IPCP ConfAck id=0x4时,调用IPCP_UP,显示

        notice("local  IP address %I", go->ouraddr);

        notice("remote IP address %I", ho->hisaddr);

    整个的协议协商过程用图表表示为:

     

    Send lcp confreq

    Send lcp ack

    Send chap confreq

    Send chap ack

    Send ipcp confreq

    Send chap ack

    Send ipcp confreq

    Send ipcp ack

    3G网络基站

    拨号端

     

     

     

     

     

                             图2-1  PPPD协议协商流程图

       。说明:箭头表示是数据的发送方向

     

    此时,已经从3G网络获得动态分配的IP地址,表明已经建立连接,可以象使用普通以太网那样使用3G网络的接口。

    从上面的代码中看到,网络的连接是PPPD主动发起的,就是函数start_link,之后就是根据所接收的状态是否满足要求进而进行下一阶段的工作。

    2.3     PPP链路的工作过程

    上面的拨号过程中使用到了PPP拨号的几个不同阶段,这里做一个简单的介绍

    (1) 链路不可用阶段(Link Dead Phase)在最开始,整条链路处于链路不可用状态,此阶段有时也称为物理不可用阶段,PPP链路都需从这个阶段开始和结束,当通信双方的两端检测到物理线路激活时,就会从当前这个阶段进入到链路建立阶段

    (2) 链路建立阶段 (Link Establishment Phase) 在此阶段,PPP链路将进行LCP相关协商,协商内容包括工作方式,认证方式,连路压缩等,LCP在协商成功后进入Opened状态,表示底层链路已经建立,如果链路协商失败,则会返回到第一阶段,在链路建立阶段成功后,如果配置了PPP认证,则会进入认证阶段,如果没有配置PPP认证,则会直接进入网络层协议阶段

    (3) 认证阶段 (Authentication Phase) 在此阶段,PPP将进行用户认证工作,PPP支持PAP和CHAP两种认证方式,关于这两种认证方式在后面将会详细介绍,如果认证失败,PPP链路会进入链路终止阶段,拆除链路,LCP状态转为DOWN,如果认证成功就进入网络层协议阶段

    (4) 网络层协议阶段 (Network-Layer Protocol Phase) 一旦PPP完成了前面几个阶段,每种网络层协议(IP,IPX等)会通过各自相应网络控制协议进行配置,只有相应的网络层协议协商成功后,该网络层协议才可以通过这条PPP链路发送报文,对于IPCP协议,协商的内容主要包括双方的IP地址等

    (5) 链路终止阶段 (Link Termination Phase) PPP能在任何时候终止链路,载波丢失,认证失败后用户人为关闭链路等情况均会导致链路终止,PPP协议通过交换LCP的链路之中报文来关闭链路,当链路关闭时,连路层会通知网络层做相应的操作,而且也会通过物理层强制关断链路

    通过学习PPPD拨号过程,可以更好的理解3G的连接情况,为定义拨号过程以及拨号成功后的数据通信可能出现的问题,有比较清晰的解决思路。

    展开全文
  • PPTP协议握手流程分析

    千次阅读 2016-12-19 22:54:14
    一 PPTP概述  PPTP(Point to Point Tunneling Protocol),即点对点隧道...该协议是在PPP协议的基础上开发的一种新的增强型安全协议,支持多协议虚拟专用网,可以通过密码验证协议,可扩展认证协议等方法增强安

    http://blog.csdn.net/hdxlzh/article/details/46711901

    一  PPTP概述


            PPTP(Point to Point Tunneling Protocol),即点对点隧道协议。该协议是在PPP协议的基础上开发的一种新的增强型安全协议,支持多协议虚拟专用网,可以通过密码验证协议,可扩展认证协议等方法增强安全性。远程用户可以通过ISP、直接连接Internet或者其他网络安全地访问企业网;

            它能够将PPP(点到点协议)帧封装成IP数据包,以便能够在基于IP的互联网上进行传输。PPTP使用TCP是实现隧道的创建、维护与终止,并使用GRE(通用路由封装)将PPP帧封装成隧道数据。被封装后的PPP帧的有效载荷可以被加密或压缩;

            PPTP通信过程中需要建立两种连接,一种是控制连接,另一种是数据连接。控制连接用来协商通信过程中的参数和进行数据连接的维护。而真正的数据通信部分则交由PPTP数据连接完成。以下两个章节分别介绍PPTP的控制连接和数据连接


    二  PPTP控制连接的建立流程分析


        PPTP控制连接建立过程可以分为以下几步:

        1、  建立TCP连接

        2、  PPTP控制连接和GRE隧道建立

        3、  PPP协议的LCP协商

        4、  PPP协议的身份验证

        5、  PPP协议的NCP协商

        6、  PPP协议的CCP协商

        以下以抓包的方式分析上述几个步骤,

        pptp client     :      192.168.163.56 

        pptp server    :      192.168.162.196


    2.1  建立TCP连接


        PPTP控制层协议是建立在TCP协议的基础上,所以刚开始即使普通的TCP三次握手


    图2-1  TCP三次握数据包


    图2-2  TCP三次握手时序图


        1、  Client端向Server的1723端口发TCPSYN包,请求建立TCP连接。

        2、  Server接收TCP连接请求,回SYN ACK。

        3、  Client端向Server发送确认包ACK


    2.2  PPTP控制连接和隧道的建立


        在此过程,完成PPTP控制层连接和Gre隧道建立的工作

    图2-3 PPTP控制层连接建立过程



    图2-4 PPTP控制层连接时序图


        1、  Client向Server发送Start-Control-Connection-Request,请求建立控制连接

        2、  Server向Client发送Start-Control-Connecton-Reply,应答客户端的请求

        3、  Client向Server发送Outgoing-Call-Request,请求建立PPTP隧道,该消息包含GRE报头中的Callid,该id可唯一地标识一条隧道

        4、  Server向Client发送Outgoing-Call-Reply,应答客户端的建立PPTP隧道请求

        5、  有Client或者Server任意一方发出Set-Link-info,设置PPP协商的选项

    2.3 PPP协议的LCP协商


        LCP是PPP协议的链路控制协议,负责建立、拆除和监控数据链路。协商链路参数,如认证方法,压缩方法,是否回叫等。


    图2-5  PPP协议的LCP协商过程



    图2-6  PPP协议的LCP协商时序图


        1、  Client发送一个Configuration Request,把自己的配置参数发送给Server

        2、  Server发送一个Configuration Request,把自己的配置参数发送给Client

        3、  Server发送一个Configuration Reject,将自己不能识别的参数告知Client,让Client进行修正

        4、  Client发送一个Configuration Ack,表示所有配置参数全部认识且可以接受,应答Server

        5、  Client修改配置项后再次发送Configuration Request

        6、  Server发送一个Configuration Ack,标示所有配置参数全部认识且可以接受,应答Client


    2.4  PPP协议的身份认证


        LCP协商完成后,PPP协议的Server端会对Client端进行身份验证,在LCP协商中已经协商好身份验证协议,本文以MS-CHAP-2为例说明


    图2-7  PPP协议的Chap身份认证过程



    图2-8  PPP协议的Chap身份认证时序图


        1、  Server向Client发送Challenge,其中包括一个Challenge string(value字段)和Server Name(pptpd)

        2、  Client向Server发送Response,其中用户名使用明文发送,密码(syberos)和Challenge字段混合hash后以密文(value字段)形式发送

        3、  Server读取密码文件,对用户身份进行验证,验证成功,向Client发送Success,表示身份验证成功


    2.5  PPP协议的NCP协商


            NCP协议是PPP协议的网络控制协议,主要用来协商双方网络层接口参数,配置虚拟端口,分配IP,DNS等信息。图中的IPCP是NCP基于TCP/IP的接口协商协议。Server和Client都要把自己的Miniport信息发送给对方


    图2-9  PPP协议的NCP协商过程



    图2-10  PPP协议的NCP协商时序图


        1、  Server把自己的Miniport信息通过Configuration Request发送给Client

        2、  Client接收Server的接口配置,向Server发送Configuration ACK,应答上一步骤的Request

        3、  Client把自己的Miniport信息(无效数据,等待Server分配)通过Configuration Request发送给Server

        4、  Server发现Client的配置是无效的,则给Client发送一条有效的配置信息,使用Configuration Nak发送,其中主要是给Client分配ip

        5、  Client根据Server端发送过来的配置,修改自己的Miniport的接口,再次发送Configuration Request

        6、  Server接受Client的配置,发送Configuration Ack应答Request


    2.6  PPP协议的CCP协商


        CCP协议协商PPP通讯中数据加密的协议


    图2-11  PPP协议的NCP协商过程



    图2-12  PPP协议的NCP协商时序图


        1、  Server向Client发送Configuration Request,标识服务端支持的加密协议

        2、  Client向Server发送Configuration Request,标识客户端支持的加密协议

        3、  Client向Server发送Configuration ack,标识接受服务端的加密协议

        4、  Server向Client发送Configuration ack,标识接受客户端的加密协议


    三  PPTP数据连接的分析


        PPTP数据隧道化过程采用多层封装的方法,下图显示了封装后在网络上传输的数据包格式

    图3-1 PPTP数据包格式


        我们以应用层使用HTTP连接为例说明PPTP数据包的封装和解析过程;


    3.1  PPTP数据包封装过程



    图3-2 PPTP数据包封装过程



        封装过程:

        1、  应用层数据封装成IP数据包

        2、  将IP数据包发送到VPN的虚拟接口

        3、  VPN的虚拟接口将IP数据包压缩和加密,并增加PPP头

        4、  VPN的虚拟接口将PPP帧发送给PPTP协议驱动程序

        5、  PPTP协议驱动程序在PPP帧外添加GRE报头

        6、  PPTP协议驱动程序将GRE报头提交给TCP/IP协议驱动程序

        7、  TCP/IP协议驱动程序为GRE驱动添加IP头部

    8、  为IP数据包进行数据链路层封装后通过物理网卡发送出去

    3.2  PPTP数据包解析过程



    图3-2 PPTP数据包解析过程


        解析过程:

        1、物理thernet帧

        2、剥掉Ethernet帧后交给TCP/IP协议驱动程序

        3、TCP/IP协议解析剥掉IP头部

        4、IP协议解析剥掉GRE头部

        5、将PPP帧发送给VPN虚拟网卡

        6、VPN虚拟网卡剥掉PPP头并对PPP有效负载进行解密或者解压缩

        7、解密或者解压缩完成后将数据提交给上层应用

        8、上层应用对数据进行处理



              从应用层看,Service和Client是直接点对点连接的,他们之间的媒介就是GRE。而这个GRE的底层并不是真正的点对点连接,

    而是建立在物理网络上的一个隧道,保护传输的数据;



    pppd源码详解 http://www.xuebuyuan.com/1954248.html

    前言:

    PPP(Point to Point Protocol)协议是一种广泛使用的数据链路层协议,在国内广泛使用的宽带拨号协议PPPoE其基础就是PPP协议,此外和PPP相关的协议PPTP,L2TP也常应用于VPN虚拟专用网络。随着智能手机系统Android的兴起,PPP协议还被应用于GPRS拨号,3G/4G数据通路的建立,在嵌入式通信设备及智能手机中有着广泛的应用基础。本文主要分析Linux中PPP协议实现的关键代码和基本数据收发流程,对PPP协议的详细介绍请自行参考RFC和相关协议资料。

    模块组成:


    上图为PPP模块组成示意图,包括:

    PPPD:PPP用户态应用程序

    PPP驱动:PPP在内核中的驱动部分,kernel源码在/drivers/net/下的ppp_generic.c, slhc.c。

    PPP线路规程*:PPP TTY线路规程,kernel源码在/drivers/net/下的ppp_async.c, ppp_synctty.c,本文只考虑异步PPP。

    TTY核心:TTY驱动,线路规程的通用框架层。

    TTY驱动:串口TTY驱动,和具体硬件相关,本文不讨论。

    说明:本文引用的pppd源码来自于android 2.3源码包,kernel源码版本为linux-2.6.18。

    Linux中PPP实现主要分成两大部分:PPPD和PPPK。PPPD是用户态应用程序,负责PPP协议的具体配置,如MTU、拨号模式、认证方式、认证所需用户名/密码等。 PPPK指的是PPP内核部分,包括上图中的PPP驱动和PPP线路规程。PPPD通过PPP驱动提供的设备文件接口/dev/ppp来对PPPK进行管理控制,将用户需要的配置策略通过PPPK进行有效地实现,并且PPPD还会负责PPP协议从LCP到PAP/CHAP认证再到IPCP三个阶段协议建立和状态机的维护。因此,从Linux的设计思想来看,PPPD是策略而PPPK是机制;从数据收发流程看,所有控制帧(LCP,PAP/CHAP/EAP,IPCP/IPXCP等)都通过PPPD进行收发协商,而链路建立成功后的数据报文直接通过PPPK进行转发,如果把Linux当做通信平台,PPPD就是Control
    Plane而PPPK是DataPlane。

    在Linux中PPPD和PPPK联系非常紧密,虽然理论上也可以有其他的应用层程序调用PPPK提供的接口来实现PPP协议栈,但目前使用最广泛的还是PPPD。PPPD的源码比较复杂,支持众多类UNIX平台,里面包含TTY驱动,字符驱动,以太网驱动这三类主要驱动,以及混杂了TTY,PTY,Ethernet等各类接口,导致代码量大且难于理解,下文我们就抽丝剥茧将PPPD中的主干代码剥离出来,遇到某些重要的系统调用,我会详细分析其在Linux内核中的具体实现。

    源码分析:

    PPPD的主函数main:

    第一阶段:

    pppd/main.c -> main():

    ……

    new_phase(PHASE_INITIALIZE)//PPPD中的状态机,目前是初始化阶段

        /*

         * Initialize magic number generator now so that protocols may

         * use magic numbers in initialization.

         */

        magic_init();

     

        /*

         * Initialize each protocol.

         */

        for(i=0;(protp=protocols[i])!=
    NULL
    ;++i//protocols[]是全局变量的协议数组

            (*protp->init)(0)//初始化协议数组中所有协议

     

        /*

         * Initialize the default channel.

         */

        tty_init()//channel初始化,默认就是全局的tty_channel,里面包括很多TTY函数指针   

        if(!options_from_file(_PATH_SYSOPTIONS,!privileged,0,1)//解析/etc/ppp/options中的参数

           ||!options_from_user() 

           ||!parse_args(argc-1,argv+1)) //解析PPPD命令行参数

           exit(EXIT_OPTION_ERROR);

        devnam_fixed=1;       /*
    can no longer change device name */

     

        /*

         * Work out the device name, if it hasn't already been specified,

         * and parse the tty's options file.

         */

        if(the_channel->process_extra_options)

           (*the_channel->process_extra_options)()//实际上是调用tty_process_extra_options解析TTY
    参数

        if(!ppp_available())//检测/dev/ppp设备文件是否有效

           option_error("%s",no_ppp_msg);

           exit(EXIT_NO_KERNEL_SUPPORT);

        }

        /*

         * Check that the options given are valid and consistent.

         */

        check_options()//检查选项参数

        if(!sys_check_options()) //检测系统参数,比如内核是否支持Multilink等

           exit(EXIT_OPTION_ERROR);

        auth_check_options()//检查认证相关的参数

    #ifdef HAVE_MULTILINK

        mp_check_options();

    #endif

        for(i=0;(protp=protocols[i])!=
    NULL
    ;++i)

           if(protp->check_options!=
    NULL
    )

               (*protp->check_options)()//检查每个控制协议的参数配置 

        if(the_channel->check_options)

           (*the_channel->check_options)()//实际上是调用tty_check_options检测TTY参数

     

    ……

        /*

         * Detach ourselves from the terminal, if required,

         * and identify who is running us.

         */

        if(!nodetach&&!updetach

           detach()//默认放在后台以daemon执行,也可配置/etc/ppp/option中的nodetach参数放在前台执行

    ……

        syslog(LOG_NOTICE,"pppd %s started by %s,
    uid %d"
    ,VERSION,p,uid)//熟悉的log,现在准备执行了

        script_setenv("PPPLOGNAME",p,0);

     

        if(devnam[0])

           script_setenv("DEVICE",devnam,1);

        slprintf(numbuf,sizeof(numbuf),"%d",getpid());

        script_setenv("PPPD_PID",numbuf,1);

     

        setup_signals()//设置信号处理函数

     

        create_linkpidfile(getpid())//创建PID文件

     

        waiting=0;

     

        /*

         * If we're doing dial-on-demand, set up the interface now.

         */

        if(demand)//以按需拨号方式运行,可配置

           /*

            * Open the loopback channel and set it up to be the ppp interface.

            */

           fd_loop=open_ppp_loopback()//详见下面分析

           set_ifunit(1)//设置IFNAME环境变量为接口名称如ppp0

           /*

            * Configure the interface and mark it up, etc.

            */

           demand_conf();

    }

    (第二阶段)……

    PPP协议里包括各种控制协议如LCP,PAP,CHAP,IPCP等,这些控制协议都有很多共同的地方,因此PPPD将每个控制协议都用结构protent表示,并放在控制协议数组protocols[]中,一般常用的是LCP,PAP,CHAP,IPCP这四个协议。

    /*

     * PPP Data Link Layer "protocol" table.

     * One entry per supported protocol.

     * The last entry must be NULL.

     */

    struct protent*protocols[]={

        &lcp_protent//LCP协议

        &pap_protent//PAP协议

        &chap_protent//CHAP协议

    #ifdef CBCP_SUPPORT

        &cbcp_protent,

    #endif

        &ipcp_protent//IPCP协议,IPv4

    #ifdef INET6

        &ipv6cp_protent, //IPCP协议,IPv6

    #endif

        &ccp_protent,

        &ecp_protent,

    #ifdef IPX_CHANGE

        &ipxcp_protent,

    #endif

    #ifdef AT_CHANGE

        &atcp_protent,

    #endif

        &eap_protent,

        NULL

    };

    每个控制协议由protent结构来表示,此结构包含每个协议处理用到的函数指针:

    /*

     * The following struct gives the addresses of procedures to call

     * for a particular protocol.

     */

    struct protent{

        u_short protocol;            /* PPP protocol number */

        /* Initialization procedure */

        void(*init)__P((int
    unit
    ));  
    //初始化指针,在main()中被调用

        /* Process a received packet */

        void(*input)__P((int
    unit
    , u_char 
    *pkt,int len))//接收报文处理

        /* Process a received protocol-reject */

        void(*protrej)__P((int
    unit
    ));  
    //协议错误处理

        /* Lower layer has come up */

        void(*lowerup)__P((int
    unit
    ));  
    //当下层协议UP起来后的处理

        /* Lower layer has gone down */

        void(*lowerdown)__P((int
    unit
    ));  
    //当下层协议DOWN后的处理

        /* Open the protocol */

        void(*open)__P((int
    unit
    ));  
    //打开协议

        /* Close the protocol */

        void(*close)__P((int
    unit
    ,char*reason))//关闭协议

        /* Print a packet in readable form */

        int (*printpkt)__P((u_char*pkt,int
    len
    ,

                           
    void
    (*printer)__P((void*,char*,...)),

                           
    void
    *arg))//打印报文信息,调试用。

        /* Process a received data packet */

        void(*datainput)__P((int
    unit
    , u_char 
    *pkt,int len))//处理已收到的数据包

        boolenabled_flag;         /* 0 iff protocol is
    disabled */

        char*name;                  /*
    Text name of protocol */

        char*data_name;          /*
    Text name of corresponding data protocol */

        option_t*options;        /*
    List of command-line options */

        /* Check requested options, assign defaults */

        void(*check_options)__P((void))//检测和此协议有关的选项参数

        /* Configure interface for demand-dial */

        int (*demand_conf)__P((int
    unit
    ));  
    //将接口配置为按需拨号需要做的 动作

        /* Say whether to bring up link for this pkt */

        int (*active_pkt)__P((u_char*pkt,int
    len
    ))//判断报文类型并激活链路 

    };

    在main()函数中会调用所有支持的控制协议的初始化函数init(),之后初始化TTY channel,解析配置文件或命令行参数,接着检测内核是否支持PPP驱动:

    pppd/sys_linux.c

    main() -> ppp_avaiable():

    intppp_available(void)

    {

    ……

        no_ppp_msg=

           "This system lacks kernel support for PPP. This could be because\n"

           "the PPP kernel module could not be loaded, or because PPP was not\n"

           "included in the kernel configuration. If PPP was included as a\n"

           "module, try `/sbin/modprobe -v ppp'. If that fails, check that\n"

           "ppp.o exists in /lib/modules/`uname -r`/net.\n"

           "See README.linux file in the ppp distribution for more details.\n";

     

        /* get the kernel version now, since we are called before sys_init */

        uname(&utsname);

        osmaj=osmin=ospatch=0;

        sscanf(utsname.release,"%d.%d.%d",&osmaj,&osmin,&ospatch);

    kernel_version=KVERSION(osmaj,osmin,ospatch);

     

        fd=open("/dev/ppp",
    O_RDWR
    );

        if(fd>=0){

           new_style_driver=1//支持PPPK

     

           /* XXX should get from driver */

           driver_version=2;

           driver_modification=4;

           driver_patch=0;

           close(fd);

           return1;

    }

    ……

    }

    函数ppp_available会尝试打开/dev/ppp设备文件来判断PPP驱动是否已加载在内核中,如果此设备文件不能打开则通过uname判断内核版本号来区分当前内核版本是否支持PPP驱动,要是内核版本很老(2.3.x以下),则打开PTY设备文件并设置PPP线路规程。目前常用的内核版本基本上都是2.6以上,绝大多数情况下使用的内核都支持PPP驱动,因此本文不分析使用PTY的old driver部分。

    接下来会检查选项的合法性,这些选项可以来自于配置文件/etc/ppp/options,也可以是命令行参数,PPPD里面对选项的处理比较多,这里不一一分析了。

    后面是把PPPD以daemon方式执行或保持在前台运行并设置一些环境变量和信号处理函数,最后进入到第一个关键部分,当demand这个变量为1时,表示PPPD以按需拨号方式运行。

    什么是按需拨号呢?如果大家用过无线路由器就知道,一般PPPoE拨号配置页面都会有一个“按需拨号”的选项,若没有到外部网络的数据流,PPP链路就不会建立,当检测到有流量访问外部网络时,PPP就开始拨号和ISP的拨号服务器建立连接,拨号成功后才产生计费。反之,如果在一定时间内没有访问外网的流量,PPP就会断开连接,为用户节省流量费用。在宽带网络普及的今天,宽带费用基本上都是包月收费了,对家庭宽带用户此功能意义不大。不过对于3G/4G网络这种按流量收费的数据访问方式,按需拨号功能还是有其用武之地。

    PPP的按需拨号功能如何实现的呢?首先调用open_ppp_loopback:

    pppd/sys-linux.c

    main() -> open_ppp_loopback():

    int

    open_ppp_loopback(void)

    {

        intflags;

     

        looped=1//设置全局变量looped为1,后面会用到

        if(new_style_driver){

           /* allocate ourselves a ppp unit */

           if(make_ppp_unit()<0//创建PPP网络接口

               die(1);

           modify_flags(ppp_dev_fd,0,
    SC_LOOP_TRAFFIC
    )//通过ioctl设置SC_LOOP_TRAFFIC

           set_kdebugflag(kdebugflag);

           ppp_fd=-1;

           returnppp_dev_fd;

        }

     

    ……(下面是old driver,忽略)

    }

    全局变量new_style_driver,这个变量已经在ppp_avaliable函数里被设置为1了。接下来调用make_ppp_unit打开/dev/ppp设备文件并请求建立一个新的unit。

    pppd/sys-linux.c

    main() -> open_ppp_loopback() -> make_ppp_unit():

    staticintmake_ppp_unit()

    {

           intx,flags;

     

           if(ppp_dev_fd>=0)//如果已经打开过,先关闭

                  dbglog("in make_ppp_unit, already had /dev/ppp open?");

                  close(ppp_dev_fd);

           }

           ppp_dev_fd=open("/dev/ppp",
    O_RDWR
    );  
    //打开/dev/ppp

           if(ppp_dev_fd<0)

                  fatal("Couldn't open /dev/ppp: %m");

           flags=fcntl(ppp_dev_fd,
    F_GETFL
    );

           if(flags==-1

               ||fcntl(ppp_dev_fd,
    F_SETFL
    ,flags| O_NONBLOCK)==-1//设置为非阻塞

                  warn("Couldn't set /dev/ppp to nonblock: %m");

     

           ifunit=req_unit//传入请求的unit
    number,可通过/etc/ppp/options配置

           x=ioctl(ppp_dev_fd,
    PPPIOCNEWUNIT
    ,&ifunit)//请求建立一个新unit

           if(x<0&&req_unit>=0&&
    errno 
    == EEXIST){

                  warn("Couldn't allocate PPP unit %d as it is already in use",req_unit);

                  ifunit=-1;

                  x=ioctl(ppp_dev_fd,
    PPPIOCNEWUNIT
    ,&ifunit);

           }

           if(x<0)

                  error("Couldn't create new ppp unit: %m");

           returnx;

    }

    这里的unit可以理解为一个PPP接口,在Linux中通过ifconfig看到的ppp0就是通过ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit)建立起来的,unit number是可以配置的,不过一般都不用配置,传入-1会自动分配一个未使用的unit number,默认从0开始。这个ioctl调用的是PPPK中注册的ppp_ioctl:

     

    linux-2.6.18/drivers/net/ppp_generic.c

    main() -> open_ppp_loopback() -> make_ppp_unit() -> ioctl(ppp_dev_fd,PPPIOCNEWUNIT,&ifunit) -> ppp_ioctl():

    staticintppp_ioctl(struct
    inode 
    *inode,struct file*file,

                       
    unsigned
    intcmd,unsignedlongarg)

    {

           struct ppp_file*pf=file->private_data;

    ……

           if(pf==0)

                  returnppp_unattached_ioctl(pf,file,cmd,arg);

     

    TIPS:这里还要解释一下PPPK中channel和unit的关系,一个channel相当于一个物理链路,而unit相当于一个接口。在Multilink PPP中,一个unit可以由多个channel组合而成,也就是说一个PPP接口下面可以有多个物理链路,这里的物理链路不一定是物理接口,也可以是一个物理接口上的多个频段(channel)比如HDLC channel。

    PPPK中channel用结构channel表示,unit用结构ppp表示。

    linux-2.6.18/drivers/net/ppp_generic.c

    /*

     * Data structure describing one ppp unit.

     * A ppp unit corresponds to a ppp network interface device

     * and represents a multilink bundle.

     * It can have 0 or more ppp channels connected to it.

     */

    struct ppp{

           struct ppp_file     file;        /*
    stuff for read/write/poll 0 */

           struct file     *owner;        /*
    file that owns this unit 48 */

           struct list_headchannels;   /*
    list of attached channels 4c */

           int          n_channels;   /*
    how many channels are attached 54 */

           spinlock_t    rlock;            /*
    lock for receive side 58 */

           spinlock_t    wlock;           /*
    lock for transmit side 5c */

           int          mru;             /*
    max receive unit 60 */

           unsignedint   flags;            /*
    control bits 64 */

           unsignedint   xstate;          /*
    transmit state bits 68 */

           unsignedint   rstate;           /*
    receive state bits 6c */

           int          debug;          /*
    debug flags 70 */

           struct slcompress*vj;        /*
    state for VJ header compression */

           enumNPmode    npmode[NUM_NP];/*
    what to do with each net proto 78 */

           struct sk_buff      *xmit_pending;     /*
    a packet ready to go out 88 */

           struct compressor*xcomp;/*
    transmit packet compressor 8c */

           void       *xc_state;     /*
    its internal state 90 */

           struct compressor*rcomp; /*
    receive decompressor 94 */

           void       *rc_state;      /*
    its internal state 98 */

          


    展开全文
  • ceph Async模块提供一种异步收发消息的机制,最底层的异步机制可以查看博文 Ceph源码分析之Async模块:1、异步通信核心模块EventCenter+Epoll,上层的收发消息机制可以查看博文Ceph源码分析之Async模块:2、上层通信...
  • Floodlight模块分析:forwarding模块

    千次阅读 2016-05-17 15:55:36
    本文是通过分析一次ping过程来学习forwarding模块,从最简单的拓扑结构入手,研究报文在整个网络中的流向,并深入到forwarding模块的源码。选择forwarding模块进行分析好处在于:这个模块是floodlight应用模块,实现...
  • 常见路由协议类型:网络层次结构:TCP/IP协议网络拓扑企业网络设计基本流程网络设计基本原则网络拓扑设计原则网络设计的方法和思路网络架构安全域和边界:设计流程为什么要分析用户需求 认识网络 构建网络的目的...
  • 在传统的TCP/IP网络的路由器中,所有的IP数据包的传输都是采用FIFO(先进先出),尽最大努力传输的处理机制。在早期网络数据量和关键业务数据不多的时候,并没有体现出非常大的缺点,路由器简单的把数据报丢弃来处理...
  • Android xUtils3源码解析之网络模块

    千次阅读 2017-03-28 09:17:57
    本文已授权微信公众号《非著名程序员》原创首发,转载请务必注明出处。...xUtils3四大模块网络请求、图片加载、ORM框架和事件注解。本文阅读分析网络请求相关代码。 使用版本:`compile 'org.xutils:xutils:3.3.36'`
  • wireshark功能模块分析

    千次阅读 2013-03-06 10:49:00
    给出了wireshark功能模块: a) GTK1/2 处理用户的输入输出显示,源码在gtk目录. b) Core 核心模块,通过函数调用将其他模块连接在一起,源码在根目录 c) Epan wireshark Packetage Analyzing,包...
  • ZigBee协议分析

    千次阅读 2007-10-31 18:23:00
    ZigBee协议分析 <!--google_ad_client = "pub-9857128768944619";google_ad_width = 336;google_ad_height = 280;google_ad_format = "336x280_as";google_ad_type = "text_imag
  • 对于核心的sock的插入、查找函数都给出了流程图。 sock如何插入内核表  socket创建后就可以用来与外部网络通信,用户可以通过文件描述符fd来找到要操作的socket,内核则通过查表来找到要操作的socket。这意味着...
  • Wifi模块分析

    万次阅读 2011-07-21 14:25:21
    Wifi模块 最近研究Wifi模块,查了不少的相关资料,但发现基本上是基于android2.0版本的的分析,而现在研发的android移动平台基本上都是2.3的版本,跟2.0版本的差别,在Wifi模块上也是显而易见的。2.3版本Wifi模块...
  • 用户调用recvfrom()或recv()系统调用从队列中取出报文,这里的队列就是sk->sk_receive_queue,它是报文中转的纽带,两部分的联系如下所示。 第一部分:协议栈如何收取udp报文的。  udp模块的注册在inet_init...
  • 协议分析工具

    千次阅读 2010-12-21 15:56:00
    本文将介绍一种直观的学习方法,利用协议分析工具学习TCP/IP,在学习的过程中能直观的看到数据的具体传输过程。  为了初学者更容易理解,本文将搭建一个最简单的网络环境,不包含子网。 二、试验环境 ...
  • Linux内核网络协议栈代码分析

    千次阅读 2014-12-23 17:24:02
    一.linux内核网络栈代码的准备知识   1. linux内核ipv4网络部分分层结构:   BSD socket层: 这一部分处理BSD socket相关操作,每个socket在内核中以struct socket结构体现。这一部分的文件   主要有:/...
  • 则表示有多个协议模块要处理它,此时就需要使用 skb_clone() 来复制一份 skb ; kfree_skb() 并不一定释放 skb ,只有当 skb->users 为 1 时,才会释放;否则只是递减 skb->users 。 */  skb = skb_share_...
  • 提到网络协议栈,数据的收发是核心,我们先来看下数据的接收函数 netif_receive_skb()  先统一介绍下数据收发   对于linux内核来说,网络报文由网络设备来进行接收。设备驱动程序从网络设备中读取报文,...
  • MMS实现详细分析(BENQ M22模块)*--(1)---------MMS实现的流程分析基于MMS的终端系统中,MMS实现的步骤如下: (1)串口初始化和设置模块参数; (2)经过处理模块处理过的数据流在控制模块控制下,按照MMS协议封装...
  • android wifi模块分析

    万次阅读 2011-10-31 13:54:36
    声明:本文纯属网上资料收集,版权归源作者...最近研究Wifi模块,查了不少的相关资料,但发现基本上是基于android2.0版本的的分析,而现在研发的android移动平台基本上都是2.3的版本,跟2.0版本的差别,在Wifi模块
  • Liunx的启动流程模块管理与Loader

    千次阅读 2016-08-10 23:06:14
    启动流程模块管理与Loader 系统启动流程 1)加载BIOS的硬件信息与进行自我测试,并依据设置取得第一个可启动的设备; 2)读取并执行第一个启动设备内的MBR的boot Loader(即grub,spfdisk等程序); 3)依据...
  • 网络协议篇之SNMP协议(一)——SNMP报文协议

    万次阅读 多人点赞 2017-08-12 19:45:02
    前言:最近工作中遇到大量的网络协议开发,现就其中一些网络协议的基础知识进行整理,文中借鉴了一些大神的整理,后面会贴上链接,如侵删)
  • 本文先大致阐述系统协议栈初始化过程,然后...下网络协议栈初始化程序流程框架 本篇幅将根据上来介绍系统网络协议栈的初始化过程。 先从init/main.c 文件出发,在执行了一系列涉及到具体处理器架构初始化代码
  • 网络协议分层模型

    千次阅读 2019-09-13 11:57:23
    一、OSI网络分层模型 1、协议的分层 在OSI网络分层模型中,每个分层都接收由它下一层所提供的特定服务,并且负责为自己的上一层提供特定的服务。上下层之间进行交互时所遵循的约定叫做接口。同一层之间的交互所...
  • Hadoop RPC通信Client客户端的流程分析

    千次阅读 2014-12-05 18:47:36
    所以我打算分成Client客户端和Server服务端2个模块分析。如果你对RPC的整套流程已经非常了解的前提下,对于Hadoop的RPC,你也一定可以非常迅速的了解的。OK,下面切入正题。  Hadoop的RPC的相关代码
  • UDP协议下数据的传输分析

    万次阅读 2012-04-22 01:10:11
    首先我分析模块是 我项目中文件传输的部分,我做的是一个基于UDP协议的一个局域网通信软件,里面有一个文件传输的模块 ,起初的时候我也完成了文件传输的功能,以为这就可以了,其实我在做的时候忽略了很多细节部分,...
  • android phone 模块分析

    千次阅读 2010-08-30 09:32:00
    http://hi.baidu.com/anly%5Fjun/blog/index/0Andriod Phone模块相关(总览)2010-01-30 13:501、从java端发送at命令的处理流程。2、unsolicited 消息从modem上报到java的流程。3、猫相关的各种状态的监听和通知机制...
  • 华为等 3G、4G模块拨号上网过程分析

    万次阅读 2016-12-15 11:04:07
    最近在分析模块拨号上网的问题,mark下供研究... 一 开发环境简介 内核版本 3.19.5、3.15(这2个我都试过) 编译环境 Ubuntu 12.04 3g模块 华为MU709s SIM卡 联通 二 主要步骤 向linux内核中添加...
  • 其中虚线框的流程是可选部分,不会影响其他流程模块的功能使用。 初始化SDK(NET_DVR_Init):对整个网络SDK系统的初始化,内存预分配等操作。 用户注册设备(NET_DVR_Login_V40):实现用户的注册功能,注册...
  • 基于SET协议的电子支付系统模块设计

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 57,060
精华内容 22,824
关键字:

网络协议分析模块流程图