`
C_J
  • 浏览: 124691 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Linux个人防火墙JAVA版实现(Netfilter+Netlink+Multi-Thre) in 2009

阅读更多
@Author: cjcj  cj.yangjun@gmail.com <c-j.iteye.com>


程序demo:
毕业设计会放到 http://code.google.com上,有兴趣一起完善的兄弟联系我哈!!

NAT实现

进度:
  5.13
  潦草地完成NAT模块,暂时收工。





   完成netlink的通讯,以及修改pool池数据结构的bug.完成状态存储,查询匹配功能。
 


  5.7凌晨
   完成内核态初步钩子函数。匹配ip,tcp,udp信息


   内核的数据结构很讲究共性和特性。这个我还没感知到。
   目前暂时完成用于存储的数据结构和固定大小队列,其实应该用hashtable的。结构体如下:
struct object_table
{
	__be32	saddr;
	__be32	daddr;
	__be16	source;
	__be16	dest;
	int status;
	//static int MAXNUM=0;
	struct object_table *next;
	struct object_table *prv;//为了便于从尾部查询

}

概念:
流程:

eth0(public ip 10.10.10.1):
DNAT(PRE_ROUTING):
   接受外网的数据包。saddr(202.202.202.2) daddr(10.10.10.1) 需要查询匹配(ip+port),查询202.202.202.2+port。
      匹配成功,则根据查询结果修改daddr为192.168.0.2+port(查询从尾部开始)
      匹配不成功,则无须更改或者丢弃
   接受内网的数据包。saddr(192.168.0.2) daddr(202.202.202.2)
SNAT(POST_ROUTING):
   发送数据包给外网。saddr(192.168.0.2) daddr(202.202.202.2)需要存储状态,存储192.168.0.2+port,202.202.202.2+port,并修改saddr为10.10.10.1。(为了提高效率,不用查询后插入)
   发送数据包给内网。saddr(202.202.202.2) daddr(192.168.0.2)

eth1(private ip 192.168.0.1):(所有的数据包都转发给eht0处理)
eth3(ip 192.168.0.2);
eth4(ip 202.202.202.2);


引用

1.        数据包进入Hook函数后,进行规则匹配;
2.        如果所有match都匹备,则进行SNAT模块的动作,即snat 的target模块;
3.        源地址转换的规则一般是…… -j SNAT –to X.X.X.X,SNAT用规则中预设的转换后地址X.X.X.X,修改连接跟踪表中的replay tuple;
4.        接着,修改数据包的来源的地址,将它替换成replay tuple中的相应地址,即规则中预设的地址,将其发送出去;
5.        对于回来的数据包,应该能在状态跟踪表中,查找与之对应的replay tuple,也就能顺藤摸瓜地找到原始的tuple中的信息,将应答包中的目的地址改回来,这样,整个数据传送就得以顺利转发了;

谢谢独孤九贱前辈的总结和对2.4的源码分析
引用

    * NF_IP_PRE_ROUTING (0)
          数据报在进入路由代码被处理之前,数据报在IP数据报接收函数ip_rcv()(位于net/ipv4/ip_input.c,Line379)的最后,也就是在传入的数据报被处理之前经过这个HOOK。在ip_rcv()中挂接这个HOOK之前,进行的是一些与类型、长度、版本有关的检查。
          经过这个HOOK处理之后,数据报进入ip_rcv_finish()(位于 net/ipv4/ip_input.c,Line306)
          在这个HOOK上主要是对数据报作报头检测处理,以捕获异常情况。
    *  NF_IP_LOCAL_IN (1)
          目的地为本地主机的数据报在IP数据报本地投递函数ip_local_deliver()(位于net/ipv4/ip_input.c,Line290)的最后经过这个HOOK。
          经过这个HOOK处理之后,数据报进入ip_local_deliver_finish()(位于 net/ipv4/ip_input.c,Line219)
          这样,IPTables模块就可以利用这个HOOK对应的INPUT规则链表来对数据报进行规则匹配的筛选了。防火墙一般建立在这个HOOK上。
    * NF_IP_FORWARD (2)
          目的地非本地主机的数据报,包括被NAT修改过地址的数据报,都要在IP数据报转发函数ip_forward()(位于net/ipv4/ip_forward.c,Line73)的最后经过这个HOOK。
          经过这个HOOK处理之后,数据报进入ip_forward_finish ()(位于net/ipv4/ip_forward.c,Line44)
          另外,在net/ipv4/ipmr.c中的 ipmr_queue_xmit()函数(Line1119)最后也会经过这个HOOK。(ipmr为多播相关,估计是在需要通过路由转发多播数据时的处理)
       这样,IPTables模块就可以利用这个HOOK对应的FORWARD规则链表来对数据报进行规则匹配的筛选了。
    * NF_IP_LOCAL_OUT (3)
          本地主机发出的数据报在IP数据报构建/ 发送函数ip_queue_xmit()(位于net/ipv4/ip_output.c,Line339)、以及 ip_build_and_send_pkt()(位于net/ipv4/ip_output.c,Line122)的最后经过这个HOOK。(在数据报处理中,前者最为常用,后者用于那些不传输有效数据的SYN/ACK包)
          经过这个HOOK处理后,数据报进入 ip_queue_xmit2()(位于net/ipv4/ip_output.c,Line281)
          另外,在 ip_build_xmit_slow()(位于net/ipv4/ip_output.c,Line429)和ip_build_xmit()(位于 net/ipv4/ip_output.c,Line638)中用于进行错误检测;在igmp_send_report()(位于 net/ipv4/igmp.c,Line195)的最后也经过了这个HOOK,进行多播时相关的处理。
          这样,IPTables模块就可以利用这个HOOK对应的OUTPUT规则链表来对数据报进行规则匹配的筛选了。
    * NF_IP_POST_ROUTING (4)
          所有数据报,包括源地址为本地主机和非本地主机的,在通过网络设备离开本地主机之前,在IP数据报发送函数ip_finish_output()(位于 net/ipv4/ip_output.c,Line184)的最后经过这个HOOK。
          经过这个HOOK处理后,数据报进入ip_finish_output2()(位于net/ipv4/ip_output.c,Line160)
          另外,在函数ip_mc_output()(位于net/ipv4/ip_output.c,Line195)中在克隆新的网络缓存skb时,也经过了这个HOOK进行处理。


简约流程图



5个挂接点
以下内核代码版本2.6.25
1.1 PREROTING
/* net/ipv4/ip_input.c */
int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
{
......
return NF_HOOK(PF_INET, NF_INET_PRE_ROUTING, skb, dev, NULL,
		       ip_rcv_finish);

......
}

1.2 INPUT
/* net/ipv4/ip_input.c */
int ip_local_deliver(struct sk_buff *skb)
{
......
return NF_HOOK(PF_INET, NF_IP_LOCAL_IN, skb, skb->dev, NULL,
         ip_local_deliver_finish);
}

1.3 FORWARD
/* net/ipv4/ip_forward.c */
int ip_forward(struct sk_buff *skb)
{
......

return NF_HOOK(PF_INET, NF_IP_FORWARD, skb, skb->dev, rt->u.dst.dev,
         ip_forward_finish);
......
}

1.4 OUTPUT
/* net/ipv4/ip_output.c */
int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk,
  __be32 saddr, __be32 daddr, struct ip_options *opt)
{
......
return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
         dst_output);
}

int __ip_local_out(struct sk_buff *skb)
{
	struct iphdr *iph = ip_hdr(skb);

	iph->tot_len = htons(skb->len);
	ip_send_check(iph);
	return nf_hook(PF_INET, NF_INET_LOCAL_OUT, skb, NULL, skb->dst->dev,dst_output);
}
1.5 POSTROUTING

/* net/ipv4/ip_output.c */

int ip_mc_output(struct sk_buff *skb)
{

 ......
return NF_HOOK_COND(PF_INET, NF_INET_POST_ROUTING, skb, NULL, skb->dev,ip_finish_output,!(IPCB(skb)->flags & IPSKB_REROUTED));
}

int ip_output(struct sk_buff *skb)
{
......
	return NF_HOOK_COND(PF_INET, NF_INET_POST_ROUTING, skb, NULL, dev,ip_finish_output,!(IPCB(skb)->flags & IPSKB_REROUTED));
......
}




引用


NAT操作也是以netfilter节点形式挂接在相应的处理点上的,DNAT挂接在NF_IP_PRE_ROUTING点上,优先级高于 FILTER低于MANGLE,表示在mangle表后处理,但在filter表前处理数据包;SNAT挂接在NF_IP_POST_ROUTING点上,优先级低于FILTER,表示在filter表后面处理数据包。


NAT分为源NAT和目的NAT两种,源NAT是指转换前源IP地址和转换后源IP地址不同,数据进入防火墙后,防火墙将其源地址进行了转换后再将其发出,使外部看不到数据包原来的源地址,对于TCP/UDP协议的数据包,防火墙不仅修改地址,还通过修改源端口来区分不同内部地址的连接,因此NAT环境下一个合法IP最多可以有65535条TCP连接;目的NAT是指转换前目的IP地址和转换后目的IP地址不同(对于TCP/UDP协议,也可以改变端口号),数据进入防火墙后,防火墙将其目的地址进行了转换后再将其发出,使看不到数据包原来的目的地址。


NAT地址转换有两种主要类型:静态转换(Static Translation),动态转换(Dynamic Translations)。
  静态转换是最简单的一种转换方式,它在NAT表中为每一个需要转换的内部地址创建了固定的转换条目,映射了唯一的全局地址。内部地址与全局地址一一对应。每当内部节点与外界通信时,内部地址就会转化为对应的全局地址。
  动态转换,增加了网络管理的复杂性,但也提供了很大的灵活性。它将可用的全局地址地址集定义成NAT池(NAT pool)。对于要与外界进行通信的内部节点,如果还没有建立转换映射,边缘路由器或者防火墙将会动态的从NAT池中选择全局地址对内部地址进行转化。每个转换条目在连接建立时动态建立,而在连接终止时会被回收。这样,网络的灵活性大大增强了,所需要的全局地址进一步的减少。值得注意的是,当NAT池中的全局地址被全部占用以后,以后的地址转换的申请会被拒绝。这样会造成网络连通性的问题。所以应该使用超时操作选项来回收NAT池的全局地址。另外,由于每次的地址转换是动态的,所以同一个节点在不同的连接中的全局地址是不同的,这会使snmp的操作复杂化。
  
  
  图1
  端口地址转换(NAPT—Network address port translatin),是动态转换的一种变形。它可以使多个内部节点共享一个全局IP地址,而使用源和目的的TCP/UDP的端口号来区分NAT表中的转换条目及内部地址。这样,就更节省了地址空间。比如说,假设内部节点10.1.1.3,10.1.1.2都用源端口1723向外发送数据包。NAPT路由器把这两个内部地址都转换成全局地址192.168.2.2,而使用不同的源端口号:1492,1723。当接收方收到的源端口号为1492,则返回的数据包在边缘网关处,目的地址和端口被转换为10.1.1.3:1723;而接收到的源端口号为1723的,目的被映射到10.1.1.2:1723。
  
  
  图2

• 应用数据被分割成T C P认为最适合发送的数据块。这和U D P完全不同,应用程序产生的
数据报长度将保持不变。由T C P传递给I P的信息单位称为报文段或段( s e g m e n t)(参见
图1 - 7)。在1 8 . 4节我们将看到T C P如何确定报文段的长度。
• 当T C P发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能
及时收到一个确认,将重发这个报文段。在第2 1章我们将了解T C P协议中自适应的超时
及重传策略。

• 当T C P收到发自T C P连接另一端的数据,它将发送一个确认。这个确认不是立即发送,
通常将推迟几分之一秒,这将在1 9 . 3节讨论。
• T C P将保持它首部和数据的检验和。这是一个端到端的检验和,目的是检测数据在传输
过程中的任何变化。如果收到段的检验和有差错, T C P将丢弃这个报文段和不确认收到
此报文段(希望发端超时并重发)。
• 既然T C P报文段作为I P数据报来传输,而I P数据报的到达可能会失序,因此T C P报文段
的到达也可能会失序。如果必要, T C P将对收到的数据进行重新排序,将收到的数据以
正确的顺序交给应用层。
• 既然I P数据报会发生重复, T C P的接收端必须丢弃重复的数据。
• T C P还能提供流量控制。T C P连接的每一方都有固定大小的缓冲空间。T C P的接收端只
允许另一端发送接收端缓冲区所能接纳的数据。这将防止较快主机致使较慢主机的缓冲
区溢出。



……………………………………………………………………………………
这两天也是朋友过来了,也没弄毕设了
刚就join()问题弄了下,发现是行不通的,因为这个线程是需要不断运行的,所以join无法等到那个进程结束从而会陷入阻塞。
    其实不用做反而效率更高,因为我想前提是我的Singleton模式已经是保证线程安全了。
    如果要做,应该用wait()和notify()也能实现,但进入阻塞了。
    今天睡不着,看完《速度与激情》,就看了下,原来wait()进入阻塞的原因是没有其他线程notify().


今天看到WilliamChen的博客写到
“4.public boolean isInterrupted()该线程是否受到中断信号。
5.public static boolean interrupted(),检测当前线程是否接受到中断信号。它实际上是调用了Thread.currentThread().isInterrupted()实现的。
6.public final void join()该方法允许一个线程等待另一个线程的结束。比如t是一个目前正在执行的线程,那么t.join()将目前线程挂起直至线程t结束。


觉得在add ip操作时  是不是该等待getIpInfo线程finish??
因为这里2个线程并不需要保证内存一致性,所以只要保证线程的执行顺序,所以我觉得用join()就够了。如果需要考虑内存一致性的话,注意下面那段代码
 private Tank tank;
   public synchronized void putTank(Tank tank){
      //Dont bother to check whether it has room.
      this.tank=tank;
      notifyAll();
   }
   public synchronized Tank getTank(){
      //Check whether there's tank to consume
      while(tank==null){
         //No tank yet, let's wait.
         try{
             wait();
         }catch(InterruptedException e){}
      }
      Tank retValue=tank.
      tank=null; //Clear tank.
      return retValue;
   }
}



ok.

连接状态信息获取出来了,用的另一种思路,不依附以前的netlink,而直接通过系统的proc文件系统获取的信息,这样不要考虑同步异步问题,也提高了多线程的效率。



不过一下午都纠结在如何分解这些字符串中,但后来还是省去了这种繁琐的分解上面。






另辟溪路:
   谢谢seen大哥给出的另一条抓包的路,确实也不用考虑阻塞问题了,我原以为netlink的效率会很高。
   如果nf_conntrack内核模块与user space只有唯一的"proc"通讯的话,那只能用"cat方式“了。给后期的nat实现带来了一定的影响。


连接跟踪实现:


貌似现在的思路越来越明朗了:

关于链接跟踪

1,通过"proc"与nf_conntrack内核模块通讯,获取连接信息


2,通过之前自己已实现ip包过滤模块的hook,来获得ip头,tcp,udp头信息并通过netlink获取连接信息

   类似“ipv4     2 tcp      6 5 TIME_WAIT src=192.168.0.25 dst=221.238.249.178 sport=39027 dport=80 packets=6 bytes=1119 src=221.238.249.178 dst=192.168.0.25 sport=80 dport=39027 packets=5 bytes=2474 [ASSURED] mark=0 secmark=0 use=1"

只是里面的bytes  mark secmark use  TIME_WAIT 这几个信息不知道是从哪来的???


_________________________________________________
发现不是单播 和多播的问题

问题出再多线程建立多个sock时候发生阻塞,虽然局部变量sock在有时候能够成功,但在多线程同时创建的时候就发生错误了。
  没办法,只能共享一个sock链接和绑定了,但动态获取ip数据包的时候也很容易和添加禁止ip发生阻塞,内核模块只要稍微没做的好,用户态这么的线程接收函数就会出现“死锁”现象,所以确定需要解决的重点在:不管网络层是否有网络数据包,内核模块都需要即使返回消息给用户态,这样才不会让用户态无限等下去....这是今天解决的问题。
并分享下今天的成果:



________________________________________________________
单播与多播
  虽然前几天解决了多线程发送消息的问题,没有发生错误,但是感觉效果不是很好,我添加了端口监听的小功能,发现和监听IP信息包同时启动共用同一协议的NL连接时的反应很慢,甚至丢失数据。
   因为在外面,今天晚上再去试试多播异步传送。

int 
netlink_unicast(struct sock *ssk, struct sk_buff 
*skb, u32 pid, int nonblock); 


data指向要被发送的 netlink信息,pid是接收进程的pid号。这里使用NLPID公式1。Nonblock表示这个API在接收缓冲不可用或者立即返回失败时是不是 被阻塞。

我们也可以使用多播来发送信息。下面的函数将发送一个消息到一个配置了特殊pid和多播组的进程。


代码:
void 
netlink_broadcast(struct sock *ssk, struct sk_buff 
*skb, u32 pid, u32 group, int allocation); 


其中group是所有接收的多播组的标志号。Allocation是内核内存分配类型。如果是再中断上下文环境,有GFP_ATOMIC,其他的有GFP_KERNEL。这取决于内核需要分配一个内存块还是分配多个缓冲用来克隆这个多播信息。

   
______________________________________
解决多线程sock通讯问题

  因为对netlink没有做深入了解,所以本以为多线程创建多个sock链接就可以解决同步问题,结果出现了error..
  而发生错误的原因是多线程创建sock后,bind()操作失败,换句话说,我需要保证做过滤ip操作和获取Ip信息包操作不能在底层发生冲突。
  于是乎,有想到再kernel_module多创建个netlink链接,结果还是以失败告终。在这简单介绍下创建nl操作
  nlfd = netlink_kernel_create(&init_net,NL_FW,0, kernel_receive,NULL,THIS_MODULE);

2.6.25的原型是:
extern struct sock *netlink_kernel_create(struct net *net,
  int unit,unsigned int groups,
  void (*input)(struct sk_buff *skb),
  struct mutex *cb_mutex,
  struct module *module);


这里只关注 NL_FW 是nl的自定义标示,kernel_receive是内核自动绑定接收消息函数,最后个参数是指定模块

   于是又去查,发现每次单播消息是通过getpid()这个进程ip作为通讯的标示符,于是如果对不同操作定义不同的唯一的标示符,试了试,果然可行。粗略代码如下:

User Space:
  local.nl_family = AF_NETLINK;//设置环境
  local.nl_pid = NL_IPINFO;//通讯唯一表示的id
  local.nl_groups = 0;//是否指定多播,我理解为可指定进程组

   //用于把一个打开的 netlink socket 与 netlink 源 socket 地址绑定在一起
  if(bind(skfd, (struct sockaddr*)&local, sizeof(local)) != 0)
    {
      printf("bind() error in getIpinfo\n");
      return 0;
    }

  memset(&kpeer, 0, sizeof(kpeer));
  kpeer.nl_family = AF_NETLINK;
  kpeer.nl_pid = 0;  //0表示内核进程
  kpeer.nl_groups = 0;

 message.hdr.nlmsg_pid = local.nl_pid;

 //发送消息包给内核
  sendto(skfd, &message, message.hdr.nlmsg_len, 0,
	 (struct sockaddr*)&kpeer, sizeof(kpeer));

Kernel Space:

 else if(nlh->nlmsg_type == FW_IP_INFO)
 		      {
			write_lock_bh(&user_proc.lock);
			user_proc.pid = nlh->nlmsg_pid;
			write_unlock_bh(&user_proc.lock);
		      }


______________________________________________________

static void  del_ip_list(struct proc *proc)
{
    struct proc_list *temp=NULL;
    struct proc_list *head=proc->head;
    printk("in del_ip_list\n");
    while(head!=NULL){
     temp=head;
     head=head->next;
     kfree(temp);
     printk("del one ip\n");
    }
proc->head=NULL; 
}

这个置空操作很诡异,弄了我一下午的时间。郁闷!让我机器重启了很多次了



linux 防火墙  简单来说就是利用netfilter的那套框架或者说HOOK/

价值:看到以前的防火墙大多是在2.4版本下的,而2.6.24之后发生大的变化了。
研究思路:

(1)、在Linux内核2.6平台上,首先实现内核模块的编写和加载,学会Netfilter的实现框架,并自己用C语言实现包过滤、数据追踪等功能;
(2)设计一套自己的框架来创建和加载用户规则;接着实现用JAVA编写的UI(界面)并调用底层的C语言用户库,并实现用户态与内核态双方的即时通讯;
(3)完善系统的整体功能和构架。并尽量做到设计上的优化、操作上的简便和使用上的高效。
方案:
用Java Swing做UI,用Netfilter框架注册自己写的C语言规则,用JNI实现用户空间与系统空间的交互,用模块编程把系统加载到Linux操作系统中。

——————————————————————————————————————————
解决2.6内核模块加载问题
——————————————————————————————————————————
解决端口包过滤,包含目的,源端口
——————————————————————————————————————————
解决IP包过滤问题
unsigned int      ---------> __u32;  
__u32                   --------->__bitwise __be32;
__be32                 ---------> saddr;
所以:saddr 实际上是unsigned int类型
输出: printk("Packet from %u\n",iph->saddr);
解决unsigned int * / unsigned char *的比较问题
解决IP地址与unsigned int *转换问题
http://libkf701.googlecode.com/svn/trunk/
**
**
——————————————————————————————————————————
解决JNI问题与Linux内核交互问题:
In static block, we are loading a DLL named TestDll.dll which should be placed in <WINDIR>/SYSTEM32 directory. I am not saying you cannot put this DLL in other directories, you can, but then you will have to register that path in JVM. Here you can call another method as well, instead of loadLibrary().
Collapse Copy Code

System.load(“c://winnt//system32//TestDll.dll”);
I think load() method is better as far as usage is concerned because you can easily place your DLL in any directory.
——————————————————————————————————————————
解决用户态与内核态交涉问题 ,以后再整理一篇博客出来
基本原理:

    iptable好像走的是
    sockfd=socket(AF_INET, SOCK_RAW, IPPROTO_RAW)
   setsockopt(sockfd, IPPROTO_IP, ......)
ip_setsockopt 如果发现不在范围的选项,可能会调用nf_setsockopt
而后者就会检查nf_register_sockopt注册的了
如果level 不是 SOL_SOCKET这个的话,应该就调用sock->ops->setsockopt()吧,这个地方没找到在初始化。
按照版主的说法,应该在这个里面实现选项的查找,然后没有查找到的话,调用nf_setsockopt
设计基本定下用netlink实现用户态到内核态的即时通讯了.
今天在公司基本看了一天的netlink,因为没环境,所以得晚上回来弄,后来发现2.6.24内核版本后做了改动,主要还是再create上面,把以前用while控制的读取消息队列,该成了自动绑定```
刚弄完内核模块的任务,明天看看用户态的实现,今天也是晚上睡了会,所以很晚才弄,接触过这么久的linux,这次才真正感觉弄了点实质的东西,呵呵```` 毕业设计估计老师望着我急,我也只能望着他干瞪眼了``
我的小乖乖开始通讯了:ICMP网络层抓截取数据包实现如下



关于自旋锁:
自旋锁最多只能被一个可执行线程持有。自旋锁不会引起调用者睡眠,如果一个执行线程试图获得一个已经被持有的自旋锁,那么线程就会一直进行忙循环,一直等待下去,在那里看是否该自旋锁的保持者已经释放了锁,"自旋"一词就是因此而得名。
由于自旋锁使用者一般保持锁时间非常短,因此选择自旋而不是睡眠是非常必要的,自旋锁的效率远高于互斥锁。
信号量和读写信号量适合于保持时间较长的情况,它们会导致调用者睡眠,因此只能在进 程上下文使用(_trylock的变种能够在中断上下文使用);而自旋锁适合于保持时间非常短的情况,因为一个被争用的自旋锁使得请求它的线程在等待重新 可用时自旋,特别浪费处理时间,这是自旋锁的要害之处,所以自旋锁不应该被长时间持有。
**
**
关于与内核通讯小结:
方式:bootloader,sysfs,sysctl,系统调用,netlink,procfs,seq_file,debugfs,relayfs
**
bootloader
内核启动参数方式是单向的,即只能向内核传递,而不能从内核获取,并且它是启动时候为内核传递参数,正因为这个存在,用户态才可以在系统启动时候为操作系统设置环境了.
这种方式可以加载到新内核中了.
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>
#define MAX_SIZE 5
static int setup_example_int;
static int setup_example_int_array[MAX_SIZE];
static char setup_example_string[16];
static int __init parse_int(char * s)//参数处理函数
{
        int ret;
        ret = get_option(&s, &setup_example_int);//内核为整数参数值的分析提供了函数 get_option 和 get_options
        if (ret == 1) {
                printk("setup_example_int=%d\n", setup_example_int);
        }
        return 1;
}
static int __init parse_string(char *s)
{
        if (strlen(s) > 15) {
                printk("Too long setup_example_string parameter, \n");
                printk("maximum length is less than or equal to 15\n");
        }
        else {
                memcpy(setup_example_string, s, strlen(s) + 1);
                printk("setup_example_string=%s\n", setup_example_string);
        }
        return 1;
}
__setup("setup_example_string",setup_example_string);


Sysfs:

文字丢失








#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/stat.h>
static int my_int_para = 0;
static char * my_string_para = "Hello, World";
static int __init exam_module_init(void)
{
        printk("my_int = %d\n", my_int_para);
        printk("my_string = '%s'\n", my_string_para);
        return 0;
}
static void __exit exam_module_exit(void)
{
        printk("my_int = %d\n", my_int_para);
        printk("my_string = '%s'\n", my_string_para);
}
module_init(exam_module_init);
module_exit(exam_module_exit);
module_param(my_int_para, int, 0);//通过宏module_param可以挂到sysfs里面
MODULE_PARM_DESC(my_int_para, "An invisible int under sysfs");
module_param(my_string_para, charp, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
MODULE_PARM_DESC(my_string_para, "An visible string under sysfs");
MODULE_LICENSE("GPL");
**


sysctl:
Sysctl是一种用户应用来设置和获得运行时内核的配置参数的一种有效方式,通过这种方式,用户应用可以在内核运行的任何时刻来改变内核的配置参数,也可以在任何时候获得内核的配置参数,通常,内核的这些配置参数也出现在proc文件系统的/proc/sys目录下,用户应用可以直接通过这个目录下的文件来实现内核配置的读写操作,例如,用户可以通过
Cat /proc/sys/net/ipv4/ip_forward
来得知内核IP层是否允许转发IP包,用户可以通过
echo 1 > /proc/sys/net/ipv4/ip_forward
**
系统调用(2.6.25):
1. linux/kernel/sys.c
在该文件里增加了 一个自己的函数:  

asmlinkage void sys_hello(void)
{
     printk("Hello world!\n");
     return ;
}

2. linux/include/asm/unistd_32.h
添加系统调用号: #define __NR_hello 325  
并且把 #define __NR_syscalls 325 改为 #define __NR_syscalls 326

3. linux/arch/x86/kernel/syscall_table_32.S
添加指向自己增加函数的指针
.long sys_hello

4. linux/syscalls.h
添加自己的系统调用的函数声明:
asmlinkage void sys_hello (void);

接下来编译内核:

动态截取系统调用:http://www.ibm.com/developerworks/cn/linux/l-knldebug/index.html?ca=dwcn-newsletter-linux
netlink
是一种双向的数据交换方式,它使用起来非常简单高效,特别是它的广播特性在一些应用中非常方便。它是所有这些用户态与内核态数据交换方式中最有效的最强大的方式。
seq_file是单向的,即只能向内核传递,而不能从内核获取,而另外三种方式均可以进行双向数据交换,即既可以从用户应用传递给内核,又可以从内核传递给应用态应用。
procfs一般用于向用户出口少量的数据信息,或用户通过它设置内核变量从而控制内核行为。
seq_file实际上依赖于 procfs,因此为了使用seq_file,必须使内核支持procfs。
debugfs用于内核开发者调试使用,它比其他集中方式都方便,但是仅用于简单类型的变量处理。
relayfs是一种非常复杂的数据交换方式,要想准确使用并不容易,但是如果使用得当,它远比procfs和seq_file功能强大。
**
__________________________________________________________________________________________
解决IP地址到__be32转换问题


__ue32 inet_addr(const char *);//不过是在用户态用的,内核态还没找到
如:
message.packet_ip_info.ip_addr=inet_addr("192.168.0.1");

____________________________________________________________

解决NL读取用户空间传来的自定义数据结构问题


memcpy(void*,void*,size_t);
如:这里用到了NLMSG常用的宏来提取数据大小
memcpy(&packet_from_user,NLMSG_DATA(nlh), sizeof(struct packet_info));

_________________________________________________________________
解决JNI与C参数传递
java 对象与 c结构体进行交互:

jobjectArray args=0;
//array size
jsize len=20;
int len_c=20;
jclass objClass = (*env)->FindClass(env,"java/lang/Object");
//create object arrary
args = (*env)->NewObjectArray(env,len, objClass, 0);
jclass objectClass = (*env)->FindClass(env,"packet/DiskInfo");
jfieldID ipsrc = (*env)->GetFieldID(env,objectClass,"ipsrc","Ljava/lang/String;");
jfieldID ipdest = (*env)->GetFieldID(env,objectClass,"ipdest","Ljava/lang/String;");
jfieldID protocol = (*env)->GetFieldID(env,objectClass,"protocol","Ljava/lang/String;");

//清理前个对象工作
jobject objTemp = (*env)->AllocObject(env,objectClass);
(*env)->SetObjectField(env,_obj,ipdest,(*env)->NewStringUTF(env,inet_ntoa(addr)));

(*env)->SetObjectArrayElement(env,args, i, objTemp);

return args;

实现ip数据包信息返回给上层ui

__________________________________________________________________
解决过滤ip存储问题
用的链表,比较粗糙,并想结合端口过滤一起做进去,通过标示符来判别是什么操作




  • 大小: 86.6 KB
  • 大小: 15.6 KB
  • 大小: 94.8 KB
  • 大小: 79.7 KB
  • demo.rar (172.1 KB)
  • 描述: demo(2.6.25kernel
  • 下载次数: 142
  • 毕业设计.rar (370.9 KB)
  • 描述: 当时自己搜集的资料
  • 下载次数: 134
3
0
分享到:
评论
5 楼 fishrain_ 2009-05-14  
做好了呀!!我的还在途中呢,期待你的项目,想借鉴一下!

我的GTKMM+iptables+netfilter,真让人头疼
4 楼 C_J 2009-05-12  
fishrain_ 写道

我的题目也是这个,第一次在LINUX下开发项目。由于前一个多月在忙于其它事,所以没有太研究,很是被动。最终我还是直接调用调用第三方库了。我用GTKMM做界面,下面直接转化为iptables,搞了半个月了,还只是把界面给弄出来。可否给我点建议,或者推荐我看一些项目,文档之类的,谢谢了!!PS:我想在防火墙里加入网络监视这个功能(对局域网中各计算机进包/出包进行监视),这个要怎么弄呢?


其实netfliter本身提供了一套扩展性很强的东西
只需要不断挂hook函数就可以了

关于监视,你是不是说的连接跟踪?  博客和"综合技术"版面有个我的帖子(在cu上也发了一个帖子http://linux.chinaunix.net/bbs/thread-1098391-1-5.html)说明了我的实现方法,其实也是直接用的 /proc/net/nf_conntrack 获取到的内核网络包信息,并没自己重新做内核模块。

我没有用到iptables,其实iptables也是个用户级的模块。

这几天也是有点忙,一直没时间上博客了。呵呵

3 楼 fishrain_ 2009-05-09  
我的题目也是这个,第一次在LINUX下开发项目。由于前一个多月在忙于其它事,所以没有太研究,很是被动。最终我还是直接调用调用第三方库了。

我用GTKMM做界面,下面直接转化为iptables,搞了半个月了,还只是把界面给弄出来。



可否给我点建议,或者推荐我看一些项目,文档之类的,谢谢了!!

PS:我想在防火墙里加入网络监视这个功能(对局域网中各计算机进包/出包进行监视),这个要怎么弄呢?
2 楼 C_J 2009-04-20  
idealab 写道

同寝室也有人做你这个毕设题目,他用QT开发界面,底层调用一些开源实现。

我没用第三方库,那样可能会省事很多,毕竟不要考虑与内核进行交互了。
选java也是为了学习点jni接口,以后可能有兴趣参与另一个开源项目。因为只提供了一个so库。
1 楼 idealab 2009-04-20  
同寝室也有人做你这个毕设题目,他用QT开发界面,底层调用一些开源实现。

相关推荐

Global site tag (gtag.js) - Google Analytics