日期:2014-05-16  浏览次数:20604 次

Linux内核中PF_KEY协议族的实现(4)
本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源:http://yfydz.cublog.cn

6. 通知回调处理

6.1 初始化
在pf_key的初始化函数中定义了通知回调结构pfkeyv2_mgr:
 err = xfrm_register_km(&pfkeyv2_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,
};

6.2 登记
/*  net/xfrm/xfrm_state.c */
// 就是把xfrm_mgr结构挂接到xfrm_km_list链表
int xfrm_register_km(struct xfrm_mgr *km)
{
 write_lock_bh(&xfrm_km_lock);
// 将结构挂接到xfrm_km_list链表
 list_add_tail(&km->list, &xfrm_km_list);
 write_unlock_bh(&xfrm_km_lock);
 return 0;
}
EXPORT_SYMBOL(xfrm_register_km);

6.3 发送通知
在pf_key中调用以下两个函数来调用通知回调函数, 分别针对安全策略操作和SA操作:
// 安全策略通知回调
void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
{
 struct xfrm_mgr *km;
 read_lock(&xfrm_km_lock);
// 状态的通知回调函数为notify_policy
 list_for_each_entry(km, &xfrm_km_list, list)
  if (km->notify_policy)
   km->notify_policy(xp, dir, c);
 read_unlock(&xfrm_km_lock);
}
// SA状态通知回调
void km_state_notify(struct xfrm_state *x, struct km_event *c)
{
 struct xfrm_mgr *km;
 read_lock(&xfrm_km_lock);
// 状态的通知回调函数为notify
 list_for_each_entry(km, &xfrm_km_list, list)
  if (km->notify)
   km->notify(x, c);
 read_unlock(&xfrm_km_lock);
}

调用这些通知函数前要填写事件的相关参数, 如:
static int pfkey_flush(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
{
 unsigned proto;
 struct km_event c;
 proto = pfkey_satype2proto(hdr->sadb_msg_satype);
 if (proto == 0)
  return -EINVAL;
 xfrm_state_flush(proto);
// 填写事件参数
// 协议
 c.data.proto = proto;
// 序列号
 c.seq = hdr->sadb_msg_seq;
// sock对方(用户空间进程)的pid
 c.pid = hdr->sadb_msg_pid;
// 事件
 c.event = XFRM_MSG_FLUSHSA;
 km_state_notify(NULL, &c);
 return 0;
}

6.4 pf_key通知回调函数
6.4.1  发送SA消息时的通知回调
// 状态通知回调, 在SA操作后调用
static int pfkey_send_notify(struct xfrm_state *x, struct km_event *c)
{
// 根据事件类型来发送相关通知
 switch (c->event) {
 case XFRM_MSG_EXPIRE:
// SA到期的通知, SA消息类型为SADB_EXPIRE,只是进行了登记的PF_KEY socket可以收到
  return key_notify_sa_expire(x, c);
 case XFRM_MSG_DELSA:
 case XFRM_MSG_NEWSA:
 case XFRM_MSG_UPDSA:
// SA的通知, SA消息类型为分别为SADB_DELETE, SADB_ADD和SADB_UPDATE,
// 所有PF_KEY socket都可以收到
  return key_notify_sa(x, c);
 case XFRM_MSG_FLUSHSA:
// 删除全部SA的通知, SA消息类型为分别为SADB_FLUSH, 所有PF_KEY socket都可以收到
  return key_notify_sa_flush(c);
 case XFRM_MSG_NEWAE: /* not yet supported */
  break;
 default:
  printk("pfkey: Unknown SA event %d\n", c->event);
  break;
 }
 return 0;
}

6.4.2 发送ACQUIRE

关于ACQUIRE的作用, 摘一段UNP vol.1, r3中的话:
chapter19.5:
"When the kernel needs to communicate with a peer and policy says that an SA is required but one is not available, the kernel sends an SADB_ACQUIRE message to key management sockets that have registered the SA type required, containing a proposal extension describing the kernel's proposed algorithms and key lengths. The proposal may be a combination of what is supported by the system and preconfigured policy that limits what is permitted for this communication. The proposal is a list of algorithms, key lengths, and lifetimes, in order of preference. When a key management daemon receives an SADB_ACQUIRE message, it performs the acts required to choose a key that fits one of the kernel's proposed combinations, and installs this key in the kernel. "
 
static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *xp, int dir)
{
 struct sk_buff *skb;
 struct sadb_msg *hdr;
 struct sadb_address *addr;
 struct sadb_x_policy *pol;
 struct sockaddr_in *sin;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 struct sockaddr_in6 *sin6;
#endif
 int sockaddr_size;
 int size;
 struct sadb_x_sec_ctx *sec_ctx;
 struct xfrm_sec_ctx *xfrm_ctx;
 int ctx_size = 0;