Linux内核中PF_KEY协议族的实现(3)

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

5.10 添加/更新SPD(安全策略)
// 添加安全策略到安全策略数据库SPDB
static int pfkey_spdadd(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
{
int err = 0;
struct sadb_lifetime *lifetime;
struct sadb_address *sa;
struct sadb_x_policy *pol;
struct xfrm_policy *xp;
struct km_event c;
struct sadb_x_sec_ctx *sec_ctx;
// 错误检查, 源目的地址类型要一致, 策略信息非空
if (!present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
ext_hdrs[SADB_EXT_ADDRESS_DST-1]) ||
!ext_hdrs[SADB_X_EXT_POLICY-1])
return -EINVAL;
// 策略指针
pol = ext_hdrs[SADB_X_EXT_POLICY-1];
// 策略类型只能是DISCARD, NONE和IPSEC三者之一
if (pol->sadb_x_policy_type > IPSEC_POLICY_IPSEC)
return -EINVAL;
// 必须明确该SP作用于哪个方向的的数据: IN, OUT和FORWARD, 最后那个不是RFC标准的
if (!pol->sadb_x_policy_dir || pol->sadb_x_policy_dir >= IPSEC_DIR_MAX)
return -EINVAL;
// 分配xfrm策略
xp = xfrm_policy_alloc(GFP_KERNEL);
if (xp == NULL)
return -ENOBUFS;
// 策略的动作: 阻塞还是放行
xp->action = (pol->sadb_x_policy_type == IPSEC_POLICY_DISCARD ?
XFRM_POLICY_BLOCK : XFRM_POLICY_ALLOW);
// 策略的优先级
xp->priority = pol->sadb_x_policy_priority;
// 获取策略源地址及地址类型(v4 or v6)
sa = ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
xp->family = pfkey_sadb_addr2xfrm_addr(sa, &xp->selector.saddr);
if (!xp->family) {
err = -EINVAL;
goto out;
}
// 填充策略选择子结构参数, 选择子中包括用来辨别策略的相关参数, 用来查找匹配策略
// 协议族
xp->selector.family = xp->family;
// 地址长度
xp->selector.prefixlen_s = sa->sadb_address_prefixlen;
// 协议
xp->selector.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
// 策略中可以有上层协议的源端口参数, 不过不是必须的
xp->selector.sport = ((struct sockaddr_in *)(sa+1))->sin_port;
if (xp->selector.sport)
xp->selector.sport_mask = htons(0xffff);
// 获取策略目的地址及地址类型(v4 or v6)
// 和源地址处理类似
sa = ext_hdrs[SADB_EXT_ADDRESS_DST-1],
pfkey_sadb_addr2xfrm_addr(sa, &xp->selector.daddr);
xp->selector.prefixlen_d = sa->sadb_address_prefixlen;
/* Amusing, we set this twice. KAME apps appear to set same value
* in both addresses.
*/
xp->selector.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
xp->selector.dport = ((struct sockaddr_in *)(sa+1))->sin_port;
if (xp->selector.dport)
xp->selector.dport_mask = htons(0xffff);
// 用户定义的安全上下文
sec_ctx = (struct sadb_x_sec_ctx *) ext_hdrs[SADB_X_EXT_SEC_CTX-1];
if (sec_ctx != NULL) {
struct xfrm_user_sec_ctx *uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx);
if (!uctx) {
err = -ENOBUFS;
goto out;
}
err = security_xfrm_policy_alloc(xp, uctx);
kfree(uctx);
if (err)
goto out;
}
// lft: xfrm_lifetime_config, 生存时间配置参数
// 关于字节数和包数的软硬限制初始化
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;
// 如果在消息中定义了限制, 设置之
if ((lifetime = ext_hdrs[SADB_EXT_LIFETIME_HARD-1]) != NULL) {
xp->lft.hard_packet_limit = _KEY2X(lifetime->sadb_lifetime_allocations);
xp->lft.hard_byte_limit = _KEY2X(lifetime->sadb_lifetime_bytes);
xp->lft.hard_add_expires_seconds = lifetime->sadb_lifetime_addtime;
xp->lft.hard_use_expires_seconds = lifetime->sadb_lifetime_usetime;
}
if ((lifetime = ext_hdrs[SADB_EXT_LIFETIME_SOFT-1]) != NULL) {
xp->lft.soft_packet_limit = _KEY2X(lifetime->sadb_lifetime_allocations);
xp->lft.soft_byte_limit = _KEY2X(lifetime->sadb_lifetime_bytes);
xp->lft.soft_add_expires_seconds = lifetime->sadb_lifetime_addtime;
xp->lft.soft_use_expires_seconds = lifetime->sadb_lifetime_usetime;
}
xp->xfrm_nr = 0;
// 如果是IPSEC策略, 解析相关请求
if (pol->sadb_x_policy_type == IPSEC_POLICY_IPSEC &&
(err = parse_ipsecrequests(xp, pol)) < 0)
goto out;
// 将策略xp插入内核SPDB
err = xfrm_policy_insert(pol->sadb_x_policy_dir-1, xp,
// 一般是添加, 也可以是更新
hdr->sadb_msg_type != SADB_X_SPDUPDATE);
if (err)
goto out;
if (hdr->sadb_msg_type == SADB_X_SPDUPDATE)
c.event = XFRM_MSG_UPDPOLICY;
else
c.event = XFRM_MSG_NEWPOLICY;
c.seq = hdr->sadb_msg_seq;
c.pid = hdr->sadb_msg_pid;
// 策略通知回调处理
km_policy_notify(xp, pol->sadb_x_policy_dir-1, &c);
xfrm_pol_put(xp);
return 0;
out:
security_xfrm_policy_free(xp);
kfree(xp);
return err;
}
/* net/xfrm/xfrm_policy.c */
// 插入策略
int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
{
struct xfrm_policy *pol;
struct xfrm_policy *delpol;
struct hlist_head *chain;
struct hlist_node *entry, *newpos, *last;
struct dst_entry *gc_list;
write_lock_bh(&xfrm_policy_lock);
// 找到具体的hash链表, SPDB也是用HASH表实现的
chain = policy_hash_bysel(&policy->selector, policy->family, dir);
delpol = NULL;
newpos = NULL;
last = NULL;
// 遍历链表, 该链表是以策略的优先级值进行排序的链表, 因此需要根据新策略的优先级大小
// 将新策略插到合适的位置
hlist_for_each_entry(pol, entry, chain, bydst) {
// delpol要为空
if (!delpol &&
// 策略类型比较
pol->type == policy->type &&
// 选择子比较
!selector_cmp(&pol->selector, &policy->selector) &&
// 安全上下文比较
xfrm_sec_ctx_match(pol->security, policy->security)) {
// 找到了
if (excl) {
// 如果是添加操作, 要插入的策略在数据库中已经存在, 发生错误
write_unlock_bh(&xfrm_policy_lock);
return -EEXIST;
}
// 保存好要删除的策略位置
delpol = pol;
// 要更新的策略优先级值大于原有的优先级值, 重新循环找到合适的插入位置
// 因为这个链表是以优先级值进行排序的, 不能乱
// 现在delpol已经非空了, 前面的策略查找条件已经不可能满足了
if (policy->priority > pol->priority)
continue;
} else if (policy->priority >= pol->priority) {
// 如果新的优先级不低于当前的优先级, 保存当前节点, 继续查找合适插入位置
last = &pol->bydst;
continue;
}
// 这里是根据新策略的优先级确定的插入位置
if (!newpos)
newpos = &pol->bydst;
// 如果已经找到要删除的策略, 中断
if (delpol)
break;
last = &pol->bydst;
}
if (!newpos)
newpos = last;
// 插入策略到按目的地址HASH的链表
if (newpos)
hlist_add_after(newpos, &policy->bydst);
else
hlist_add_head(&policy->bydst, chain);
xfrm_pol_hold(policy);
xfrm_policy_count[dir]++;
atomic_inc(&flow_cache_genid);
// 如果有相同的老策略, 要从目的地址HASH和索引号HASH这两个表中删除
if (delpol) {
hlist_del(&delpol->bydst);
hlist_del(&delpol->byidx);
xfrm_policy_count[dir]--;
}
// 获取策略索引号, 插入索引HASH链表
policy->index = delpol ? delpol->index : xfrm_gen_index(policy->type, dir);
hlist_add_head(&policy->byidx, xfrm_policy_byidx+idx_hash(policy->index));
// 策略插入实际时间
policy->curlft.add_time = (unsigned long)xtime.tv_sec;
policy->curlft.use_time = 0;
if (!mod_timer(&policy->timer, jiffies + HZ))
xfrm_pol_hold(policy);
write_unlock_bh(&xfrm_policy_lock);
// 释放老策略
if (delpol)
xfrm_policy_kill(delpol);
else if (xfrm_bydst_should_resize(dir, NULL))
schedule_work(&xfrm_hash_work);
// 下面释放所有策略当前的路由cache
read_lock_bh(&xfrm_policy_lock);
gc_list = NULL;
entry = &policy->bydst;
// 遍历链表, 搜集垃圾路由cache建立链表
hlist_for_each_entry_continue(policy, entry, bydst) {
struct dst_entry *dst;
write_lock(&policy->lock);
dst = policy->bundles;
if (dst) {
struct dst_entry *tail = dst;
while (tail->next)
tail = tail->next;
tail->next = gc_list;
gc_list = dst;
policy->bundles = NULL;
}
write_unlock(&policy->lock);
}
read_unlock_bh(&xfrm_policy_lock);
// 释放垃圾路由cahce
while (gc_list) {
struct dst_entry *dst = gc_list;
gc_list = dst->next;
dst_free(dst);
}
return 0;
}
EXPORT_SYMBOL(xfrm_policy_insert);

5.11 删除SPD

static int pfkey_spddelete(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
{
int err;
struct sadb_address *sa;
struct sadb_x_policy *pol;
struct xfrm_policy *xp, tmp;
struct xfrm_selector sel;
struct km_event c;
struct sadb_x_sec_ctx *sec_ctx;
// 错误检查, 源目的地址类型要一致, 策略信息非空
if (!present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
ext_hdrs[SADB_EXT_ADDRESS_DST-1]) ||
!ext_hdrs[SADB_X_EXT_POLICY-1])
return -EINVAL;
// 消息中定义的策略
pol = ext_hdrs[SADB_X_EXT_POLICY-1];
// 策略类型合法性检查
if (!pol->sadb_x_policy_dir || pol->sadb_x_policy_dir >= IPSEC_DIR_MAX)
return -EINVAL;
memset(&sel, 0, sizeof(sel));
// 解析消息中的源地址参数填充到选择子结构中
sa = ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
sel.family = pfkey_sadb_addr2xfrm_addr(sa, &sel.saddr);
sel.prefixlen_s = sa->sadb_address_prefixlen;
sel.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
sel.sport = ((struct sockaddr_in *)(sa+1))->sin_port;
if (sel.sport)
sel.sport_mask = htons(0xffff);
// 解析消息中的目的地址参数填充到选择子结构中
sa = ext_hdrs[SADB_EXT_ADDRESS_DST-1],
pfkey_sadb_addr2xfrm_addr(sa, &sel.daddr);
sel.prefixlen_d = sa->sadb_address_prefixlen;
sel.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
sel.dport = ((struct sockaddr_in *)(sa+1))->sin_port;
if (sel.dport)
sel.dport_mask = htons(0xffff);
sec_ctx = (struct sadb_x_sec_ctx *) ext_hdrs[SADB_X_EXT_SEC_CTX-1];
memset(&tmp, 0, sizeof(struct xfrm_policy));
// 扩展的用户安全上下文信息处理
if (sec_ctx != NULL) {
struct xfrm_user_sec_ctx *uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx);
if (!uctx)
return -ENOMEM;
err = security_xfrm_policy_alloc(&tmp, uctx);
kfree(uctx);
if (err)
return err;
}
// 根据策略类型, 处理的数据方向, 选择子参数等信息查找策略
// 同时最后一个参数为1表示找到后将策略从系统的SPDB链表中断开后删除
xp = xfrm_policy_bysel_ctx(XFRM_POLICY_TYPE_MAIN, pol->sadb_x_policy_dir-1,
&sel, tmp.security, 1);
security_xfrm_policy_free(&tmp);
// 没有该策略, 出错
if (xp == NULL)
return -ENOENT;
err = 0;
if ((err = security_xfrm_policy_delete(xp)))
goto out;
c.seq = hdr->sadb_msg_seq;
c.pid = hdr->sadb_msg_pid;
c.event = XFRM_MSG_DELPOLICY;
// 反方向的策略通知回调处理
km_policy_notify(xp, pol->sadb_x_policy_dir-1, &c);
out:
// 释放策略
xfrm_pol_put(xp);
return err;
}
/* net/xfrm/xfrm_policy.c */
// 根据选择子和上下文查找策略
struct xfrm_policy *xfrm_policy_bysel_ctx(u8 type, int dir,
struct xfrm_selector *sel,
struct xfrm_sec_ctx *ctx, int delete)
{
struct xfrm_policy *pol, *ret;
struct hlist_head *chain;
struct hlist_node *entry;
write_lock_bh(&xfrm_policy_lock);
// 定位HASH表
chain = policy_hash_bysel(sel, sel->family, dir);
ret = NULL;
// 遍历链表
hlist_for_each_entry(pol, entry, chain, bydst) {
// 根据类型, 选择子和上下文进行匹配
if (pol->type == type &&
!selector_cmp(sel, &pol->selector) &&
xfrm_sec_ctx_match(ctx, pol->security)) {
xfrm_pol_hold(pol);
if (delete) {
// 要的删除话将策略节点从目的地址HASH链表和索引HASH链表中断开
hlist_del(&pol->bydst);
hlist_del(&pol->byidx);
xfrm_policy_count[dir]--;
}
ret = pol;
break;
}
}
write_unlock_bh(&xfrm_policy_lock);
if (ret && delete) {
atomic_inc(&flow_cache_genid);
// 将策略状态置为dead, 并添加到系统的策略垃圾链表进行调度处理准备删除
xfrm_policy_kill(ret);
}
return ret;
}
EXPORT_SYMBOL(xfrm_policy_bysel_ctx);

5.12 获取SPD

static int pfkey_spdget(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
{
unsigned int dir;
int err;
struct sadb_x_policy *pol;
struct xfrm_policy *xp;
struct km_event c;
// 消息中的策略头
if ((pol = ext_hdrs[SADB_X_EXT_POLICY-1]) == NULL)
return -EINVAL;
// 根据策略id判断数据方向
dir = xfrm_policy_id2dir(pol->sadb_x_policy_id);
if (dir >= XFRM_POLICY_MAX)
return -EINVAL;
// 根据方向/ID等参数来查找策略, 如果最后一个参数为真的同时删除策略
xp = xfrm_policy_byid(XFRM_POLICY_TYPE_MAIN, dir, pol->sadb_x_policy_id,
hdr->sadb_msg_type == SADB_X_SPDDELETE2);
if (xp == NULL)
return -ENOENT;
err = 0;
c.seq = hdr->sadb_msg_seq;
c.pid = hdr->sadb_msg_pid;
if (hdr->sadb_msg_type == SADB_X_SPDDELETE2) {
// 如果要删除策略, 进行通知回调
c.data.byid = 1;
c.event = XFRM_MSG_DELPOLICY;
km_policy_notify(xp, dir, &c);
} else {
// 将结果填充一个skb返回给用户空间
err = key_pol_get_resp(sk, xp, hdr, dir);
}
xfrm_pol_put(xp);
return err;
}

5.13 输出整个SPD
static int pfkey_spddump(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
{
struct pfkey_dump_data data = { .skb = skb, .hdr = hdr, .sk = sk };
// 遍历策略链表输出策略, dump_sa是输出单一SP的函数
return xfrm_policy_walk(XFRM_POLICY_TYPE_MAIN, dump_sp, &data);
}
// 输出单一SP
static int dump_sp(struct xfrm_policy *xp, int dir, int count, void *ptr)
{
struct pfkey_dump_data *data = ptr;
struct sk_buff *out_skb;
struct sadb_msg *out_hdr;
// 将安全策略填充到skb前的准备操作
out_skb = pfkey_xfrm_policy2msg_prep(xp);
if (IS_ERR(out_skb))
return PTR_ERR(out_skb);
// 将安全策略填充到skb
pfkey_xfrm_policy2msg(out_skb, xp, dir);
// 填充基本SA消息头
out_hdr = (struct sadb_msg *) out_skb->data;
out_hdr->sadb_msg_version = data->hdr->sadb_msg_version;
out_hdr->sadb_msg_type = SADB_X_SPDDUMP;
out_hdr->sadb_msg_satype = SADB_SATYPE_UNSPEC;
out_hdr->sadb_msg_errno = 0;
// SA消息的序号
out_hdr->sadb_msg_seq = count;
out_hdr->sadb_msg_pid = data->hdr->sadb_msg_pid;
// 发送到指定的sock
pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ONE, data->sk);
return 0;
}

/* net/xfrm/xfrm_policy.c */
// 遍历安全策略
int xfrm_policy_walk(u8 type, int (*func)(struct xfrm_policy *, int, int, void*),
void *data)
{
struct xfrm_policy *pol;
struct hlist_node *entry;
int dir, count, error;
read_lock_bh(&xfrm_policy_lock);
count = 0;
// 先统计符合类型的策略的数量, 方向是双向的
for (dir = 0; dir < 2*XFRM_POLICY_MAX; dir++) {
struct hlist_head *table = xfrm_policy_bydst[dir].table;
int i;
// inexact HASH表
hlist_for_each_entry(pol, entry,
&xfrm_policy_inexact[dir], bydst) {
if (pol->type == type)
count++;
}
// bydst HASH表
for (i = xfrm_policy_bydst[dir].hmask; i >= 0; i--) {
hlist_for_each_entry(pol, entry, table + i, bydst) {
if (pol->type == type)
count++;
}
}
}
if (count == 0) {
error = -ENOENT;
goto out;
}
// 重新遍历HASH表, 当前的count值作为SA的序号, 因此用户空间收到的序号是递减的
for (dir = 0; dir < 2*XFRM_POLICY_MAX; dir++) {
struct hlist_head *table = xfrm_policy_bydst[dir].table;
int i;
hlist_for_each_entry(pol, entry,
&xfrm_policy_inexact[dir], bydst) {
if (pol->type != type)
continue;
error = func(pol, dir % XFRM_POLICY_MAX, --count, data);
if (error)
goto out;
}
for (i = xfrm_policy_bydst[dir].hmask; i >= 0; i--) {
hlist_for_each_entry(pol, entry, table + i, bydst) {
if (pol->type != type)
continue;
error = func(pol, dir % XFRM_POLICY_MAX, --count, data);
if (error)
goto out;
}
}
}
error = 0;
out:
read_unlock_bh(&xfrm_policy_lock);
return error;
}
EXPORT_SYMBOL(xfrm_policy_walk);

5.14 删除全部SPD
[SADB_X_SPDFLUSH] = pfkey_spdflush,
// 删除全部SP
static int pfkey_spdflush(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
{
struct km_event c;
// 删除全部MAIN类型的SP
xfrm_policy_flush(XFRM_POLICY_TYPE_MAIN);
c.data.type = XFRM_POLICY_TYPE_MAIN;
c.event = XFRM_MSG_FLUSHPOLICY;
c.pid = hdr->sadb_msg_pid;
c.seq = hdr->sadb_msg_seq;
// 通知回调处理
km_policy_notify(NULL, 0, &c);
return 0;
}
// 删除SP回调
static int key_notify_policy_flush(struct km_event *c)
{
struct sk_buff *skb_out;
struct sadb_msg *hdr;
// 分配skb
skb_out = alloc_skb(sizeof(struct sadb_msg) + 16, GFP_ATOMIC);
if (!skb_out)
return -ENOBUFS;
hdr = (struct sadb_msg *) skb_put(skb_out, sizeof(struct sadb_msg));
// SA消息类型为删除所有SP
hdr->sadb_msg_type = SADB_X_SPDFLUSH;
hdr->sadb_msg_seq = c->seq;
hdr->sadb_msg_pid = c->pid;
hdr->sadb_msg_version = PF_KEY_V2;
hdr->sadb_msg_errno = (uint8_t) 0;
hdr->sadb_msg_len = (sizeof(struct sadb_msg) / sizeof(uint64_t));
// 广播给所有打开PF_KEY类型套接口的用户进程
pfkey_broadcast(skb_out, GFP_ATOMIC, BROADCAST_ALL, NULL);
return 0;
}
/* net/xfrm/xfrm_policy.c */
// 删除全部安全策略
void xfrm_policy_flush(u8 type)
{
int dir;
write_lock_bh(&xfrm_policy_lock);
for (dir = 0; dir < XFRM_POLICY_MAX; dir++) {
struct xfrm_policy *pol;
struct hlist_node *entry;
int i, killed;
killed = 0;
again1:
// 遍历inexact HASH链表
hlist_for_each_entry(pol, entry,
&xfrm_policy_inexact[dir], bydst) {
// 判断类型
if (pol->type != type)
continue;
// 将策略从bydst链表中断开
hlist_del(&pol->bydst);
// 将策略从byidt链表中断开
hlist_del(&pol->byidx);
write_unlock_bh(&xfrm_policy_lock);
// 将策略状态置为dead, 并添加到系统的策略垃圾链表进行调度处理准备删除
xfrm_policy_kill(pol);
killed++;
write_lock_bh(&xfrm_policy_lock);
goto again1;
}
// 遍历bydst HASH链表
for (i = xfrm_policy_bydst[dir].hmask; i >= 0; i--) {
again2:
hlist_for_each_entry(pol, entry,
xfrm_policy_bydst[dir].table + i,
bydst) {
if (pol->type != type)
continue;
// 将节点从链表中断开
hlist_del(&pol->bydst);
hlist_del(&pol->byidx);
write_unlock_bh(&xfrm_policy_lock);
// 释放节点
xfrm_policy_kill(pol);
killed++;
write_lock_bh(&xfrm_policy_lock);
goto again2;
}
}
xfrm_policy_count[dir] -= killed;
}
atomic_inc(&flow_cache_genid);
write_unlock_bh(&xfrm_policy_lock);
}
EXPORT_SYMBOL(xfrm_policy_flush);

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值