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的这两个函数....... 待续 ......