当前位置: 首页 >> 程序设计 >> Linux网络接口源码导读
 

Linux网络接口源码导读

作者:bekars 的 Blog      来源:     发表时间:2006-03-22     浏览次数:      字号:    




网络接口源码的结构(二)

Linux最新稳定内核2.4.x的网络接口源码的结构(二)
李元佳

  如果你在Linux启动的时候有留意启动的信息, 或者在linux下打命令dmesg就可以看到这一段程序输出的信息:
  IP Protocols: ICMP,UDP,TCP,IGMP也就是说现在数组inet_protos[]里面有了ICMP,UDP,TCP,IGMP四个协议的inet_protocol数据结构,数据结构包含了它们接收数据的处理函数。
  Linux 2.4.16在linux/include/linux/socket.h里定义了32种支持的BSDsocket协议,常见的有TCP/IP,IPX/SPX,X.25等,而每种协议还提供不同的服务,例如TCP/IP协议通过TCP协议支持连接服务,而通过UDP协议支持无连接服务,面对这么多的协议,向用户提供统一的接口是必要的,这种统一是通过socket来进行的。
  在BSD socket网络编程的模式下,利用一系列的统一的函数来利用通信的服务。例如一个典型的利用TCP协议通信程序是这样:
  sock_descriptor = socket(AF_INET,SOCK_STREAM,0);
  connect(sock_descriptor, 地址,) ;
  send(sock_descriptor,”hello world”);
  recv(sock_descriptor,buffer,1024,0);
  第一个函数指定了协议Inet协议,即TCP/IP协议,同时是利用面向连接的服务,这样就对应到TCP协议,以后的操作就是利用socket的标准函数进行的。
  从上面我们可以看到两个问题,首先socket层需要根据用户指定的协议族(上面是AF_INET) 从下面32种协议中选择一种协议来完成用户的要求,当协议族确定以后,还要把特定的服务映射到协议族下的具体协议,例如当用户指定的是面向连接的服务时,Inet协议族会映射到TCP协议。
  从多个协议中选择用户指定的协议,并把具体的出理交给选中的协议,这和一起网络核心层向上和向下衔接的问题本质上是一样的,所以解决的方法也是一样的,同样还是通过数组。在Linux/net/socket.c定义了这个数组staticstruct net_proto_family*net_families[NPROTO] 。数组的元素已经确定了,net_families[2]是TCP/IP协议,net_families[3]是X.25协议,具体那一项对应什么协议,在include/linux/socket.h有定义。但是每一项的数据结构net_proto_family的ops是空的,也就是具体协议处理函数的地址是不知道的。协议的处理函数和ops建立联系是通过sock_register()(Linux/net/socket.c)这个函数建立的,例如TCP/IP协议的是这样建立关系的:
  int __init inet_init(void) (net/ipv4/af_inet.c)
  {
   (void) sock_register(&inet_family_ops);
  }
  只要给出AF_INET(在宏里定义是2),就可以找到net_failies[2] 里面的处理函数了。
  协议的映射完成了,现在要进行服务的映射了。上层当然不可能知道下层的什么协议能对应特定的服务,所以这种映射自然由协议族自己完成。在TCP/IP协议族里,这种映射是通过struct list_head inetsw[SOCK_MAX]( net/ipv4/af_inet.c)
  这个数组进行映射的,在谈论这个数组之前我们来看另外一个数组inetsw_array[](net/ipv4/af_inet.c)
  static struct inet_protosw inetsw_array[] =
  {
  {
  type: SOCK_STREAM,
  protocol: IPPROTO_TCP,
  prot: &tcp_prot,
  ops: &inet_stream_ops,
  capability: -1,
  no_check: 0,
  flags: INET_PROTOSW_PERMANENT,
  },
  {
  type: SOCK_DGRAM,
  protocol: IPPROTO_UDP,
  prot: &udp_prot,
  ops: &inet_dgram_ops,
  capability: -1,
  no_check: UDP_CSUM_DEFAULT,
  flags: INET_PROTOSW_PERMANENT,
  },
  {
  type: SOCK_RAW,
  protocol: IPPROTO_IP, /* wild card */
  prot: &raw_prot,
  ops: &inet_dgram_ops,
  capability: CAP_NET_RAW,
  no_check: UDP_CSUM_DEFAULT,
  flags: INET_PROTOSW_REUSE,
  }
  };
  我们看到,SOCK_STREAM映射到了TCP协议,SOCK_DGRAM映射到了UDP协议,SOCK_RAW映射到了IP协议。现在只要把inetsw_array里的三项添加到数组inetsw[SOCK_MAX]就可以了,添加是通过函数inet_register_protosw()实现的。在inet_init()(net/ipv4/af_inet.c) 里完成了这些工作。
  还有一个需要映射的就是socket其它诸如accept,send(), connect(),release(),bind()等的操作函数是怎么映射的呢?我们来看一下上面的数组的TCP的项
  {
  type: SOCK_STREAM,
  protocol: IPPROTO_TCP,
  prot: &tcp_prot,
  ops: &inet_stream_ops,
  capability: -1,
  no_check: 0,
  flags: INET_PROTOSW_PERMANENT,
  },
  我们看到这种映射是通过ops,和prot来映射的,我们再来看看 tcp_prot这一项:

  struct proto tcp_prot = {
  name: "TCP",
  close: tcp_close,
  connect: tcp_v4_connect,
  disconnect: tcp_disconnect,
  accept: tcp_accept,
  ioctl: tcp_ioctl,
  init: tcp_v4_init_sock,
  destroy: tcp_v4_destroy_sock,
  shutdown: tcp_shutdown,
  setsockopt: tcp_setsockopt,
  getsockopt: tcp_getsockopt,
  sendmsg: tcp_sendmsg,
  recvmsg: tcp_recvmsg,
  backlog_rcv: tcp_v4_do_rcv,
  hash: tcp_v4_hash,
  unhash: tcp_unhash,
  get_port: tcp_v4_get_port,
  };
  所以的映射都已经完成了,用户调用connect()函数,其实就是调用了tcp_v4_connect()函数,按照这幅图,读起源码来就简单了很多了。

六 Socket层
  上一节把socket层大多数要讨论的东西都谈论了,现在只讲讲socket 层和用户的衔接。
  系统调用socket(),bind(),connect(),accept,send(),release()等是在Linux/net/socket.c里面的实现的,系统调用实现的函数是相应的函数名加上sys_的前缀。
  现在看看当用户调用socket()这个函数,到底下面发生了什么。
Socket(AF_INET,SOCK_STREAM,0)调用了sys_socket(),sys_socket()接着调用socket_creat(),socket_creat()就要根据用户提供的协议族参数在net_families[]里寻找合适的协议族,如果协议族没有被安装就要请求安装该协议族的模块,然后就调用该协议族的create()函数的处理句柄。根据参数AF_INET,inet_creat()就被调用了,在inet_creat()根据服务类型在inetsw[SOCK_MAX]选择合适的协议,并把协议的操作集赋给socket就是了,根据SOCK_STREAM,TCP协议被选中,
  inet_creat(){
  answer=inetsw [用户要求服务服务] ;
  sock->ops = answer->ops;
  sk->prot = answer->prot
  }
  到此为止,上下都打通了,该是大家读源码的时候了。



防火墙



netfilter

标题: A new place to LKM:netfilter
作者: yawl <yawl@nsfocus.com>
时间: 2000-10

目录:
-.前言
二.分析
三.例子代码
四.附录:与2.2在应用方面的区别简介
五.后记

-.前言

  在linux2.2内核中的防火墙ipchains已经被用户广泛认可,它提供了完整的防火墙功能(包过滤,地址伪装,透明代理),又避免了商业防火墙那高的惊人的价格。如果你用的是某款国产防火墙,那么十有八九你实际在受到ipchains(有些甚至是2.0系列中ipfwadm)的保护:-).在未来的2.4内核中,被称为netfilter(http://netfilter.kernelnotes.org/)的防火墙以更好的结构重新构造,并实现了许多新功能,如完整的动态NAT(2.2内核实际是多对一的"地址伪装"),基于MAC及用户的过滤,真正的基于状态的过滤(不再是简单的查看tcp的标志位等),包速率限制等。
  在原有的网络部分的LKM中,如果对网络部分进行处理,一般是先生成struct packet_type结构,在用dev_add_pack将其插入网络层(注意此时的packet_type实际相当于一个的三层的协议,如ip_packet_type,ipx_8023_packet_type等),具体的例子可参见phrack 55期<Building into the linux network layer>和本月小四写的月刊文章<利用LLKM处理网络通信----对抗IDS、Firewall>。
  而netfilter本身在IP层内提供了另外的5个插入点(其文档中称为HOOK):NF_IP_PRE_ROUTING,NF_IP_LOCAL_IN,NF_IP_FORWARD,NF_IP_LOCAL_OUT,NF_IP_POST_ROUTING,分别对应IP层的五个不同位置,这样理论上在写lkm时便可以选择更适合的切入点,再辅以netfilter内置的新功能(如connect tracking),应该会帮助写出功能更强的lkm。
  本来准备写出一个完整的例子(限制IP连接数),但计划总赶不上变化:-(,只好先贴出个简单的例子来,权且自我安慰成抛砖引玉了。
  本文的参考配置是linux2.4.0-test4和iptable-1.1.1,好,开始抛砖,闪人喽!


二.分析
  通俗的说,netfilter的架构就是在整个网络流程的若干位置放置了一些检测点(HOOK),而在每个检测点上上登记了一些处理函数进行处理(如包过滤,NAT等,甚至可以是用户自定义的功能)。
  IP层的五个HOOK点的位置如下图所示(copy from <packet filter howto>) :

          --->[1]--->[ROUTE]--->[3]--->[5]--->
                        |            ^
                        |            |
                        |         [ROUTE]
                        v            |
                       [2]          [4]
                        |            ^
                        |            |
                        v            |
                       [local process]

[1]:NF_IP_PRE_ROUTING:刚刚进入网络层的数据包通过此点(刚刚进行完版本号,校验和等检测),源地址转换在此点
进行;
[2]:NF_IP_LOCAL_IN:经路由查找后,送往本机的通过此检查点,INPUT包过滤在此点进行;
[3]:NF_IP_FORWARD:要转发的包通过此检测点,FORWORD包过滤在此点进行;
[4]:NF_IP_LOCAL_OUT:本机进程发出的包通过此检测点,OUTPUT包过滤在此点进行;
[5]:NF_IP_POST_ROUTING:所有马上便要通过网络设备出去的包通过此检测点,内置的目的地址转换功能(包括地址伪
装)在此点进行。

  在IP层代码中,有一些带有NF_HOOK宏的语句,如IP的转发函数中有:

<-ipforward.c ip_forward()->
NF_HOOK(PF_INET, NF_IP_FORWARD, skb, skb->dev, dev2,
                       ip_forward_finish);

  其中NF_HOOK宏的定义提炼如下:

<-/include/linux/netfilter.h->
#ifdef CONFIG_NETFILTER
#define NF_HOOK(pf, hook, skb, indev, outdev, okfn)                        \
(list_empty(&nf_hooks[(pf)][(hook)])                                        \
? (okfn)(skb)                                                                \
: nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn)))
#else /* !CONFIG_NETFILTER */
#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (okfn)(skb)
#endif /*CONFIG_NETFILTER*/

  如果在编译内核时没有配置netfilter时,就相当于调用最后一个参数,此例中即执行ip_forward_finish函数;否则进入HOOK点,执行通过nf_register_hook()登记的功能(这句话表达的可能比较含糊,实际是进入
nf_hook_slow()函数,再由它执行登记的函数)。

  NF_HOOK宏的参数分别为:
1.pf:协议族名,netfilter架构同样可以用于IP层之外,因此这个变量还可以有诸如PF_INET6,PF_DECnet等名字。
2.hook:HOOK点的名字,对于IP层,就是取上面的五个值;
3.skb:不用多解释了吧;
4.indev:进来的设备,以struct net_device结构表示;
5.outdev:出去的设备,以struct net_device结构表示;
(后面可以看到,以上五个参数将传到用nf_register_hook登记的处理函数中。)
6.okfn:是个函数指针,当所有的该HOOK点的所有登记函数调用完后,转而走此流程。

  这些点是已经在内核中定义好的,除非你是这部分内核代码的维护者,否则无权增加或修改,而在此检测点进行的处理,则可由用户指定。像packet filter,NAT,connection track这些功能,也是以这种方式提供的。正如netfilter的当初的设计目标--提供一个完善灵活的框架,为扩展功能提供方便。

  如果我们想加入自己的代码,便要用nf_register_hook函数,其函数原型为:
  int nf_register_hook(struct nf_hook_ops *reg)
  我们考察一下struct nf_hook_ops结构:

struct nf_hook_ops
{
        struct list_head list;

        /* User fills in from here down. */
        nf_hookfn *hook;
        int pf;
        int hooknum;
        /* Hooks are ordered in ascending priority. */
        int priority;
};

  我们的工作便是生成一个struct nf_hook_ops结构的实例,并用nf_register_hook将其HOOK上。其中list项我们总要初始化为{NULL,NULL};由于一般在IP层工作,pf总是PF_INET;hooknum就是我们选择的HOOK点;一个HOOK点可能挂多个处理函数,谁先谁后,便要看优先级,即priority的指定了。netfilter_ipv4.h中用一个枚举类型指定了内置的处理函数的优先级:

enum nf_ip_hook_priorities {
        NF_IP_PRI_FIRST = INT_MIN,
        NF_IP_PRI_CONNTRACK = -200,
        NF_IP_PRI_MANGLE = -150,
        NF_IP_PRI_NAT_DST = -100,
        NF_IP_PRI_FILTER = 0,
        NF_IP_PRI_NAT_SRC = 100,
        NF_IP_PRI_LAST = INT_MAX,
};

  hook是提供的处理函数,也就是我们的主要工作,其原型为:

unsigned int nf_hookfn(unsigned int hooknum,
                               struct sk_buff **skb,
                               const struct net_device *in,
                               const struct net_device *out,
                               int (*okfn)(struct sk_buff *));

  它的五个参数将由NFHOOK宏传进去。
  了解了这些,基本上便可以可以写一个lkm出来了。

三.例子代码

这段代码是一个例子,其功能实现了一个IDS,检测几个简单攻击(land,winnuke)和特殊扫描(nmap),当然,不会有人真把
它当严肃的IDS使用吧:-)。可以利用类似结构干点别的。。。

<-example.c begin->

/*
*  netfilter module example: it`s a kernel IDS(be quie,donot laugh, my friend)
*  yawl@nsfocus.com
*  Compile:gcc -O -c -Wall sample.c ,under linux2.4 kernel,netfilter is needed.
*/

#define __KERNEL__
#define MODULE

#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/config.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/netfilter_ipv4.h>

#define ALERT(fmt,args...) printk("nsfocus: " fmt, ##args)
/*message will be print to screen(too many~),and logged to /var/log/message*/

static unsigned int sample(unsigned int hooknum,struct sk_buff **skb,
                                const struct net_device *in,
                                const struct net_device *out,int (*okfn)(struct sk_buff *))
{
        struct iphdr *iph;
        struct tcphdr *tcph;
        struct udphdr *udph;

        __u32        sip;
        __u32        dip;
        __u16        sport;
        __u16        dport;

        iph=(*skb)->nh.iph;
        sip=iph->saddr;
        dip=iph->daddr;

        /*play ip packet here
        (note:checksum has been checked,if connection track is enabled,defrag have been done )*/
        if(iph->ihl!=5){
                ALERT("IP packet with packet from %d.%d.%d.%d to %d.%d.%d.%d\n",NIPQUAD(sip),NIPQUAD(dip));
        }

        if(iph->protocol==6){
                tcph=(struct tcphdr*)((__u32 *)iph+iph->ihl);
                sport=tcph->source;
                dport=tcph->dest;
                /*play tcp packet here*/
                if((tcph->syn)&&(sport==dport)&&(sip==dip)){
                        ALERT("maybe land attack\n");
                }
                if(ntohs(tcph->dest)==139&&tcph->urg){
                        ALERT("maybe winnuke a from %d.%d.%d.%d to %d.%d.%d.%d\n",NIPQUAD(sip),NIPQUAD(dip));
                }
                if(tcph->ece&&tcph->cwr){
                        ALERT("queso from %d.%d.%d.%d to %d.%d.%d.%d\n",NIPQUAD(sip),NIPQUAD(dip));
                }
                   if((tcph->fin)&&(tcph->syn)&&(!tcph->rst)&&(!tcph->psh)&&(!tcph->ack)&&(!tcph->urg)){
                        ALERT("SF_scan from %d.%d.%d.%d to %d.%d.%d.%d\n",NIPQUAD(sip),NIPQUAD(dip));
                }
                   if((!tcph->fin)&&(!tcph->syn)&&(!tcph->rst)&&(!tcph->psh)&&(!tcph->ack)&&(!tcph->urg)){
                        ALERT("NULL_scan from %d.%d.%d.%d to %d.%d.%d.%d\n",NIPQUAD(sip),NIPQUAD(dip));
                }
                   if(tcph->fin&&tcph->syn&&tcph->rst&&tcph->psh&&tcph->ack&&tcph->urg){
                        ALERT("FULL_Xmas_scan from %d.%d.%d.%d to %d.%d.%d.%d\n",NIPQUAD(sip),NIPQUAD(dip));
                }
                   if((tcph->fin)&&(!tcph->syn)&&(!tcph->rst)&&(tcph->psh)&&(!tcph->ack)&&(tcph->urg)){
                        ALERT("XMAS_Scan(FPU)from %d.%d.%d.%d to %d.%d.%d.%d\n",NIPQUAD(sip),NIPQUAD(dip));
                }
        }

        else if(iph->protocol==17){
                udph=(struct udphdr *)((__u32 *)iph+iph->ihl);
                sport=udph->source;
                dport=udph->dest;
                /*play udp packet here*/
        }

        else if(iph->protocol==1){
                /*play icmp packet here*/
        }

        else if(iph->protocol==2){
                ALERT("igmp packet from %d.%d.%d.%d to %d.%d.%d.%d\n",NIPQUAD(sip),NIPQUAD(dip));
                /*play igmp packet here*/
        }

        else{
                ALERT("unknown protocol%d packet from %d.%d.%d.%d to %d.%d.%d.%d\n",iph->protocol,NIPQUAD(sip),NIPQUAD(dip));
        }
        return NF_ACCEPT;
        /*for it is IDS,we just accept all packet,
        if you really want to drop this skb,just return NF_DROP*/

}

static struct nf_hook_ops iplimitfilter
={ {NULL,NULL} ,sample,PF_INET,NF_IP_PRE_ROUTING,NF_IP_PRI_FILTER-1};

int init_module(void)
{
        return nf_register_hook(&iplimitfilter);
}

void cleanup_module(void)
{
        nf_unregister_hook(&iplimitfilter);
}

<-example.c end->

四.附录:与2.2在应用方面的区别简介

  本来还想详细介绍一下iptables的用法,但如果说的太详细的话,还不如索性将HOWTO翻译一下,于是干脆了却了这个念头,只顺便简介一下与以前版本的变化(而且是我认为最重要的)。如果ipchains本来便没有在你的脑子中扎根,其实便没有必要看这部分。
  netfilter,又可称为iptables.开发初期准备将packet filter和NAT的配置工具完全分开,一个称为iptables,另一个称为ipnatctl,而将整个项目成为netfilter.但后来可能是还是习惯2.2内核中用ipchians一个工具干两件事的用法,又改为全部用iptables配置了。
  理论上还可以用2.2系列的ipchains和2.0系列的ipfwadm作配置工具,但只是做兼容或过渡的考虑了。通过源码看到他们也是通过现有的结构HOOK上去的(主要是net/ipv4/netfilter目录下的ip_fw_compat.c,ip_fw_compat_masq.c,
ip_fw_compat_redir.c,ipchains_core.c,ipfwadm_core.c这几个文件)。
  一个重要的变化是原有的INPUT,OUTPUT(原来是小写的input,ouput)链(现在应称为表?)的位置变了,原来的input,output的位置相当于现在的NF_IP_PRE_ROUTING,NF_IP_POST_ROUTING 。原有的结构确实不太合理,转发的包要经过三条链,现在INPUT专指去往本机的,OUPUT专指从本机发出的,而FOWARD仍旧是转发的包。
  举两个简单的例子:
  1.作地址伪装(场景:对外通过拨号连接internet)注意原来的MASQ变成好长的MASQUERATE,而伪装相当于SNAT,因此位置是在POSTROUTING:

  iptables -t nat -A POSTROUTING -o ppp0 -j MASQUERATE

  2.还有一个限制包速率的功能比较好玩,例如如下规则:

  iptables -A FORWARD -p tcp --syn -m limit --limit 1/s -j ACCEPT

  简单的说就是在转发时(-A FORWARD:因为是防火墙嘛),如果是tcp协议,且有syn标记(-p tcp --syn),可以限制为每秒一个(-m limit --limit 1/s ),行动项是ACCEPT。最后连起来意义就是每秒只允许转发一个tcp连接请求。


五.后记
  netfilter还提供了许多新功能,如可以将包转发到应用层,由应用程序进行处理等,可目前我还没有分析多少,慢慢抽出点时间看吧。唉,尽管以前看过ipchains的代码,但netfilter实在变动太大了,一切都要从头看起:-(
  最后,当然要感谢Rusty Russell,netfilter项目的负责人,不仅为我们提供了这个强大好用的工具,还写了大量非常优秀的文档。


参考文献:

[1.] Linux 2.4 Packet Filtering HOWTO
        Rusty Russell, mailing list netfilter@lists.samba.org
          v1.0.1 Mon May  1 18:09:31 CST 2000
[2.] Linux IPCHAINS-HOWTO
          Paul Russell, ipchains@rustcorp.com
          v1.0.7, Fri Mar 12 13:46:20 CST 1999
[3.] Linux 2.4 NAT HOWTO
          Rusty Russell, mailing list netfilter@lists.samba.org
          v1.0.1 Mon May  1 18:38:22 CST 2000
[4.] Linux netfilter Hacking HOWTO
          Rusty Russell, mailing list netfilter@lists.samba.org
          v1.0.1 Sat Jul  1 18:24:41 EST 2000
[5.] Writing a Module for netfilter
        by Paul "Rusty" Russell
        Linux Magazine June 2000 http://www.linux-mag.com/2000-06/gear_01.html
[6.] Salvatore Sanfilippo<antirez@invece.org>写的一份netfilter sample,但可惜我找不到出处了,只剩下手头一份打印稿,But anyway,thanks to Salvatore.



ip/tables

  1 /*
  2  * 25-Jul-1998 Major changes to allow for ip chain table
  3  *
  4  * 3-Jan-2000 Named tables to allow packet selection for different uses.
  5  */
  6
  7 /*
  8  *      Format of an IP firewall descriptor
  9  *

注意这里的说明:IP地址和掩码是按照网络字节存储(大端存储)标志字节和端口号是按照主机字节序存储(依主机硬件结构而定)

10  *      src, dst, src_mask, dst_mask are always stored in network byte order.
11  *      flags are stored in host byte order (of course).
12  *      Port numbers are stored in HOST byte order.
13  */
14
15 #ifndef _IPTABLES_H
16 #define _IPTABLES_H
17
18 #ifdef __KERNEL__
19 #include linux/if.h
20 #include linux/types.h
21 #include linux/in.h
22 #include linux/ip.h
23 #include linux/skbuff.h
24 #endif
25 #include linux/netfilter_ipv4.h
26
27 #define IPT_FUNCTION_MAXNAMELEN 30
28 #define IPT_TABLE_MAXNAMELEN 32
29
这个结构存储与IP头部有关的防火墙规则信息。这里的注释说“这个结构无须填充零字节”,就是说这个结构的大小正好是4的倍数。这里由于IFNAMSIZ等于16,所以整个结构大小确实是4的倍数。
30 /* Yes, Virginia, you have to zero the padding. */
31 struct ipt_ip {
32         /* Source and destination IP addr */
33         struct in_addr src, dst;
34         /* Mask for src and dest IP addr */
35         struct in_addr smsk, dmsk;
36         char iniface[IFNAMSIZ], outiface[IFNAMSIZ];
37         unsigned char iniface_mask[IFNAMSIZ], outiface_mask[IFNAMSIZ];
38
39         /* Protocol, 0 = ANY */
40         u_int16_t proto;
41
42         /* Flags word */
43         u_int8_t flags;
44         /* Inverse flags */
45         u_int8_t invflags;
46 };
47这个结构存储match的信息,这里的匹配主要是指与IP无关的防火墙规则信息。由系统缺省设置的匹配主要有三个“tcp”、“udp”,“icmp”,我在分析ip_tables.c时将详细描述。
48 struct ipt_entry_match
49 {
50         union {
51                 struct {
52                         u_int16_t match_size;
53
54                         /* Used by userspace */
55                         char name[IPT_FUNCTION_MAXNAMELEN];
56                 } user;
57                 struct {
58                         u_int16_t match_size;
59
60                         /* Used inside the kernel */
61                         struct ipt_match *match;
62                 } kernel;
63
64                 /* Total length */
65                 u_int16_t match_size;
66         } u;
67
68         unsigned char data[0];
69 };
70 target结构信息,是决定一个分组命运的信息。也可以理解为action信息,其意义是指当一个分组与rule和match信息匹配后,如何处置该分组。处置方法一般有三种:一,命令常数,比如DROP ACCEPT等等;二 系统预定义的模块处理函数,比如”SNAT DNAT"等等;第三种是用户自己写模块函数。
71 struct ipt_entry_target
72 {
73         union {
74                 struct {
75                         u_int16_t target_size;
76
77                         /* Used by userspace */
78                         char name[IPT_FUNCTION_MAXNAMELEN];
79                 } user;
80                 struct {
81                         u_int16_t target_size;
82
83                         /* Used inside the kernel */
84                         struct ipt_target *target;
85                 } kernel;
86
87                 /* Total length */
88                 u_int16_t target_size;
89         } u;
90
91         unsigned char data[0];
92 };
93这个结构已经很明显给出了target的形式:命令常数、或者模块函数。
94 struct ipt_standard_target
95 {
96         struct ipt_entry_target target;
97         int verdict;
98 };
99
计数器结构,每一个rule都有一个计数器结构用来统计匹配该条规则的分组数目和字节数目。为基于统计的安全工具提供分析基础。
100 struct ipt_counters
101 {
102         u_int64_t pcnt, bcnt;                   /* Packet and byte counters */
103 };
104
标志字段,各个常数后面的注释已经给出了明确的解释,这里不再赘述。
105 /* Values for "flag" field in struct ipt_ip (general ip structure). */
106 #define IPT_F_FRAG              0x01    /* Set if rule is a fragment rule */
107 #define IPT_F_MASK              0x01    /* All possible flag bits mask. */
108
109 /* Values for "inv" field in struct ipt_ip. */
110 #define IPT_INV_VIA_IN          0x01    /* Invert the sense of IN IFACE. */
111 #define IPT_INV_VIA_OUT         0x02    /* Invert the sense of OUT IFACE */
112 #define IPT_INV_TOS             0x04    /* Invert the sense of TOS. */
113 #define IPT_INV_SRCIP           0x08    /* Invert the sense of SRC IP. */
114 #define IPT_INV_DSTIP           0x10    /* Invert the sense of DST OP. */
115 #define IPT_INV_FRAG            0x20    /* Invert the sense of FRAG. */
116 #define IPT_INV_PROTO           0x40    /* Invert the sense of PROTO. */
掩码标志。用法是当出现超出掩码范围的标志时,确认是非法标志。
117 #define IPT_INV_MASK            0x7F    /* All possible flag bits mask. */
118
其实这个结构的构成这里的注释已经说的很清楚,但是从论坛上有人问"关于netfilter的问题“时,可以看出很多人还是不理解。与前面ipchains版本防火墙不同的是iptables的防火墙规则构成发生了变化。ipchains的构成是rule+target,而iptables的构成是ip匹配信息+match+target。同时iptables构成的每一个部分都是可变大小的,由于经常出现”char XXX[0]“就可以看出。但是我个人认为规则的组织有点不好理解,它经常是先分配一段空间,然后将规则一条一条放入。如同文件系统存放变长记录的文件时,总要在记录中放入记录长度,以便以后取出记录,这里iptables正是使用这种方法,在每个规则中都放入长度字段,这样方便提取各个组成部分和计算下一条规则的位置。
119 /* This structure defines each of the firewall rules.  Consists of 3
120    parts which are 1) general IP header stuff 2) match specific
121    stuff 3) the target to perform if the rule matches */
122 struct ipt_entry
123 {
124         struct ipt_ip ip;
125
126         /* Mark with fields that we care about. */
127         unsigned int nfcache;
128下面两个字段用来计算target的位置和下一条规则的位置。
129         /* Size of ipt_entry + matches */
130         u_int16_t target_offset;
131         /* Size of ipt_entry + matches + target */
132         u_int16_t next_offset;
133
这个字段的存在,为发现规则中存在”环路“提供手段。
134         /* Back pointer */
135         unsigned int comefrom;
136
137         /* Packet and byte counters. */
138         struct ipt_counters counters;
139
140         /* The matches (if any), then the target. */
141         unsigned char elems[0];
142 };
143
144 /*
145  * New IP firewall options for [gs]etsockopt at the RAW IP level.
146  * Unlike BSD Linux inherits IP options so you don't have to use a raw
147  * socket for this. Instead we check rights in the calls. */
定义提供给set/getsockopt系统调用的命令常数的基常数。
148 #define IPT_BASE_CTL            64      /* base for firewall socket options */
149
150 #define IPT_SO_SET_REPLACE      (IPT_BASE_CTL)
151 #define IPT_SO_SET_ADD_COUNTERS (IPT_BASE_CTL + 1)
152 #define IPT_SO_SET_MAX          IPT_SO_SET_ADD_COUNTERS
153
154 #define IPT_SO_GET_INFO         (IPT_BASE_CTL)
155 #define IPT_SO_GET_ENTRIES      (IPT_BASE_CTL + 1)
156 #define IPT_SO_GET_MAX          IPT_SO_GET_ENTRIES
157
158 /* CONTINUE verdict for targets */
159 #define IPT_CONTINUE 0xFFFFFFFF
160
161 /* For standard target */
162 #define IPT_RETURN (-NF_MAX_VERDICT - 1)
163
Tcp匹配规则信息。
164 /* TCP matching stuff */
165 struct ipt_tcp
166 {
167         u_int16_t spts[2];                      /* Source port range. */
168         u_int16_t dpts[2];                      /* Destination port range. */
169         u_int8_t option;                        /* TCP Option iff non-zero*/
170         u_int8_t flg_mask;                      /* TCP flags mask byte */
171         u_int8_t flg_cmp;                       /* TCP flags compare byte */
172         u_int8_t invflags;                      /* Inverse flags */
173 };
174
tcp的取反标志值。
175 /* Values for "inv" field in struct ipt_tcp. */
176 #define IPT_TCP_INV_SRCPT       0x01    /* Invert the sense of source ports. */
177 #define IPT_TCP_INV_DSTPT       0x02    /* Invert the sense of dest ports. */
178 #define IPT_TCP_INV_FLAGS       0x04    /* Invert the sense of TCP flags. */
179 #define IPT_TCP_INV_OPTION      0x08    /* Invert the sense of option test. */
180 #define IPT_TCP_INV_MASK        0x0F    /* All possible flags. */
181
udp匹配规则信息
182 /* UDP matching stuff */
183 struct ipt_udp
184 {
185         u_int16_t spts[2];                      /* Source port range. */
186         u_int16_t dpts[2];                      /* Destination port range. */
187         u_int8_t invflags;                      /* Inverse flags */
188 };
189
190 /* Values for "invflags" field in struct ipt_udp. */
191 #define IPT_UDP_INV_SRCPT       0x01    /* Invert the sense of source ports. */
192 #define IPT_UDP_INV_DSTPT       0x02    /* Invert the sense of dest ports. */
193 #define IPT_UDP_INV_MASK        0x03    /* All possible flags. */
194
195 /* ICMP matching stuff */
ICMP匹配规则信息
196 struct ipt_icmp
197 {
198         u_int8_t type;                          /* type to match */
199         u_int8_t code[2];                       /* range of code */
200         u_int8_t invflags;                      /* Inverse flags */
201 };
202
203 /* Values for "inv" field for struct ipt_icmp. */
204 #define IPT_ICMP_INV    0x01    /* Invert the sense of type/code test */
205
这个结构实质上用户通过getsockopt系统调用获取table信息时所传递参数的类型。
206 /* The argument to IPT_SO_GET_INFO */
207 struct ipt_getinfo
208 {
209         /* Which table: caller fills this in. */
210         char name[IPT_TABLE_MAXNAMELEN];
211
212         /* Kernel fills these in. */
213         /* Which hook entry points are valid: bitmask */
214         unsigned int valid_hooks;
215
216         /* Hook entry points: one per netfilter hook. */
217         unsigned int hook_entry[NF_IP_NUMHOOKS];
218
219         /* Underflow points. */
220         unsigned int underflow[NF_IP_NUMHOOKS];
221
222         /* Number of entries */
223         unsigned int num_entries;
224
225         /* Size of entries. */
226         unsigned int size;
227 };
228
这个结构是用户通过系统调用更换table是所传递的参数类型。
229 /* The argument to IPT_SO_SET_REPLACE. */
230 struct ipt_replace
231 {
232         /* Which table. */
233         char name[IPT_TABLE_MAXNAMELEN];
234
235         /* Which hook entry points are valid: bitmask.  You can't
236            change this. */
237         unsigned int valid_hooks;
238
239         /* Number of entries */
240         unsigned int num_entries;
241
242         /* Total size of new entries */
243         unsigned int size;
244
245         /* Hook entry points. */
246         unsigned int hook_entry[NF_IP_NUMHOOKS];
247
248         /* Underflow points. */
249         unsigned int underflow[NF_IP_NUMHOOKS];
250
251         /* Information about old entries: */
252         /* Number of counters (must be equal to current number of entries). */
253         unsigned int num_counters;
254         /* The old entries' counters. */
255         struct ipt_counters *counters;
256
257         /* The entries (hang off end: not really an array). */
258         struct ipt_entry entries[0];
259 };
260
这个更改计数器时传递的参数类型。
261 /* The argument to IPT_SO_ADD_COUNTERS. */
262 struct ipt_counters_info
263 {
264         /* Which table. */
265         char name[IPT_TABLE_MAXNAMELEN];
266
267         unsigned int num_counters;
268
269         /* The counters (actually `number' of these). */
270         struct ipt_counters counters[0];
271 };
272
这个是想获取防火墙规则时,传递给系统调用的参数类型。
273 /* The argument to IPT_SO_GET_ENTRIES. */
274 struct ipt_get_entries
275 {
276         /* Which table: user fills this in. */
277         char name[IPT_TABLE_MAXNAMELEN];
278
279         /* User fills this in: total entry size. */
280         unsigned int size;
281
282         /* The entries. */
283         struct ipt_entry entrytable[0];
284 };
285
286 /* Standard return verdict, or do jump. */
287 #define IPT_STANDARD_TARGET ""
288 /* Error verdict. */
289 #define IPT_ERROR_TARGET "ERROR"
290
现面定义了一些使用例程
291 /* Helper functions */
获取一条防火墙规则的target位置
292 extern __inline__ struct ipt_entry_target *
293 ipt_get_target(struct ipt_entry *e)
294 {
295         return (void *)e + e->target_offset;
296 }
297
下面的宏遍历处理一条防火墙规则的所有匹配。我已经说过每一条防火墙规则在iptables中分为三部分,而且每一部分的大小都是可变的。比如match部分,它本身可以有多个match项。
298 /* fn returns 0 to continue iteration */
299 #define IPT_MATCH_ITERATE(e, fn, args...)       \
300 ({                                              \
301         unsigned int __i;                       \
302         int __ret = 0;                          \
303         struct ipt_entry_match *__m;            \

这个for语句我来解释一下:首先__i取值为ipt_entry结构的大小,实质上就是match匹配的开始处的偏移地址,将其与e相加就得到了match匹配的地址,然后调用fn处理这个匹配。如果函数返回值为零,当前匹配的偏移地址加上当前匹配的大小,如果不超过target的偏移地址,则继续处理下一条匹配。
304                                                 \
305         for (__i = sizeof(struct ipt_entry);    \
306              __i target_offset;          \
307              __i += __m->u.match_size) {        \
308                 __m = (void *)(e) + __i;        \
309                                                 \
310                 __ret = fn(__m , ## args);      \
311                 if (__ret != 0)                 \
312                         break;                  \
313         }                                       \
314         __ret;                                  \
315 })
316
这个宏处理一个table中的所有防火墙规则。对比对上一个宏的理解,这里我就不解释了。
317 /* fn returns 0 to continue iteration */
318 #define IPT_ENTRY_ITERATE(entries, size, fn, args...)           \
319 ({                                                              \
320         unsigned int __i;                                       \
321         int __ret = 0;                                          \
322         struct ipt_entry *__e;                                  \
323                                                                 \
324         for (__i = 0; __i next_offset) {  \
325                 __e = (void *)(entries) + __i;                  \
326                                                                 \
327                 __ret = fn(__e , ## args);                      \
328                 if (__ret != 0)                                 \
329                         break;                                  \
330         }                                                       \
331         __ret;                                                  \
332 })
333
334 /*
335  *      Main firewall chains definitions and global var's definitions.
336  */
337 #ifdef __KERNEL__
338
339 #include ·
340 extern void ipt_init(void) __init;
341
所有的匹配处理都注册到一个match处理链表中,链表结点的类型就是这里的结构类型。当处理匹配时都是调用这里注册的处理函数。每个结点实质上由三个函数构成,一个匹配处理函数,一个合法性检查函数,一个析构函数。最后一个是反身指针,指针的作用如注释所示。
342 struct ipt_match
343 {
344         struct list_head list;
345
346         const char name[IPT_FUNCTION_MAXNAMELEN];
347
348         /* Return true or false: return FALSE and set *hotdrop = 1 to
349            force immediate packet drop. */
350         int (*match)(const struct sk_buff *skb,
351                      const struct net_device *in,
352                      const struct net_device *out,
353                      const void *matchinfo,
354                      int offset,
355                      const void *hdr,
356                      u_int16_t datalen,
357                      int *hotdrop);
358
359         /* Called when user tries to insert an entry of this type. */
360         /* Should return true or false. */
361         int (*checkentry)(const char *tablename,
362                           const struct ipt_ip *ip,
363                           void *matchinfo,
364                           unsigned int matchinfosize,
365                           unsigned int hook_mask);
366
367         /* Called when entry of this type deleted. */
368         void (*destroy)(void *matchinfo, unsigned int matchinfosize);
369
370         /* Set this to THIS_MODULE if you are a module, otherwise NULL */
371         struct module *me;
372 };
373
和match一样,所有的target都注册到这个结构类型的全局链表中,每个target的处理函数都是这里注册的函数。和上面的解释一样,这里也主要包含三个函数指针。
374 /* Registration hooks for targets. */
375 struct ipt_target
376 {
377         struct list_head list;
378
379         const char name[IPT_FUNCTION_MAXNAMELEN];
380
381         /* Returns verdict. */
382         unsigned int (*target)(struct sk_buff **pskb,
383                                unsigned int hooknum,
384                                const struct net_device *in,
385                                const struct net_device *out,
386                                const void *targinfo,
387                                void *userdata);
388
389         /* Called when user tries to insert an entry of this type:
390            hook_mask is a bitmask of hooks from which it can be
391            called. */
392         /* Should return true or false. */
393         int (*checkentry)(const char *tablename,
394                           const struct ipt_entry *e,
395                           void *targinfo,
396                           unsigned int targinfosize,
397                           unsigned int hook_mask);
398
399         /* Called when entry of this type deleted. */
400         void (*destroy)(void *targinfo, unsigned int targinfosize);
401
402         /* Set this to THIS_MODULE if you are a module, otherwise NULL */
403         struct module *me;
404 };
405
注册函数
406 extern int ipt_register_target(struct ipt_target *target);
407 extern void ipt_unregister_target(struct ipt_target *target);
408
409 extern int ipt_register_match(struct ipt_match *match);
410 extern void ipt_unregister_match(struct ipt_match *match);
411
table结构
412 /* Furniture shopping... */
413 struct ipt_table
414 {
415         struct list_head list;
416
417         /* A unique name... */
418         char name[IPT_TABLE_MAXNAMELEN];
419
420         /* Seed table: copied in register_table */
421         struct ipt_replace *table;
422
423         /* What hooks you will enter on */
424         unsigned int valid_hooks;
425
426         /* Lock for the curtain */
427         rwlock_t lock;
428
429         /* Man behind the curtain... */
430         struct ipt_table_info *private;
431 };
432
小结iptables的防火墙组织结构:

现在我们可以给出iptables防火墙规则的组织结构了。第一级是table级,每一个防火墙可以有多个table;第二级是hook级,每一个table都有一个hook集合,每个hook都有一个防火墙规则链;第三级基本规则级,基本规则级的规则包括三部分,IP规则信息、匹配规则信息和target。而这三个组成部分的每一个都可以包括同类型的多个部分规则。
433 extern int ipt_register_table(struct ipt_table *table);
434 extern void ipt_unregister_table(struct ipt_table *table);
435 extern unsigned int ipt_do_table(struct sk_buff **pskb,
436                                  unsigned int hook,
437                                  const struct net_device *in,
438                                  const struct net_device *out,
439                                  struct ipt_table *table,
440                                  void *userdata);
441
有关table结构对齐的宏。

442 #define IPT_ALIGN(s) (((s) + (__alignof__(struct ipt_entry)-1))  ~(__alignof__(struct ipt_entry)-1))
443 #endif /*__KERNEL__*/
444 #endif /* _IPTABLES_H */
445

[1] [2] [3] [4]

编辑 webmaster

 
 
 
评论更多>>
 
 
发表
 
姓名: QQ:
性别: MSN:
E-mail: 主页:
评分: 1 2 3 4 5
评论内容:
验证码:
  
  • 请遵守《互联网电子公告服务管理规定》及中华人民共和国其他各项有关法律法规。
  • 严禁发表危害国家安全、损害国家利益、破坏民族团结、破坏国家宗教政策、破坏社会稳定、侮辱、诽谤、教唆、淫秽等内容的评论 。
  • 用户需对自己在使用本站服务过程中的行为承担法律责任(直接或间接导致的)。
  • 本站管理员有权保留或删除评论内容。
  • 评论内容只代表网友个人观点,与本网站立场无关。
  •