Linux防火牆程式設計(轉)

amyz發表於2007-08-13
Linux防火牆程式設計(轉)[@more@]

  Yahoo、eBay、CNN.com、Amazon、Buy.com和E*Trade等著名商業網站連續遭到駭客攻擊,造成了數以十億美元的損失,向世人再一次敲響了網路並不安全的警鐘。防火牆作為一種網路或系統之間強制實行訪問控制的機制,是確保網路安全的重要手段。目前社會上各種商業產品的防火牆非常多,功能也大都很強。我們暫且不管這些防火牆產品的價格如何,由於它們在開發設計過程中注重的是產品的通用性、相容性,考慮更多的是市場和利潤,因此對於某些特殊的應用就不一定很合適。如果使用者能根據自己的實際需要,將防火牆設計的一般理論和方法與自己系統的具體實踐相結合,設計一些小而精、精而強的防火牆程式,則往往可以發揮出比花大價錢買來的通用型防火牆更好的作用。

  由於篇幅所限,本文不可能對防火牆的一般理論和結構進行深入的討論,因此僅以Linux系統為例,具體說明防火牆程式的設計方法。

  一、 從程式設計角度看Linux網路

  編寫防火牆程式並不一定要求對Linux網路核心有多麼深刻的理解,只是需要明白在網路核心中有這樣一種機制,那就是核心可以自動呼叫使用者編寫的防火牆程式,並根據這個防火牆程式返回的結果來決定對網路收發資料包的處理策略。

  二、 怎樣將自己編寫的防火牆程式登記到核心中

  我們已經知道核心在網路層中自動呼叫使用者編寫的防火牆程式。但有一個前提條件就是使用者必須正確地將自己編寫的防火牆程式登記到核心中。

  核心中提供了防火牆的登記和解除安裝函式,分別是register_firewall和unregister_firewall,參見firewall.c。

  1、 register_firewall

  函式原型如下:

  int register_firewall(int pf,struct firewall_ops *fw)

  返回值:0代表成功,小於0表示不成功。

  引數:

  * 協議標誌pf,主要的取值及其代表的協議如下:

  2代表Ipv4協議,4代表IPX協議,10代表Ipv6協議等。

  * 引數結構fw定義如下:

  struct firewall_ops{

  struct firewall_ops *next;

  int (*fw_forward)(struct firewall_ops *this, int pf,

  struct device *dev, void *phdr, void *arg, struct sk_buff **pskb);

  int (*fw_input)(struct firewall_ops *this, int pf,

  struct device *dev, void *phdr, void *arg, struct sk_buff **pskb);

  int (*fw_output)(struct firewall_ops *this, int pf,

  struct device *dev, void *phdr, void *arg, struct sk_buff **pskb);

  int fw_pf;

  int fw_priority;

  };

  結構中next的域將由核心來修改,使其指向下一個防火牆模組。

  fw_pf域為協議標誌,含義同上。

  fw_priority指定優先順序,一般應大於0。

  fw_input、fw_output、fw_forward是使用者編寫的防火牆函式模組,在接收到網路報和傳送網路報時核心將呼叫這些模組,後面將詳細討論。

  2、 unregister_firewall

  unregister_firewall的原型說明與呼叫方法同register_firewall。

  三、 防火牆函式模組的設計

  1、 防火牆函式模組的返回值

  返回值是至關重要的,核心將根據它來決定對網路資料包採取的處理策略。主要返回值及意義如下:

  0和1 通知核心忽略該網路報。

  -1 通知核心忽略該網路報,併傳送不可達到的網路控制報(ICMP報文)。

  2 通知核心認可該網路報。

  2、 各模組函式的入口引數

  * 引數this

  指向register_firewall中的fw引數結構。

  * 引數pf

  含義同register_firewall中的pf引數。

  * 引數dev

  dev是指向資料結構device的指標。在Linux系統中,每一個網路裝置都是用device資料結構來描述的。在系統引導期間,網路裝置驅動程式向Linux登記裝置資訊,如裝置名、裝置的I/O基地址、裝置中斷號、網路卡的48位硬體地址等,device資料結構中包括這些裝置資訊以及裝置服務函式的地址。關於device結構的詳細資訊可參見netdevice.h標頭檔案。

  * 引數phdr

  該引數指向鏈路層資料包報頭首址。

  * 引數arg

  利用這個引數可以向核心傳遞資訊,如重定向時的埠號。

  * 引數pskb

  此引數是指向sk_buff結構指標的指標。在Linux中,所有網路資料的傳送和接收都用sk_buff資料結構表示。在sk_buff資料結構中包含有對應裝置結構的device地址、傳輸層、網路層、鏈路層協議頭地址等。關於sk_buff的定義可參見skbuff.h標頭檔案。

  3、防火牆程式示例

  下面給出一個簡單防火牆程式。在這裡假設讀者對以太協議、IP協議、TCP協議等常用協議有一定的瞭解。用命令列"gcc -Wall -O2 -c MyFirewall.c"進行編譯,再用insmod命令載入程式後,系統將只響應外部網路用TCP協議的80埠所進行的訪問。要讓系統恢復原有功能,則可用rmmod命令解除安裝該程式,原始碼見網站上的同名文章。

  // MyFirewall.c 2000年3月7日編寫

  #ifndef __KERNEL__

  # define __KERNEL__ //按核心模組編譯

  #endif

  #ifndef MODULE

  # define MODULE //按裝置驅動程式模組編譯

  #endif

  #include <linux/module.h> //最基本的核心模組標頭檔案

  #include <linux/sched.h>

  #include <linux/kernel.h> //最基本的核心模組標頭檔案

  #include <linux/netdevice.h>

  #include <linux/ip.h>

  #include <linux/tcp.h>

  #include <linux/skbuff.h>

  #include <linux/proc_fs.h>

  #include <linux/if.h>

  #include <linux/in.h>

  #include <linux/firewall.h>

  #define SOL_ICMP 1

  #define PERMIT_PORT 80 //只允許訪問TCP的80埠

  int zzl_input(struct firewall_ops *this,int pf,struct device *dev,

  void *phdr,void *arg,struct sk_buff **pskb)

  {//每當收到一個網路報時,此函式將被核心呼叫

  struct tcphdr *tcph; //TCP的頭指標

  struct iphdr *iph; //IP頭指標

  struct sk_buff *skb=*pskb;

  if (skb->protocol==htons(ETH_P_ARP)){

  printk(" Permit a ARP Packet");

  return FW_ACCEPT;//允許地址解析協議報

  }

  if(skb->protocol==htons(ETH_P_RARP)){

  printk(" Permit a RARP Packet");

  return FW_ACCEPT;//允許反向地址解析協議報

  }

  if(skb->protocol==htons(ETH_P_IP))

  {

  iph=skb->nh.iph;

  if (iph->protocol==SOL_ICMP)

  {

  printk(" Permit a ICMP Packet");

  return FW_ACCEPT;//允許網路控制報

  }

  if(iph->protocol==SOL_TCP){

  tcph=skb->h.th;

  if(tcph->dest==PERMIT_PORT){

  printk(" Permit a valid access");

  return FW_ACCEPT;//允許對TCP埠80的訪問

  }

  }

  }

  return FW_REJECT;//禁止對本計算機的所有其它訪問

  }

  int zzl_output(struct firewall_ops *this,int pf,struct device *dev,

  void *phdr,void *arg,struct sk_buff **pskb)

  {//程式編寫方法同zzl_input函式模組

  printk(" zzl_output is called ");

  return FW_SKIP;

  }

  int zzl_foreward(struct firewall_ops *this,int pf,struct device *dev,

  void *phdr,void *arg,struct sk_buff **pskb)

  {//程式編寫方法同zzl_input函式模組

  printk(" zzl_foreward is called ");

  return FW_SKIP;

  }

  struct firewall_ops zzl_ops=

  {

  NULL,

  zzl_foreward,

  zzl_input,

  zzl_output,

  PF_INET,

  01

  };

  int init_module(void)

  {

  if(register_firewall(PF_INET,&zzl_ops)!=0)

  {

  printk(" unable register firewall");

  return -1;

  }

  printk(" zzl_ops=%p",&zzl_ops);

  return 0;

  }

  void cleanup_module(void)

  {

  printk("unload ");

  unregister_firewall(PF_INET,&zzl_ops);

  }

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752019/viewspace-955532/,如需轉載,請註明出處,否則將追究法律責任。

相關文章