• 共享内存是最快的IPC方式。
    离国赤旅雷骑,以机动文明于世,以牺牲防御为代价,换来的是无以匹敌的速度,hiahiahiahia。

     原相关链接:

    Linux下进程通信的八种方法[连载-记1]:所有方法登场

    Linux下进程通信的八种方法[连载-记3]:青阳虎豹骑--内存映射和共享内存之内存映射

    九州十二骑兵

     

  • GTK+与MFC不完全对比

     

    转载时请注明出处:http://blog.csdn.net/absurd/

     

    MFC已经江河日下,日渐式微,而GTK+可谓欣欣向荣,如日中天。这里无意于落井下石,痛打落水狗,贬MFC而尊GTK+。自己即在使用MFC也在使用GTK+,不会偏袒其中之任何一方。这个对比完全出于个人对两者的理解,说它是不完全对比,一方面只是一时兴起想做个笔记而已,另外一方面我对两者的...
  • 我们手机平台的几个基础模型

     

  • Linux下文件关联的实现原理

     

  •  Linux支持共享库已经有悠久的历史了,不再是什么新概念了。大家都知道如何编译、连接以及动态加载(dlopen/dlsym/dlclose) 共享库。但...
  • 开发嵌入式软件通常是比较麻烦的事,一些常用的工具往往无法使用,在开发PC软件时简单的任务,此时变得很复杂。今天就遇到了这样一件事,折腾了几个小时,仅仅是为知道call stack。

    我 编译了一个程序放到PDA(ARM9+LINUX+UCLIBC)上面运行,出现了一个ASSERT,并显示了文件名和行号,原来是调用了一个没有实现的 函数,我很想知道是谁调用了它,这看似简单的问题却让我很头疼,如果有gdb,那好办-用bt命令就可以搞定,如果用的libc,那也好办-用 backtrace函数就可以搞定,问题是两者都没有。

    想来想去只有自己写一个backtrace,要实现这个功能并不难,如果我们知道调用堆栈的格式,就可以很容易取出上层调用者的指令地址,有了这些上层调用者的指令地址,我们可以通过MAP文件找到指令地址对应的源文件名和行号。
    ...
  • Linux下的调试工具

     

    随着
  • 负载平衡的网络转发技术

     

        随着互联网的迅速发展,应用服务器工作量的日益增加,对负载均衡技术的需求也就越来越多。而在众多的负载平衡技术中,网络负载平衡技术由于其优势,成为了目前使用最为广泛的技术,具体的产品也最为广泛:如F5的BIG-IP、RadWare的Web Server Director、IBM的WebSphere Edge Server、TriLoad负载平衡服务器等。

      目前主要有三种网络负载平衡转发技术,分别是网络地址转换、直接路由和IP隧道技术,采用不同的方法将客户端发送的包转发到目的服务器上,并确保目的服务器的返回包可以顺利到达客户端。

      ● 网络地址转换

      网络地址转换模式



      在目标网络地址转换(DNAT)模式下,提供服务的IP被定义在平衡服务器上,应用服务器只需要定义各自的内部IP地址,但是必须将负载平衡服务器定义为缺省路由,以保证返回客户端的包经过负载平衡服务器,完成第二次地址转换后再送回客户端。

      原理:

      1. 客户发出服务请求

      2. 负载平衡服务器接收到请求,将数据包中目的IP地址改为选中的应用服务器IP地址,然后重新发出数据包

      3. 应用服务器收到后,将应答包发回给负载平衡服务器

      4. 负载平衡服务器收到应答包后将其中的源地址改回成服务IP,发回客户端

      网络地址转换的优点是实际服务器可以运行支持TCP/IP协议的任意操作系统,实际服务器可以使用私有地址,只需在平衡服务器上配置服务IP地址。而且现在的防火墙都支持地址转换功能,因此,容易为用户接受。

      缺点是网络地址转换的性能扩展能力有限,因为请求包和应答包都必须通过平衡服务器,当服务器的节点数量达到20或更多时,平衡服务器可能成为整个系统的瓶颈。因此,这种方式主要适用于网络负载不是很高的场合。

      ● 直接路由

      和那种请求包和应答包都必须通过平衡服务器的网络地址转换不同,平衡服务器在直接路由模式下,将请求调度到不同的实际服务器,实际服务器直接将结果发回客户端。在大多数应用中,请求的字节数远小于应答的字节数,所以与网络地址转换相比平衡服务器能处理更多的请求。采用直接路由能更大程度提高平衡服务器的最大节点数和网络吞吐量。甚至于平衡服务器使用100M全双工的网卡,系统的最大数据吞吐量仍可以超过1Gbps。

      直接路由的特点是运用网络分层原理,通过将目标IP包封装在指定MAC地址的以太网数据包中欺骗TCP堆栈,因此实际服务器和负载平衡服务器必须在同一个物理网段中,并且在应用服务器上必须将服务IP定义在loopback虚拟网卡上。

      原理:

      1. 客户发出服务请求

      2. 负载平衡服务器接收到请求,将数据包中网卡物理地址(MAC)改为选中的应用服务器的MAC地址,然后重新发出数据包

      3. 目标应用服务器收到后,将应答包通过路由器直接发回客户端(不经过负载平衡服务器)

      直接路由是最高效,网络延时最小的负载平衡技术,但是,为达到MAC地址的欺骗,负载平衡服务器和所有应用服务器必须在同一个物理网段。而且,现在出现操作系统缺省是关闭这种特性的,如FreeBSD,必须显示打开相应的内核开关才可以。不过,目前的主流操作系统,都可以满足直接路由的需求,如 Windows、Linux、AIX、Solaris、FreeBSD等。



      直接路由转换模式

      ● IP隧道

      与直接路由的方法类似,区别仅仅在于不是通过MAC欺骗来转发数据包,而是通过建立负载平衡服务器和应用服务器之间的IP隧道来完成。

      因为这些服务器的连接是通过IP隧道,平衡服务器和实际服务器可以在不同的局域网甚至是广域网。缺点是所有服务器必须使用IP隧道(IP封装)协议,比直接路由的效率要低,而且,不是所有的操作系统都支持。

      在实际环境中,需要根据实际的情况来选择采用合适的转发技术的产品,甚至在某些场合,需要混合不同的转发方式。

  • Linux 用户态与内核态的交互
    ——netlink 篇


    作者:Kendo
    2006-9-3

    这是一篇学习笔记,主要是对《Linux 系统内核空间与用户空间通信的实现与分析》中的源码imp2的分析。其中的源码,可以到以下URL下载:
    http://www-128.ibm.com/developerworks/cn/linux/l-netlink/imp2.tar.gz

    参考文档
    《Linux 系统内核空间与用户空间通信的实现与分析》                陈鑫
    http://www-128.ibm.com/developerworks/cn/linux/l-netlink/?ca=dwcn-newsletter-linux
    《在 Linux 下用户空间与内核空间数据交换的方式》                杨燚
    http://www-128.ibm.com/developerworks/cn/linux/l-kerns-usrs/

    理论篇
            在 Linux 2.4 版以后版本的内核中,几乎全部的中断过程与用户态进程的通信都是使用 netlink 套接字实现的,例如iprote2网络管理工具,它与内核的交互就全部使用了netlink,著名的内核包过滤框架Netfilter在与用户空间的通读,也在最新版本中改变为netlink,无疑,它将是Linux用户态与内核态交流的主要方法之一。它的通信依据是一个对应于进程的标识,一般定为该进程的 ID。当通信的一端处于中断过程时,该标识为 0。当使用 netlink 套接字进行通信,通信的双方都是用户态进程,则使用方法类似于消息队列。但通信双方有一端是中断过程,使用方法则不同。netlink 套接字的最大特点是对中断过程的支持,它在内核空间接收用户空间数据时不再需要用户自行启动一个内核线程,而是通过另一个软中断调用用户事先指定的接收函数。工作原理如图:



    如图所示,这里使用了软中断而不是内核线程来接收数据,这样就可以保证数据接收的实时性。
    当 netlink 套接字用于内核空间与用户空间的通信时,在用户空间的创建方法和一般套接字使用类似,但内核空间的创建方法则不同,下图是 netlink 套接字实现此类通信时创建的过程:



    用户空间

    用户态应用使用标准的socket与内核通讯,标准的socket API 的函数, socket(), bind(), sendmsg(), recvmsg() 和 close()很容易地应用到 netlink socket。
    为了创建一个 netlink socket,用户需要使用如下参数调用 socket():

    socket(AF_NETLINK, SOCK_RAW, netlink_type)
    netlink对应的协议簇是 AF_NETLINK,第二个参数必须是SOCK_RAW或SOCK_DGRAM, 第三个参数指定netlink协议类型,它可以是一个自定义的类型,也可以使用内核预定义的类型:

    #define NETLINK_ROUTE          0       /* Routing/device hook                          */
    #define NETLINK_W1             1       /* 1-wire subsystem                             */
    #define NETLINK_USERSOCK       2       /* Reserved for user mode socket protocols      */
    #define NETLINK_FIREWALL       3       /* Firewalling hook                             */
    #define NETLINK_INET_DIAG      4       /* INET socket monitoring                       */
    #define NETLINK_NFLOG          5       /* netfilter/iptables ULOG */
    #define NETLINK_XFRM           6       /* ipsec */
    #define NETLINK_SELINUX        7       /* SELinux event notifications */
    #define NETLINK_ISCSI          8       /* Open-iSCSI */
    #define NETLINK_AUDIT          9       /* auditing */
    #define NETLINK_FIB_LOOKUP     10
    #define NETLINK_CONNECTOR      11
    #define NETLINK_NETFILTER      12      /* netfilter subsystem */
    #define NETLINK_IP6_FW         13
    #define NETLINK_DNRTMSG        14      /* DECnet routing messages */
    #define NETLINK_KOBJECT_UEVENT 15      /* Kernel messages to userspace */
    #define NETLINK_GENERIC        16

    同样地,socket函数返回的套接字,可以交给bing等函数调用:

    static int skfd;
    skfd = socket(PF_NETLINK, SOCK_RAW, NL_IMP2);
    if(skfd < 0)
    {
          printf("can not create a netlink socket\n");
          exit(0);
    }
    bind函数需要绑定协议地址,netlink的socket地址使用struct sockaddr_nl结构描述:

    struct sockaddr_nl
    {
      sa_family_t    nl_family;
      unsigned short nl_pad;
      __u32          nl_pid;
      __u32          nl_groups;
    };
    成员 nl_family为协议簇 AF_NETLINK,成员 nl_pad 当前没有使用,因此要总是设置为 0,成员 nl_pid 为接收或发送消息的进程的 ID,如果希望内核处理消息或多播消息,就把该字段设置为 0,否则设置为处理消息的进程 ID。成员 nl_groups 用于指定多播组,bind 函数用于把调用进程加入到该字段指定的多播组,如果设置为 0,表示调用者不加入任何多播组:

    struct sockaddr_nl local;

    memset(&local, 0, sizeof(local));
    local.nl_family = AF_NETLINK;
    local.nl_pid = getpid();                /*设置pid为自己的pid值*/
    local.nl_groups = 0;
    /*绑定套接字*/
    if(bind(skfd, (struct sockaddr*)&local, sizeof(local)) != 0)
    {
    printf("bind() error\n");
         return -1;
    }
    用户空间可以调用send函数簇向内核发送消息,如sendto、sendmsg等,同样地,也可以使用struct sockaddr_nl来描述一个对端地址,以待send函数来调用,与本地地址稍不同的是,因为对端为内核,所以nl_pid成员需要设置为0:

    struct sockaddr_nl kpeer;
    memset(&kpeer, 0, sizeof(kpeer));
    kpeer.nl_family = AF_NETLINK;
    kpeer.nl_pid = 0;
    kpeer.nl_groups = 0;
    另一个问题就是发内核发送的消息的组成,使用我们发送一个IP网络数据包的话,则数据包结构为“IP包头+IP数据”,同样地,netlink的消息结构是“netlink消息头部+数据”。Netlink消息头部使用struct nlmsghdr结构来描述:

    struct nlmsghdr
    {
      __u32 nlmsg_len;   /* Length of message */
      __u16 nlmsg_type;  /* Message type*/
      __u16 nlmsg_flags; /* Additional flags */
      __u32 nlmsg_seq;   /* Sequence number */
      __u32 nlmsg_pid;   /* Sending process PID */
    };
    字段 nlmsg_len 指定消息的总长度,包括紧跟该结构的数据部分长度以及该结构的大小,一般地,我们使用netlink提供的宏NLMSG_LENGTH来计算这个长度,仅需向NLMSG_LENGTH宏提供要发送的数据的长度,它会自动计算对齐后的总长度:

    /*计算包含报头的数据报长度*/
    #define NLMSG_LENGTH(len) ((len)+NLMSG_ALIGN(sizeof(struct nlmsghdr)))
    /*字节对齐*/
    #define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )
    后面还可以看到很多netlink提供的宏,这些宏可以为我们编写netlink宏提供很大的方便。

    字段 nlmsg_type 用于应用内部定义消息的类型,它对 netlink 内核实现是透明的,因此大部分情况下设置为 0,字段 nlmsg_flags 用于设置消息标志,对于一般的使用,用户把它设置为 0 就可以,只是一些高级应用(如 netfilter 和路由 daemon 需要它进行一些复杂的操作),字段 nlmsg_seq 和 nlmsg_pid 用于应用追踪消息,前者表示顺序号,后者为消息来源进程 ID。

    struct msg_to_kernel                /*自定义消息首部,它仅包含了netlink的消息首部*/
    {
      struct nlmsghdr hdr;
    };

    struct msg_to_kernel message;
    memset(&message, 0, sizeof(message));
    message.hdr.nlmsg_len = NLMSG_LENGTH(0);                /*计算消息,因为这里只是发送一个请求消息,没有多余的数据,所以,数据长度为0*/
    message.hdr.nlmsg_flags = 0;
    message.hdr.nlmsg_type = IMP2_U_PID;                        /*设置自定义消息类型*/
    message.hdr.nlmsg_pid = local.nl_pid;                /*设置发送者的PID*/

    这样,有了本地地址、对端地址和发送的数据,就可以调用发送函数将消息发送给内核了:
      /*发送一个请求*/
      sendto(skfd, &message, message.hdr.nlmsg_len, 0,
             (struct sockaddr*)&kpeer, sizeof(kpeer));
    当发送完请求后,就可以调用recv函数簇从内核接收数据了,接收到的数据包含了netlink消息首部和要传输的数据:

    /*接收的数据包含了netlink消息首部和自定义数据结构*/
    struct u_packet_info
    {
      struct nlmsghdr hdr;
      struct packet_info icmp_info;
    };
    struct u_packet_info info;
    while(1)
    {
        kpeerlen = sizeof(struct sockaddr_nl);
          /*接收内核空间返回的数据*/
          rcvlen = recvfrom(skfd, &info, sizeof(struct u_packet_info),
                            0, (struct sockaddr*)&kpeer, &kpeerlen);
                      
           /*处理接收到的数据*/
    ……
    }
    同样地,函数close用于关闭打开的netlink socket。程序中,因为程序一直循环接收处理内核的消息,需要收到用户的关闭信号才会退出,所以关闭套接字的工作放在了自定义的信号函数sig_int中处理:

    /*这个信号函数,处理一些程序退出时的动作*/
    static void sig_int(int signo)
    {
      struct sockaddr_nl kpeer;
      struct msg_to_kernel message;

      memset(&kpeer, 0, sizeof(kpeer));
      kpeer.nl_family = AF_NETLINK;
      kpeer.nl_pid    = 0;
      kpeer.nl_groups = 0;

      memset(&message, 0, sizeof(message));
      message.hdr.nlmsg_len = NLMSG_LENGTH(0);
      message.hdr.nlmsg_flags = 0;
      message.hdr.nlmsg_type = IMP2_CLOSE;
      message.hdr.nlmsg_pid = getpid();

      /*向内核发送一个消息,由nlmsg_type表明,应用程序将关闭*/
      sendto(skfd, &message, message.hdr.nlmsg_len, 0, (struct sockaddr *)(&kpeer),         sizeof(kpeer));

      close(skfd);
      exit(0);
    }
    这个结束函数中,向内核发送一个“我已经退出了”的消息,然后调用close函数关闭netlink套接字,退出程序。

    内核空间

    与应用程序内核,内核空间也主要完成三件工作:
    n        创建netlink套接字
    n        接收处理用户空间发送的数据
    n        发送数据至用户空间

    API函数netlink_kernel_create用于创建一个netlink socket,同时,注册一个回调函数,用于接收处理用户空间的消息:

    struct sock *
    netlink_kernel_create(int unit, void (*input)(struct sock *sk, int len));
    参数unit表示netlink协议类型,如NL_IMP2,参数input则为内核模块定义的netlink消息处理函数,当有消息到达这个 netlink socket时,该input函数指针就会被引用。函数指针input的参数sk实际上就是函数netlink_kernel_create返回的 struct sock指针,sock实际是socket的一个内核表示数据结构,用户态应用创建的socket在内核中也会有一个struct sock结构来表示。

    static int __init init(void)
    {
      rwlock_init(&user_proc.lock);                /*初始化读写锁*/

      /*创建一个netlink socket,协议类型是自定义的ML_IMP2,kernel_reveive为接受处理函数*/
      nlfd = netlink_kernel_create(NL_IMP2, kernel_receive);
      if(!nlfd)                /*创建失败*/
      {
          printk("can not create a netlink socket\n");
          return -1;
      }

      /*注册一个Netfilter 钩子*/
      return nf_register_hook(&imp2_ops);
    }


    module_init(init);
    用户空间向内核发送了两种自定义消息类型:IMP2_U_PID和IMP2_CLOSE,分别是请求和关闭。kernel_receive 函数分别处理这两种消息:

    DECLARE_MUTEX(receive_sem);                                                        /*初始化信号量*/
    static void kernel_receive(struct sock *sk, int len)
    {
            do
        {
                    struct sk_buff *skb;
                    if(down_trylock(&receive_sem))                                /*获取信号量*/
                            return;
                    /*从接收队列中取得skb,然后进行一些基本的长度的合法性校验*/
                    while((skb = skb_dequeue(&sk->receive_queue)) != NULL)
            {
                            {
                                    struct nlmsghdr *nlh = NULL;
                                   
                                    if(skb->len >= sizeof(struct nlmsghdr))
                                    {
                                            /*获取数据中的nlmsghdr 结构的报头*/
                                            nlh = (struct nlmsghdr *)skb->data;
                                            if((nlh->nlmsg_len >= sizeof(struct nlmsghdr))
                                                    && (skb->len >= nlh->nlmsg_len))
                                            {
                                                    /*长度的全法性校验完成后,处理应用程序自定义消息类型,主要是对用户PID的保存,即为内核保存“把消息发送给谁”*/
                                                    if(nlh->nlmsg_type == IMP2_U_PID)                /*请求*/
                                                    {
                                                            write_lock_bh(&user_proc.pid);
                                                            user_proc.pid = nlh->nlmsg_pid;
                                                            write_unlock_bh(&user_proc.pid);
                                                    }
                                                    else if(nlh->nlmsg_type == IMP2_CLOSE)        /*应用程序关闭*/
                                                    {
                                                            write_lock_bh(&user_proc.pid);
                                                            if(nlh->nlmsg_pid == user_proc.pid)
                                                                    user_proc.pid = 0;
                                                            write_unlock_bh(&user_proc.pid);
                                                    }
                                            }
                                    }
                            }
                            kfree_skb(skb);
            }
                    up(&receive_sem);                                /*返回信号量*/
        }while(nlfd && nlfd->receive_queue.qlen);
    }
    因为内核模块可能同时被多个进程同时调用,所以函数中使用了信号量和锁来进行互斥。skb = skb_dequeue(&sk->receive_queue)用于取得socket sk的接收队列上的消息,返回为一个struct sk_buff的结构,skb->data指向实际的netlink消息。

    程序中注册了一个Netfilter钩子,钩子函数是get_icmp,它截获ICMP数据包,然后调用send_to_user函数将数据发送给应用空间进程。发送的数据是info结构变量,它是struct packet_info结构,这个结构包含了来源/目的地址两个成员。Netfilter Hook不是本文描述的重点,略过。
    send_to_user 用于将数据发送给用户空间进程,发送调用的是API函数netlink_unicast 完成的:

    int netlink_unicast(struct sock *sk, struct sk_buff *skb, u32 pid, int nonblock);
    参数sk为函数netlink_kernel_create()返回的套接字,参数skb存放待发送的消息,它的data字段指向要发送的netlink消息结构,而skb的控制块保存了消息的地址信息,参数pid为接收消息进程的pid,参数nonblock表示该函数是否为非阻塞,如果为1,该函数将在没有接收缓存可利用时立即返回,而如果为0,该函数在没有接收缓存可利用时睡眠。
    向用户空间进程发送的消息包含三个部份:netlink 消息头部、数据部份和控制字段,控制字段包含了内核发送netlink消息时,需要设置的目标地址与源地址,内核中消息是通过sk_buff来管理的, linux/netlink.h中定义了NETLINK_CB宏来方便消息的地址设置:

    #define NETLINK_CB(skb)         (*(struct netlink_skb_parms*)&((skb)->cb))
    例如:

    NETLINK_CB(skb).pid = 0;
    NETLINK_CB(skb).dst_pid = 0;
    NETLINK_CB(skb).dst_group = 1;
    字段pid表示消息发送者进程ID,也即源地址,对于内核,它为 0, dst_pid 表示消息接收者进程 ID,也即目标地址,如果目标为组或内核,它设置为 0,否则 dst_group 表示目标组地址,如果它目标为某一进程或内核,dst_group 应当设置为 0。

    static int send_to_user(struct packet_info *info)
    {
    int ret;
    int size;
    unsigned char *old_tail;
    struct sk_buff *skb;
    struct nlmsghdr *nlh;
    struct packet_info *packet;

    /*计算消息总长:消息首部加上数据加度*/
    size = NLMSG_SPACE(sizeof(*info));

    /*分配一个新的套接字缓存*/
    skb = alloc_skb(size, GFP_ATOMIC);
    old_tail = skb->tail;

    /*初始化一个netlink消息首部*/
    nlh = NLMSG_PUT(skb, 0, 0, IMP2_K_MSG, size-sizeof(*nlh));
    /*跳过消息首部,指向数据区*/
    packet = NLMSG_DATA(nlh);
    /*初始化数据区*/
    memset(packet, 0, sizeof(struct packet_info));
    /*填充待发送的数据*/
    packet->src = info->src;
    packet->dest = info->dest;

    /*计算skb两次长度之差,即netlink的长度总和*/
    nlh->nlmsg_len = skb->tail - old_tail;
    /*设置控制字段*/
    NETLINK_CB(skb).dst_groups = 0;

    /*发送数据*/
    read_lock_bh(&user_proc.lock);
    ret = netlink_unicast(nlfd, skb, user_proc.pid, MSG_DONTWAIT);
    read_unlock_bh(&user_proc.lock);


    }
    函数初始化netlink 消息首部,填充数据区,然后设置控制字段,这三部份都包含在skb_buff中,最后调用netlink_unicast函数把数据发送出去。
    函数中调用了netlink的一个重要的宏NLMSG_PUT,它用于初始化netlink 消息首部:

    #define NLMSG_PUT(skb, pid, seq, type, len) \
    ({ if (skb_tailroom(skb) < (int)NLMSG_SPACE(len)) goto nlmsg_failure; \
       __nlmsg_put(skb, pid, seq, type, len); })
    static __inline__ struct nlmsghdr *
    __nlmsg_put(struct sk_buff *skb, u32 pid, u32 seq, int type, int len)
    {
            struct nlmsghdr *nlh;
            int size = NLMSG_LENGTH(len);

            nlh = (struct nlmsghdr*)skb_put(skb, NLMSG_ALIGN(size));
            nlh->nlmsg_type = type;
            nlh->nlmsg_len = size;
            nlh->nlmsg_flags = 0;
            nlh->nlmsg_pid = pid;
            nlh->nlmsg_seq = seq;
            return nlh;
    }
    这个宏一个需要注意的地方是调用了nlmsg_failure标签,所以在程序中应该定义这个标签。

    在内核中使用函数sock_release来释放函数netlink_kernel_create()创建的netlink socket:

    void sock_release(struct socket * sock);
    程序在退出模块中释放netlink sockets和netfilter hook:

    static void __exit fini(void)
    {
      if(nlfd)
        {
          sock_release(nlfd->socket);                /*释放netlink socket*/
        }
      nf_unregister_hook(&imp2_ops);                /*撤锁netfilter 钩子*/
    }




    __________________________________

    netlink相关收藏地址:

    http://lzueclipse.bokee.com/4851988.html

    http://www.ilib.cn/Abstract.aspx?A=wjsjxx200609033

  • 1. 系统目标
    基于脚本语言的协议分析器,根据用户的协议脚本,对相应的协议进行解析和审计。协议分析析器能够解析出2~7层协议,脚本语言采用基于位操作的方式进行,并生成相应的协议模块。对于协议解析工作中的非法操作,根据用户的审计脚本发出入侵事件并给予审计。
    2. 系统功能
    1) 协议解释器:解释7层协议,根据协议脚本生成相应的协议解析模块。
    2) 协议分析器:引用协议解析模块进行协议解析据非法操作等可疑事件进行审计。
    3) 事件触发器: 对网络事件进行统计和审计。
    4) 协议模拟器:应用协议解释器的解析模块进行应用协议模拟。
    其中最典型是基于电力系统协议的分析和审计,用户只要熟悉协议解析语言就能够实时监控电力系统协议。对于一些非法行为或者越权行为(非法用户IP,非法协议操作命令)可采用协议分析语言进行安全审计和告警。安全告警如果支持链路割断则系统就是一个动态的入侵防护系统。
    用户只要熟悉某种特殊的网络协议就可以采用协议解析脚本对协议进行安全审计或者安全监控,系统支持协议跟踪,便于入侵的反演。
     
    3. 系统开发
    1) 脚本解释器是核心
    脚本语言的设计可以参考Etherpeek SDK 和 Bro,利用 lex 和yacc 进行脚本解释器编写。
    2) 基于特征值和异常行为的IDS
    针对基于端口的协议分析目前已经无法真正完成应用解析,知名端口被相应的协议利用,非知名端口却被知名协议利用。Dynamic Protocol Analysis for Network Intrusion Detection Systems 和Dynamic Application-Layer Protocol Analysis for Network Intrusion Detection 两偏文章对基于特征值的协议分析给了很好建议。其中都利用了L7-FILTER的特征值。
       基于行为的异常检测目前基本上通过网络流量进行。目前这方面资料  很多,但实时性很少,学术性很多。这方面实现的深度可有研究的程序决定。
    3) 协议模拟器
    根据协议脚本,用户可自行组织网络报文进行发送。
    4. 前景分析
    1) 应用审计系统
    2) 网络审计系统
    3) 协议分析仪
    5. 网络资源
    1) 
    http://www.icir.org/vern/bro-info.html
    Bro是一个Vern Paxson实现的实时网络入侵检测软件,于98年对外发布,BSD license,它的最初设计目标是实现一个在100M网络下实时告警、机制与策略分离、高可扩展性的入侵检测及网络监视审计系统。
    Bro的系统结构如下图示:
                 
     Bro的设计实现遵循分层的原则,利用libpcap从网络上获取的数据包经过事件生成引擎和规则引擎被抽象成一系列的事件,这些事件被策略脚本做进一步的深入分析,基本事件本身就可以触发告警,策略脚本分析基本事件以后既可以生成新的事件也可以触发告警
    2) 
    http://l7-filter.sourceforge.net/
    l7-filter是一个Linux内核Netfilter子系统的协议分类器,它可以识别应用层(OSI layer 7)的数据包。所以l7-filter可以识别如HTTP, FTP, Gnucleus, eDonkey2000等各种协议的数据包,从而进行过滤等操作。
    l7-filter支持众多的应用协议,具体协议请参考:
    http://l7-filter.sourceforge.net/protocols
    3) http:// dinosaur.compilertools.net/
    Lex和 Yacc 是 UNIX 两个非常重要的、功能强大的工具。事实上,如果你熟练掌握 Lex 和 Yacc 的话,它们的强大功能使创建 FORTRAN 和 C 的编译器如同儿戏。Ashish Bansal 为您详细的讨论了编写自己的语言和编译器所用到的这两种工具,包括常规表达式、声明、匹配模式、变量、Yacc 语法和解析器代码。最后,他解释了怎样把 Lex 和 Yacc 结合起来。
    4) http://www.wildpackets.com/
    EtherPeek包括一个Plug-in SDK,所以任何一个有程序开发经验的人都能在程序中加入自己所写的plug-in来扩充或增加EtherPeek的功能。
    5)