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

...... 待续 ......
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值