`

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;
 
 sockaddr_size = pfkey_sockaddr_size(x->props.family);
 if (!sockaddr_size)
  return -EINVAL;
// 消息长度包括SA基本头, 两个SA地址, 两个网络地址和策略
 size = sizeof(struct sadb_msg) +
  (sizeof(struct sadb_address) * 2) +
  (sockaddr_size * 2) +
  sizeof(struct sadb_x_policy);
// 还添加AH或ESP中所有算法描述的长度
 if (x->id.proto == IPPROTO_AH)
  size += count_ah_combs(t);
 else if (x->id.proto == IPPROTO_ESP)
  size += count_esp_combs(t);
 if ((xfrm_ctx = x->security)) {
  ctx_size = PFKEY_ALIGN8(xfrm_ctx->ctx_len);
  size +=  sizeof(struct sadb_x_sec_ctx) + ctx_size;
 }
// 分配skb
 skb =  alloc_skb(size + 16, GFP_ATOMIC);
 if (skb == NULL)
  return -ENOMEM;
// 先填基本SA消息头信息 
 hdr = (struct sadb_msg *) skb_put(skb, sizeof(struct sadb_msg));
 hdr->sadb_msg_version = PF_KEY_V2;
 hdr->sadb_msg_type = SADB_ACQUIRE;
 hdr->sadb_msg_satype = pfkey_proto2satype(x->id.proto);
 hdr->sadb_msg_len = size / sizeof(uint64_t);
 hdr->sadb_msg_errno = 0;
 hdr->sadb_msg_reserved = 0;
 hdr->sadb_msg_seq = x->km.seq = get_acqseq();
 hdr->sadb_msg_pid = 0;
 /* src address */
// 填充SA源地址信息
 addr = (struct sadb_address*) skb_put(skb,
           sizeof(struct sadb_address)+sockaddr_size);
 addr->sadb_address_len =
  (sizeof(struct sadb_address)+sockaddr_size)/
   sizeof(uint64_t);
 addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
 addr->sadb_address_proto = 0;
 addr->sadb_address_reserved = 0;
 if (x->props.family == AF_INET) {
// IPV4地址
  addr->sadb_address_prefixlen = 32;
  sin = (struct sockaddr_in *) (addr + 1);
  sin->sin_family = AF_INET;
  sin->sin_addr.s_addr = x->props.saddr.a4;
  sin->sin_port = 0;
  memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
 }
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 else if (x->props.family == AF_INET6) {
// IPV6地址
  addr->sadb_address_prefixlen = 128;
  sin6 = (struct sockaddr_in6 *) (addr + 1);
  sin6->sin6_family = AF_INET6;
  sin6->sin6_port = 0;
  sin6->sin6_flowinfo = 0;
  memcpy(&sin6->sin6_addr,
         x->props.saddr.a6, sizeof(struct in6_addr));
  sin6->sin6_scope_id = 0;
 }
#endif
 else
  BUG();
 
 /* dst address */
// 填充SA目的地址信息
 addr = (struct sadb_address*) skb_put(skb,
           sizeof(struct sadb_address)+sockaddr_size);
 addr->sadb_address_len =
  (sizeof(struct sadb_address)+sockaddr_size)/
   sizeof(uint64_t);
 addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
 addr->sadb_address_proto = 0;
 addr->sadb_address_reserved = 0;
 if (x->props.family == AF_INET) {
  addr->sadb_address_prefixlen = 32;
  sin = (struct sockaddr_in *) (addr + 1);
  sin->sin_family = AF_INET;
  sin->sin_addr.s_addr = x->id.daddr.a4;
  sin->sin_port = 0;
  memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
 }
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 else if (x->props.family == AF_INET6) {
  addr->sadb_address_prefixlen = 128;
  sin6 = (struct sockaddr_in6 *) (addr + 1);
  sin6->sin6_family = AF_INET6;
  sin6->sin6_port = 0;
  sin6->sin6_flowinfo = 0;
  memcpy(&sin6->sin6_addr,
         x->id.daddr.a6, sizeof(struct in6_addr));
  sin6->sin6_scope_id = 0;
 }
#endif
 else
  BUG();
// 填充策略信息
 pol = (struct sadb_x_policy *)  skb_put(skb, sizeof(struct sadb_x_policy));
 pol->sadb_x_policy_len = sizeof(struct sadb_x_policy)/sizeof(uint64_t);
 pol->sadb_x_policy_exttype = SADB_X_EXT_POLICY;
 pol->sadb_x_policy_type = IPSEC_POLICY_IPSEC;
 pol->sadb_x_policy_dir = dir+1;
 pol->sadb_x_policy_id = xp->index;
 /* Set sadb_comb's. */
// 填充AH或ESP的可用算法信息
 if (x->id.proto == IPPROTO_AH)
  dump_ah_combs(skb, t);
 else if (x->id.proto == IPPROTO_ESP)
  dump_esp_combs(skb, t);
 /* security context */
 if (xfrm_ctx) {
// 填充安全上下文信息
  sec_ctx = (struct sadb_x_sec_ctx *) skb_put(skb,
    sizeof(struct sadb_x_sec_ctx) + ctx_size);
  sec_ctx->sadb_x_sec_len =
    (sizeof(struct sadb_x_sec_ctx) + ctx_size) / sizeof(uint64_t);
  sec_ctx->sadb_x_sec_exttype = SADB_X_EXT_SEC_CTX;
  sec_ctx->sadb_x_ctx_doi = xfrm_ctx->ctx_doi;
  sec_ctx->sadb_x_ctx_alg = xfrm_ctx->ctx_alg;
  sec_ctx->sadb_x_ctx_len = xfrm_ctx->ctx_len;
  memcpy(sec_ctx + 1, xfrm_ctx->ctx_str,
         xfrm_ctx->ctx_len);
 }
// 广播到进行了登记的PF_KEY套接口
 return pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL);
}

6.4.3 编译策略
// 将sadb_x_policy(标准接口格式)编译为xfrm_policy(内核具体实现格式)
static struct xfrm_policy *pfkey_compile_policy(struct sock *sk, int opt,
                                                u8 *data, int len, int *dir)
{
 struct xfrm_policy *xp;
 struct sadb_x_policy *pol = (struct sadb_x_policy*)data;
 struct sadb_x_sec_ctx *sec_ctx;
// 选项opt必须是IP_IPSEC_POLICY, 否则出错
 switch (sk->sk_family) {
 case AF_INET:
  if (opt != IP_IPSEC_POLICY) {
   *dir = -EOPNOTSUPP;
   return NULL;
  }
  break;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 case AF_INET6:
  if (opt != IPV6_IPSEC_POLICY) {
   *dir = -EOPNOTSUPP;
   return NULL;
  }
  break;
#endif
 default:
  *dir = -EINVAL;
  return NULL;
 }
 *dir = -EINVAL;
 if (len < sizeof(struct sadb_x_policy) ||
     pol->sadb_x_policy_len*8 > len ||
     pol->sadb_x_policy_type > IPSEC_POLICY_BYPASS ||
     (!pol->sadb_x_policy_dir || pol->sadb_x_policy_dir > IPSEC_DIR_OUTBOUND))
  return NULL;
// 分配策略空间
 xp = xfrm_policy_alloc(GFP_ATOMIC);
 if (xp == NULL) {
  *dir = -ENOBUFS;
  return NULL;
 }
// 填写策略动作
 xp->action = (pol->sadb_x_policy_type == IPSEC_POLICY_DISCARD ?
        XFRM_POLICY_BLOCK : XFRM_POLICY_ALLOW);
// 填写策略有效期参数
 xp->lft.soft_byte_limit = XFRM_INF;
 xp->lft.hard_byte_limit = XFRM_INF;
 xp->lft.soft_packet_limit = XFRM_INF;
 xp->lft.hard_packet_limit = XFRM_INF;
 xp->family = sk->sk_family;
 xp->xfrm_nr = 0;
// 解析ipsec请求
 if (pol->sadb_x_policy_type == IPSEC_POLICY_IPSEC &&
     (*dir = parse_ipsecrequests(xp, pol)) < 0)
  goto out;
 /* security context too */
 if (len >= (pol->sadb_x_policy_len*8 +
     sizeof(struct sadb_x_sec_ctx))) {
// 转换安全上下文
  char *p = (char *)pol;
  struct xfrm_user_sec_ctx *uctx;
  p += pol->sadb_x_policy_len*8;
  sec_ctx = (struct sadb_x_sec_ctx *)p;
  if (len < pol->sadb_x_policy_len*8 +
      sec_ctx->sadb_x_sec_len) {
   *dir = -EINVAL;
   goto out;
  }
  if ((*dir = verify_sec_ctx_len(p)))
   goto out;
  uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx);
  *dir = security_xfrm_policy_alloc(xp, uctx);
  kfree(uctx);
  if (*dir)
   goto out;
 }
 *dir = pol->sadb_x_policy_dir-1;
 return xp;
out:
 security_xfrm_policy_free(xp);
 kfree(xp);
 return NULL;
}
6.4.4 NAT穿越映射
// 发送新映射(NAT穿越)SA消息, 包含SA头, SA, 转换前地址,端口, 转换后的地址端口
static int pfkey_send_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, u16 sport)
{
 struct sk_buff *skb;
 struct sadb_msg *hdr;
 struct sadb_sa *sa;
 struct sadb_address *addr;
 struct sadb_x_nat_t_port *n_port;
 struct sockaddr_in *sin;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 struct sockaddr_in6 *sin6;
#endif
 int sockaddr_size;
 int size;
 __u8 satype = (x->id.proto == IPPROTO_ESP ? SADB_SATYPE_ESP : 0);
 struct xfrm_encap_tmpl *natt = NULL;
// 协议的地址长度
 sockaddr_size = pfkey_sockaddr_size(x->props.family);
 if (!sockaddr_size)
  return -EINVAL;
 if (!satype)
  return -EINVAL;
 if (!x->encap)
  return -EINVAL;
// NAT转换结构
 natt = x->encap;
 /* Build an SADB_X_NAT_T_NEW_MAPPING message:
  *
  * HDR | SA | ADDRESS_SRC (old addr) | NAT_T_SPORT (old port) |
  * ADDRESS_DST (new addr) | NAT_T_DPORT (new port)
  */
// 消息总长: SA消息头+SA+两个SA地址+两个协议地址+2个NAT端口 
 size = sizeof(struct sadb_msg) +
  sizeof(struct sadb_sa) +
  (sizeof(struct sadb_address) * 2) +
  (sockaddr_size * 2) +
  (sizeof(struct sadb_x_nat_t_port) * 2);
// 分配skb 
 skb =  alloc_skb(size + 16, GFP_ATOMIC);
 if (skb == NULL)
  return -ENOMEM;
// 填写SA头 
 hdr = (struct sadb_msg *) skb_put(skb, sizeof(struct sadb_msg));
 hdr->sadb_msg_version = PF_KEY_V2;
 hdr->sadb_msg_type = SADB_X_NAT_T_NEW_MAPPING;
 hdr->sadb_msg_satype = satype;
 hdr->sadb_msg_len = size / sizeof(uint64_t);
 hdr->sadb_msg_errno = 0;
 hdr->sadb_msg_reserved = 0;
 hdr->sadb_msg_seq = x->km.seq = get_acqseq();
 hdr->sadb_msg_pid = 0;
 /* SA */
// 填写SA结构
 sa = (struct sadb_sa *) skb_put(skb, sizeof(struct sadb_sa));
 sa->sadb_sa_len = sizeof(struct sadb_sa)/sizeof(uint64_t);
 sa->sadb_sa_exttype = SADB_EXT_SA;
 sa->sadb_sa_spi = x->id.spi;
 sa->sadb_sa_replay = 0;
 sa->sadb_sa_state = 0;
 sa->sadb_sa_auth = 0;
 sa->sadb_sa_encrypt = 0;
 sa->sadb_sa_flags = 0;
 /* ADDRESS_SRC (old addr) */
// 转换前SA地址参数
 addr = (struct sadb_address*)
  skb_put(skb, sizeof(struct sadb_address)+sockaddr_size);
 addr->sadb_address_len =
  (sizeof(struct sadb_address)+sockaddr_size)/
   sizeof(uint64_t);
 addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
 addr->sadb_address_proto = 0;
 addr->sadb_address_reserved = 0;
 if (x->props.family == AF_INET) {
// 填写转换前IPV4协议地址具体参数
  addr->sadb_address_prefixlen = 32;
  sin = (struct sockaddr_in *) (addr + 1);
  sin->sin_family = AF_INET;
  sin->sin_addr.s_addr = x->props.saddr.a4;
  sin->sin_port = 0;
  memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
 }
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 else if (x->props.family == AF_INET6) {
// 填写转换前IPV6协议地址具体参数
  addr->sadb_address_prefixlen = 128;
  sin6 = (struct sockaddr_in6 *) (addr + 1);
  sin6->sin6_family = AF_INET6;
  sin6->sin6_port = 0;
  sin6->sin6_flowinfo = 0;
  memcpy(&sin6->sin6_addr,
         x->props.saddr.a6, sizeof(struct in6_addr));
  sin6->sin6_scope_id = 0;
 }
#endif
 else
  BUG();
 /* NAT_T_SPORT (old port) */
// 填写转换前端口参数
 n_port = (struct sadb_x_nat_t_port*) skb_put(skb, sizeof (*n_port));
 n_port->sadb_x_nat_t_port_len = sizeof(*n_port)/sizeof(uint64_t);
 n_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_SPORT;
 n_port->sadb_x_nat_t_port_port = natt->encap_sport;
 n_port->sadb_x_nat_t_port_reserved = 0;
 /* ADDRESS_DST (new addr) */
// 填写转换后地址属性参数
 addr = (struct sadb_address*)
  skb_put(skb, sizeof(struct sadb_address)+sockaddr_size);
 addr->sadb_address_len =
  (sizeof(struct sadb_address)+sockaddr_size)/
   sizeof(uint64_t);
 addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
 addr->sadb_address_proto = 0;
 addr->sadb_address_reserved = 0;
 if (x->props.family == AF_INET) {
// 填写转换后IPV4协议地址具体参数
  addr->sadb_address_prefixlen = 32;
  sin = (struct sockaddr_in *) (addr + 1);
  sin->sin_family = AF_INET;
  sin->sin_addr.s_addr = ipaddr->a4;
  sin->sin_port = 0;
  memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
 }
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 else if (x->props.family == AF_INET6) {
// 填写转换后IPV6协议地址具体参数
  addr->sadb_address_prefixlen = 128;
  sin6 = (struct sockaddr_in6 *) (addr + 1);
  sin6->sin6_family = AF_INET6;
  sin6->sin6_port = 0;
  sin6->sin6_flowinfo = 0;
  memcpy(&sin6->sin6_addr, &ipaddr->a6, sizeof(struct in6_addr));
  sin6->sin6_scope_id = 0;
 }
#endif
 else
  BUG();
 /* NAT_T_DPORT (new port) */
// 填写转换前端口参数
 n_port = (struct sadb_x_nat_t_port*) skb_put(skb, sizeof (*n_port));
 n_port->sadb_x_nat_t_port_len = sizeof(*n_port)/sizeof(uint64_t);
 n_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_DPORT;
 n_port->sadb_x_nat_t_port_port = sport;
 n_port->sadb_x_nat_t_port_reserved = 0;
// 发送给进行登记了的PF_KEY套接口
 return pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL);
}

6.4.5 发送策略的通知

// 策略通知回调, 在安全策略操作后调用
static int pfkey_send_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
{
 if (xp && xp->type != XFRM_POLICY_TYPE_MAIN)
  return 0;
 switch (c->event) {
 case XFRM_MSG_POLEXPIRE:
// 策略到期通知, 空函数
  return key_notify_policy_expire(xp, c);
 case XFRM_MSG_DELPOLICY:
 case XFRM_MSG_NEWPOLICY:
 case XFRM_MSG_UPDPOLICY:
// 策略的通知, SA消息类型为分别为SADB_X_SPDDELETE, SADB_X_SPDADD, SADB_X_SPDUPDATE等,
// 所有PF_KEY socket都可以收到
  return key_notify_policy(xp, dir, c);
 case XFRM_MSG_FLUSHPOLICY:
  if (c->data.type != XFRM_POLICY_TYPE_MAIN)
   break;
// 策略的通知, SA消息类型为分别为SADB_X_FLUSH, 所有PF_KEY socket都可以收到
  return key_notify_policy_flush(c);
 default:
  printk("pfkey: Unknown policy event %d\n", c->event);
  break;
 }
 return 0;
}

在pf_key.c中只用到了6.4.1和6.4.5的这两个函数.

...... 待续 ......
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics