歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Unix知識 >> Unix教程 >> PF_KEY接口在OpenBSD內核中的實現

PF_KEY接口在OpenBSD內核中的實現

日期:2017/2/27 17:43:54   编辑:Unix教程
OpenBSD
  PF_KEY協議是IPSec的重要組成部分。密鑰管理進程利用PF_KEY與內核的SADB進行通信,實現SA(Security Association,安全聯盟)和SP(Security Policy,安全策略)的管理。本文將從PF_KEY協議構造和PF_KEY相關系統調用等方面描述OpenBSD內核中的PF_KEY實現。
  
  利用IPSec技術可在IP層實現對數據包進行保護,降低了互聯網通信的風險。IPsec的安全服務是由通信雙方建立的安全聯盟(SA)來提供的。IPsec系統在處理輸入/輸出IP 流時必須參考安全策略庫(SPD),並根據從SPD中提取的策略對IP流進行不同的處理,例如拒絕、繞過和進行IPsec保護。SA和SPD的管理是利用PF_key API來實現用戶進程和內核之間的通信,可以通過手工進行,也可以通過IKE來進行動態協商。
  
  PF_KEY是用戶進程操作內核中的SADB(Security Associations Database)和SPD的編程接口。下面將從協議族構造、系統調用和PF_KEY socket實現三部分加以描述。
  
  協議族構造
  
  Net/3組把協議關聯到一個域中,並且用一個協議族常量來標識每個域。在OpenBSD定義的域包含以下的四個,分別是路由協議族、IP協議族、Unix協議族和PF_KEY協議族。同時定義了全局指針型變量domains,由它將協議族結構鏈接在一起。
  
  初始化完成後,內核構建了圖1所示的數據結構。pfkey_domain定義的協議族為PF_KEY,Socket地址為PF_KEY。
  
  在PF_KEY協議初始化過程中,系統還定義了指針數組,用於指向含有Socket操作函數的不同版本pfkey_version結構。pfkey_version結構定義在sys/net/pfkeyv2.h文件中。
  
   
  
  圖1 初始化後的domain鏈表和protosw數組
  
  目前OpenBSD實現的PF_KEY版本是2.0,該指針數組結構定義如下:
  
  struct pfkey_version
  {  int protocol;  
     int (*create)(struct socket socket); 
     int (*release)(struct socket *socket);
     int (*send)(struct socket *socket, void *message, int len);
  } = { PFKEYV2_PROTOCOL, &pfkeyv2_create, &pfkeyv2_release, &pfkeyv2_send };
  
  PF_KEY socket實現
  
  PF_KEY socket系統調用
  
  當一個應用調用Socket時,進程用系統調用機制將三個獨立的整數傳給內核。syscall將參數復制到32bit值的數組中,並將數組指針作為第二個參數傳給Socket的內核版。內核版的Socket將第二個參數“uap”作為指向sys_socket_args結構的指針。圖2顯示了上述過程。
  
   
  
  圖2 用戶空間Socket參數到內核空間轉換圖
  
  當進程調用socket(PF_KEY,SOCK_RAW, PF_KEY_V2)時,內核sys_socket()向PF_KEY協議中pfkey_protosw(見圖1)定義的pfkey_usrreq()發送PRU_ATTACH,調用如下:
  
  error=(*prp->pr_usrreq)(so,PRU_ATTACH,NULL,(struct mbuf *)(long)proto,NULL);
  
  Pfkey_usrreq()處理PRU_ATTACH請求,創建協議控制塊。
  
  PF_KEY close()系統調用
  
  內核中sys_close()對應用戶空間的close()系統調用,用來關閉各類描述符。當fd是引用對象的最後描述符時,與對象有關的close函數被調用如下:
  
  error=(*fp->f_ops->fo_close)(fp, p);
  
  Socket的fp->f_ops->fo_close是soo_close()函數。soo_close()是soclose()函數的封裝器。soclose()取消Socket上相關連接並釋放不需要的數據結構,發送PRU_DETACH請求斷開Socket與PF_KEY協議的聯系,最後調用sofree()釋放PF_KEY socket。
  
  error2=(*so->so_proto->pr_usrreq)(so, PRU_DETACH, NULL,NULL, NULL);
  
  Pfkey_usrreq()響應soclose()的PRU_DETACH請求,釋放該Socket協議控制塊,並將該Socket從pfkeyv2_sockets鏈表中刪除。
  
  PF_KEY 讀寫系統調用
  
  所有的寫系統調用都要直接或間接地調用sosend。sosend的功能是將進程來的數據復制到內核,並將數據傳遞給與插口相關的協議,如下:
  
  error=(*so->so_proto->pr_usrreq)(so, PRU_SEND, top, addr, control);
  
  對於PF_KEY的Socket,pfkey_usrreq()間接調用pfkey_output(),pfkey_output()直接調用pfkeyv2_send()。
  
  所有處理用戶消息的工作全部在pfkeyv2_send()內完成,包括SA的增加、刪除、清空和請求操作等。pfkeyv2_send()通過pfkeyv2_sendup()將結果放入Socket的接收緩沖區中,並通知用戶進程接收緩存已改變。
  
  同寫調用一樣,用戶空間的讀調用如read和recv等,都是由內核中的一個公共函數soreceive()來完成所有的工作。sorecevice()函數將數據從Socket的接口緩存傳送到進程指定的緩存中。
  
  sorecevice是一個復雜的函數,導致其復雜的主要原因是繁瑣的指針操作及對多種類型的數據(帶外數據、地址、控制信息和正常數據)和多目標(進程緩存和mbuf鏈)的處理。
  
  最後在這裡列出PF_KEY的接口實現的相關系統調用圖,見圖3。
  
  
  
  圖3 PF_KEY內核調用圖
  
  結束語
  
  本文主要描述了PF_KEY接口在OpenBSD內核中的實現。通過該接口,可以實現IPSec的SA和SP的自動和手工配置管理。作為IPSec的重要組成部分,SADB和SPD與PF_KEY有著緊密的聯系,但本文中沒有具體描述。想要深入了解的IPSec的整體實現,閱讀源碼是比較有效的方法之一。
Copyright © Linux教程網 All Rights Reserved