Linux內核中的IPSEC實現(1)

Linux內核中的IPSEC實現(1)

本文檔的Copyleft歸yfydz所有,使用GPL發佈,可以自由拷貝,轉載,轉載時請保持文檔的完整性,嚴禁用於任何商業用途。

1. 前言

在Linux2.6內核中自帶了IPSEC的實現,這樣就不用象2.4那樣打補丁來實現了。該實現包括以下幾個部分: PF_KEY類型套接口, 用來提供和用戶層空間進行PF_KEY通信,代碼在net/key目錄下,前面已經介紹過;安全聯盟SA和安全策略SP管理,是使用xfrm庫來實現的,代碼在net/xfrm/目錄下定義;ESP,AH等協議實現,在net/ipv4(6)下定義;加密認證算法庫,在crypto目錄下定義,這些算法都是標準代碼了。本系列文章主要描述XFRM庫的實現以及在IPV4下相關協議的處理部分, IPV6的忽略。

本文Linux內核代碼版本爲2.6.19.2。xfrm是內核中變化比較大的部分,每個版本中都有不小的差異, 同時也說明了該模塊的不成熟性。
在net/xfrm目錄下的各文件大致功能說明如下:
xfrm_state.c: xfrm狀態管理
xfrm_policy.c: xfrm策略管理
xfrm_algo.c: 算法管理
xfrm_hash.c: HASH計算函數
xfrm_input.c: 安全路徑(sec_path)處理,用於進入的ipsec包
xfrm_user.c:  netlink接口的SA和SP管理
在net/ipv4目錄下的和ipsec相關各文件大致功能說明如下:
ah4.c: IPV4的AH協議處理
esp4.c: IPV4的ESP協議處理
ipcomp.c: IP壓縮協議處理
xfrm4_input: 接收的IPV4的IPSEC包處理
xfrm4_output: 發出的IPV4的IPSEC包處理
xfrm4_state: IPV4的SA處理
xfrm4_policy: IPV4的策略處理
xfrm4_tunnel: IPV4的通道處理
xfrm4_mode_transport: 傳輸模式
xfrm4_mode_tunnel: 通道模式
xfrm4_mode_beet: BEET模式

2. 數據結構

內核SA的定義用xfrm_state結構定義,SP用xfrm_policy結構定義,在include/net/xfrm.h中定義。

2.1 狀態(SA)

xfrm_state狀態結構用來描述SA在內核中的具體實現:
struct xfrm_state
{
 /* Note: bydst is re-used during gc */
// 每個狀態結構掛接到三個HASH鏈表中
 struct hlist_node bydst; // 按目的地址HASH
 struct hlist_node bysrc; // 按源地址HASH
 struct hlist_node byspi; // 按SPI值HASH
 atomic_t  refcnt; // 所有使用計數
 spinlock_t  lock;   // 狀態鎖
 struct xfrm_id  id; // ID結構, 即目的地址,SPI,協議三元組
 struct xfrm_selector sel; // 狀態選擇子
 u32   genid; // 狀態的標誌值, 防止發生碰撞
 /* Key manger bits */
 struct {
  u8  state;
  u8  dying;
  u32  seq;
 } km;  // KEY回調管理處理結構參數
 /* Parameters of this state. */
 struct {
  u32  reqid; // 請求ID
  u8  mode;  // 模式: 傳輸/通道
  u8  replay_window; // 回放窗口
  u8  aalgo, ealgo, calgo; // 認證,加密,壓縮算法ID值
  u8  flags; // 一些標準
  u16  family; // 協議族
  xfrm_address_t saddr;  // 源地址
  int  header_len;  // 添加的協議頭長度
  int  trailer_len; //
 } props; // SA相關參數結構
 struct xfrm_lifetime_cfg lft; // 生存時間配置
 /* Data for transformer */
 struct xfrm_algo *aalg; // hash算法
 struct xfrm_algo *ealg; // 加密算法
 struct xfrm_algo *calg; // 壓縮算法
 /* Data for encapsulator */
 struct xfrm_encap_tmpl *encap; // NAT-T封裝信息
 /* Data for care-of address */
 xfrm_address_t *coaddr;
 /* IPComp needs an IPIP tunnel for handling uncompressed packets */
 struct xfrm_state *tunnel;  // 通道, 實際是另一個SA
 /* If a tunnel, number of users + 1 */
 atomic_t  tunnel_users; // 通道的使用數
 /* State for replay detection */
 struct xfrm_replay_state replay; // 回放檢測結構,包含各種序列號掩碼等信息
 /* Replay detection state at the time we sent the last notification */
 struct xfrm_replay_state preplay; // 上次的回放記錄值
 /* internal flag that only holds state for delayed aevent at the
  * moment
 */
 u32   xflags; // 標誌
 /* Replay detection notification settings */
 u32   replay_maxage; // 回放最大時間間隔
 u32   replay_maxdiff; // 回放最大差值
 /* Replay detection notification timer */
 struct timer_list rtimer; // 回放檢測定時器
 /* Statistics */
 struct xfrm_stats stats; // 統計值
 struct xfrm_lifetime_cur curlft; // 當前時間計數器
 struct timer_list timer;  // SA定時器
 /* Last used time */
 u64   lastused; // 上次使用時間
 /* Reference to data common to all the instances of this
  * transformer. */
 struct xfrm_type *type;  // 協議, ESP/AH/IPCOMP
 struct xfrm_mode *mode;  // 模式, 通道或傳輸
 /* Security context */
 struct xfrm_sec_ctx *security; // 安全上下文, 加密時使用
 /* Private data of this transformer, format is opaque,
  * interpreted by xfrm_type methods. */
 void   *data; // 內部數據
};
2.2 安全策略(SP)

xfrm_policy結構用於描述SP在內核內部的具體實現:
struct xfrm_policy
{
 struct xfrm_policy *next; // 下一個策略
 struct hlist_node bydst; // 按目的地址HASH的鏈表
 struct hlist_node byidx; // 按索引號HASH的鏈表
 /* This lock only affects elements except for entry. */
 rwlock_t  lock;  // 策略結構鎖
 atomic_t  refcnt; // 引用次數
 struct timer_list timer; // 策略定時器
 u8   type;     // 類型
 u32   priority; // 策略優先級
 u32   index;    // 策略索引號
 struct xfrm_selector selector; // 選擇子
 struct xfrm_lifetime_cfg lft;     // 策略生命期
 struct xfrm_lifetime_cur curlft;  // 當前的生命期數據
 struct dst_entry       *bundles;  // 路由鏈表
 __u16   family;   // 協議族
 __u8   action;   // 策略動作, 接受/加密/阻塞...
 __u8   flags;    // 標誌
 __u8   dead;     // 策略死亡標誌
 __u8   xfrm_nr;  // 使用的xfrm_vec的數量
 struct xfrm_sec_ctx *security; // 安全上下文
 struct xfrm_tmpl        xfrm_vec[XFRM_MAX_DEPTH]; // 狀態模板
};

xfrm模板結構, 用於狀態和策略的查詢:
struct xfrm_tmpl
{
/* id in template is interpreted as:
 * daddr - destination of tunnel, may be zero for transport mode.
 * spi   - zero to acquire spi. Not zero if spi is static, then
 *    daddr must be fixed too.
 * proto - AH/ESP/IPCOMP
 */
// SA三元組, 目的地址, 協議, SOI
 struct xfrm_id  id;
/* Source address of tunnel. Ignored, if it is not a tunnel. */
// 源地址
 xfrm_address_t  saddr;
// 請求ID
 __u32   reqid;
/* Mode: transport, tunnel etc. */
 __u8   mode;
/* Sharing mode: unique, this session only, this user only etc. */
 __u8   share;
/* May skip this transfomration if no SA is found */
 __u8   optional;
/* Bit mask of algos allowed for acquisition */
 __u32   aalgos;
 __u32   ealgos;
 __u32   calgos;
};
2.3 協議結構
對ESP, AH, IPCOMP等協議的描述是通過xfrm_type結構來描述的, 多個協議的封裝就是靠多個協議結構形成的鏈表來實現:
struct xfrm_type
{
 char   *description; // 描述字符串
 struct module  *owner; // 協議模塊
 __u8   proto;  // 協議值
 __u8   flags;  // 標誌
#define XFRM_TYPE_NON_FRAGMENT 1
// 初始化狀態
 int   (*init_state)(struct xfrm_state *x);
// 析構函數
 void   (*destructor)(struct xfrm_state *);
// 數據輸入函數
 int   (*input)(struct xfrm_state *, struct sk_buff *skb);
// 數據輸出函數
 int   (*output)(struct xfrm_state *, struct sk_buff *pskb);
// 拒絕函數
 int   (*reject)(struct xfrm_state *, struct sk_buff *, struct flowi *);
// 頭部偏移
 int   (*hdr_offset)(struct xfrm_state *, struct sk_buff *, u8 **);
// 本地地址
 xfrm_address_t  *(*local_addr)(struct xfrm_state *, xfrm_address_t *);
// 遠程地址
 xfrm_address_t  *(*remote_addr)(struct xfrm_state *, xfrm_address_t *);
 /* Estimate maximal size of result of transformation of a dgram */
// 最大數據報長度
 u32   (*get_max_size)(struct xfrm_state *, int size);
};
具體的協議結構定義如下, 通常只定義初始化,析構,輸入和輸出四個成員函數:
AH協議定義
/* net/ipv4/ah4.c */
static struct xfrm_type ah_type =
{
 .description = "AH4",
 .owner  = THIS_MODULE,
 .proto       = IPPROTO_AH,
 .init_state = ah_init_state,
 .destructor = ah_destroy,
 .input  = ah_input,
 .output  = ah_output
};
ESP協議定義:
/* net/ipv4/esp4.c */
static struct xfrm_type esp_type =
{
 .description = "ESP4",
 .owner  = THIS_MODULE,
 .proto       = IPPROTO_ESP,
 .init_state = esp_init_state,
 .destructor = esp_destroy,
 .get_max_size = esp4_get_max_size,
 .input  = esp_input,
 .output  = esp_output
};
IP壓縮協議定義:
/* net/ipv4/ipcomp.c */
static struct xfrm_type ipcomp_type = {
 .description = "IPCOMP4",
 .owner  = THIS_MODULE,
 .proto       = IPPROTO_COMP,
 .init_state = ipcomp_init_state,
 .destructor = ipcomp_destroy,
 .input  = ipcomp_input,
 .output  = ipcomp_output
};
IPIP協議定義:
/* net/ipv4/xfrm4_tunnel.c */
static struct xfrm_type ipip_type = {
 .description = "IPIP",
 .owner  = THIS_MODULE,
 .proto       = IPPROTO_IPIP,
 .init_state = ipip_init_state,
 .destructor = ipip_destroy,
 .input  = ipip_xfrm_rcv,
 .output  = ipip_output
};
2.4 模式結構

模式結構用於描述IPSEC連接描述, 可爲通道模式或傳輸模式兩種:
struct xfrm_mode {
// 數據輸入函數
 int (*input)(struct xfrm_state *x, struct sk_buff *skb);
// 數據輸出函數
 int (*output)(struct xfrm_state *x,struct sk_buff *skb);
// 模塊指針
 struct module *owner;
// 封裝
 unsigned int encap;
};
通道模式結構定義:
/* net/ipv4/xfrm4_mode_tunnel.c */
static struct xfrm_mode xfrm4_tunnel_mode = {
 .input = xfrm4_tunnel_input,
 .output = xfrm4_tunnel_output,
 .owner = THIS_MODULE,
 .encap = XFRM_MODE_TUNNEL,
};
傳輸模式結構定義:
/* net/ipv4/xfrm4_mode_transport.c */
static struct xfrm_mode xfrm4_transport_mode = {
 .input = xfrm4_transport_input,
 .output = xfrm4_transport_output,
 .owner = THIS_MODULE,
 .encap = XFRM_MODE_TRANSPORT,
};
beet模式, 不知道在哪用
/* net/ipv4/xfrm4_mode_beet.c */
static struct xfrm_mode xfrm4_beet_mode = {
 .input = xfrm4_beet_input,
 .output = xfrm4_beet_output,
 .owner = THIS_MODULE,
 .encap = XFRM_MODE_BEET,
};

2.5 策略的相關協議處理結構

以下結構用於描述具體協議族下的的策略處理:
struct xfrm_policy_afinfo {
// 協議族
 unsigned short  family;
// 協議類型
 struct xfrm_type *type_map[IPPROTO_MAX];
// 模式
 struct xfrm_mode *mode_map[XFRM_MODE_MAX];
// 目的操作結構
 struct dst_ops  *dst_ops;
// 垃圾蒐集
 void   (*garbage_collect)(void);
// 路由選擇
 int   (*dst_lookup)(struct xfrm_dst **dst, struct flowi *fl);
// 獲取源地址
 int   (*get_saddr)(xfrm_address_t *saddr, xfrm_address_t *daddr);
// 查找路由項
 struct dst_entry *(*find_bundle)(struct flowi *fl, struct xfrm_policy *policy);
// 創建新路由項
 int   (*bundle_create)(struct xfrm_policy *policy,
       struct xfrm_state **xfrm,
       int nx,
       struct flowi *fl,
       struct dst_entry **dst_p);
// 解碼會話
 void   (*decode_session)(struct sk_buff *skb,
        struct flowi *fl);
};

IPV4的策略協議相關處理結構定義如下:
/* net/ipv4/xfrm4_policy.c */
static struct xfrm_policy_afinfo xfrm4_policy_afinfo = {
 .family =   AF_INET,
 .dst_ops =  &xfrm4_dst_ops,
 .dst_lookup =  xfrm4_dst_lookup,
 .get_saddr =  xfrm4_get_saddr,
 .find_bundle =   __xfrm4_find_bundle,
 .bundle_create = __xfrm4_bundle_create,
 .decode_session = _decode_session4,

2.5 狀態的相關協議處理結構
以下結構用於描述具體協議族下的的狀態處理:
struct xfrm_state_afinfo {
// 協議族
 unsigned short  family;
// 初始化標誌
 int   (*init_flags)(struct xfrm_state *x);
// 初始化模板選擇
 void   (*init_tempsel)(struct xfrm_state *x, struct flowi *fl,
      struct xfrm_tmpl *tmpl,
      xfrm_address_t *daddr, xfrm_address_t *saddr);
// 模板排序
 int   (*tmpl_sort)(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n);
// 狀態排序
 int   (*state_sort)(struct xfrm_state **dst, struct xfrm_state **src, int n);
};
IPV4的狀態相關協議處理結構
/* net/ipv4/xfrm4_state.c */
static struct xfrm_state_afinfo xfrm4_state_afinfo = {
 .family   = AF_INET,
 .init_flags  = xfrm4_init_flags,
 .init_tempsel  = __xfrm4_init_tempsel,
};

2.6 回調通知信息結構
struct xfrm_mgr
{
 struct list_head list;
 char   *id;
// 狀態通知
 int   (*notify)(struct xfrm_state *x, struct km_event *c);
// 獲取, 如獲取SA
 int   (*acquire)(struct xfrm_state *x, struct xfrm_tmpl *, struct xfrm_policy *xp, int dir);
// 編譯策略
 struct xfrm_policy *(*compile_policy)(struct sock *sk, int opt, u8 *data, int len, int *dir);
// 映射
 int   (*new_mapping)(struct xfrm_state *x, xfrm_address_t *ipaddr, u16 sport);
// 策略通知
 int   (*notify_policy)(struct xfrm_policy *x, int dir, struct km_event *c);
// 報告
 int   (*report)(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr);
};

在net/key/pf_key.c中定義了pkeyv2_mgr結構:
static struct xfrm_mgr pfkeyv2_mgr =
{
 .id  = "pfkeyv2",
 .notify  = pfkey_send_notify,
 .acquire = pfkey_send_acquire,
 .compile_policy = pfkey_compile_policy,
 .new_mapping = pfkey_send_new_mapping,
 .notify_policy = pfkey_send_policy_notify,
};

3. 初始化

/* net/xfrm/xfrm_policy.c */
// xfrm初始化函數包括狀態, 策略和輸入處理的三初始化函數
// xfrm是不支持模塊方式的
void __init xfrm_init(void)
{
 xfrm_state_init();
 xfrm_policy_init();
 xfrm_input_init();
}

3.1 xfrm狀態初始化
/* net/xfrm/xfrm_state.c */
void __init xfrm_state_init(void)
{
 unsigned int sz;
// 初始HASH表不大, 每個HASH中初始化爲8個鏈表, 但隨着狀態數量的增加
// 會動態增加HASH表數量
 sz = sizeof(struct hlist_head) * 8;
// 建立3組HASH, 分別按SA的源地址, 目的地址和SPI值
 xfrm_state_bydst = xfrm_hash_alloc(sz);
 xfrm_state_bysrc = xfrm_hash_alloc(sz);
 xfrm_state_byspi = xfrm_hash_alloc(sz);
 if (!xfrm_state_bydst || !xfrm_state_bysrc || !xfrm_state_byspi)
  panic("XFRM: Cannot allocate bydst/bysrc/byspi hashes.");
// xfrm_state_hmask初始值爲=7, 計算出的HASH值與該值與來得到鏈表號
 xfrm_state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
// 初始化工作隊列work_queue, 完成對狀態垃圾的蒐集和釋放
 INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task, NULL);
}

3.2 策略初始化

static void __init xfrm_policy_init(void)
{
 unsigned int hmask, sz;
 int dir;
// 建立一個內核cache, 用於分配xfrm_dst結構()
 xfrm_dst_cache = kmem_cache_create("xfrm_dst_cache",
        sizeof(struct xfrm_dst),
        0, SLAB_HWCACHE_ALIGN|SLAB_PANIC,
        NULL, NULL);
// 分配狀態HASH表, 初始是8個HASH鏈表,以後隨着策略數量的增加
// 會動態增加HASH表的數量
 hmask = 8 - 1;
 sz = (hmask+1) * sizeof(struct hlist_head);
// 該HASH表是按策略的index參數進行索引的
 xfrm_policy_byidx = xfrm_hash_alloc(sz);
 xfrm_idx_hmask = hmask;
 if (!xfrm_policy_byidx)
  panic("XFRM: failed to allocate byidx hash\n");
// 輸入, 輸出, 轉發三個處理點, 雙向
 for (dir = 0; dir < XFRM POLICY_MAX dir br style='font-size:12px;font-style:normal;font-weight:400;color:#666;' />  struct xfrm_policy_hash *htab;
// 初始化inexact鏈表頭, inexact處理選擇子相關長度不是標準值的一些特別策略
  INIT_HLIST_HEAD(&xfrm_policy_inexact[dir]);
// 分配按地址HASH的HASH表
  htab = &xfrm_policy_bydst[dir];
  htab-<table = xfrm_hash_alloc(sz);
  htab-<hmask = hmask;
  if (!htab-<table)
   panic("XFRM: failed to allocate bydst hash\n");
 }
// 初始化策略垃圾蒐集的工作隊列, 完成對策略垃圾的蒐集和釋放
 INIT_WORK(&xfrm_policy_gc_work, xfrm_policy_gc_task, NULL);
// 登記網卡通知
 register_netdevice_notifier(&xfrm_dev_notifier);
}
xfrm的網卡通知回調結構
static struct notifier_block xfrm_dev_notifier = {
 xfrm_dev_event,
 NULL,
 0
};
// 網卡通知回調函數
static int xfrm_dev_event(struct notifier_block *this, unsigned long event, void *ptr)
{
 switch (event) {
// 如果網卡down掉的話, 清除相關的所有的xfrm路由項
 case NETDEV_DOWN:
  xfrm_flush_bundles();
 }
 return NOTIFY_DONE;
}
// 清除相關的所有的xfrm路由項
static int xfrm_flush_bundles(void)
{
// 將不用的路由項刪除
 xfrm_prune_bundles(stale_bundle);
 return 0;
}
3.3 輸入初始化
/* net/xfrm/xfrm_input.c */
void __init xfrm_input_init(void)
{
// 建立一個內核cache, 用於分配sec_path結構(安全路徑)
 secpath_cachep = kmem_cache_create("secpath_cache",
        sizeof(struct sec_path),
        0, SLAB_HWCACHE_ALIGN|SLAB_PANIC,
        NULL, NULL);
}
struct sec_path結構是對輸入的加密包進行層層解包的處理, 在sk_buff中有該結構的指針sp, 如果sp非空表示這是個IPSEC解密後的包。
...... 待續 ......
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章