- 浏览: 266038 次
- 性别:
- 来自: 吉林
-
文章分类
最新评论
-
hhb19900618:
你好 请问怎么处理 客户端 socket发送数据超时后的崩溃问 ...
c socket 编程(TCP服务器端代码) -
gar_odie:
挺好的,谢谢露珠
jquery table分页插件 -
a545807638:
...
jquery table分页插件 -
a06062125:
挺好!值得学习
二叉查找树 --c代码 -
a06062125:
只有代码没有讲解 这也算日志
kmp 算法 --转
本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源:http://yfydz.cublog.cn
5. 安全策略(xfrm_policy)处理
本节所介绍的函数都在net/xfrm/xfrm_policy.c中定义。
5.1 策略分配
策略分配函数为xfrm_policy_alloc(), 该函数被pfkey_spdadd()函数调用
struct xfrm_policy *xfrm_policy_alloc(gfp_t gfp)
{
struct xfrm_policy *policy;
// 分配struct xfrm_policy结构空间并清零
policy = kzalloc(sizeof(struct xfrm_policy), gfp);
policy = kzalloc(sizeof(struct xfrm_policy), gfp);
if (policy) {
// 初始化链接节点
INIT_HLIST_NODE(&policy->bydst);
INIT_HLIST_NODE(&policy->byidx);
// 初始化锁
rwlock_init(&policy->lock);
// 策略引用计数初始化为1
atomic_set(&policy->refcnt, 1);
// 初始化定时器
init_timer(&policy->timer);
policy->timer.data = (unsigned long)policy;
policy->timer.function = xfrm_policy_timer;
}
return policy;
}
EXPORT_SYMBOL(xfrm_policy_alloc);
// 初始化链接节点
INIT_HLIST_NODE(&policy->bydst);
INIT_HLIST_NODE(&policy->byidx);
// 初始化锁
rwlock_init(&policy->lock);
// 策略引用计数初始化为1
atomic_set(&policy->refcnt, 1);
// 初始化定时器
init_timer(&policy->timer);
policy->timer.data = (unsigned long)policy;
policy->timer.function = xfrm_policy_timer;
}
return policy;
}
EXPORT_SYMBOL(xfrm_policy_alloc);
定时器函数:
static void xfrm_policy_timer(unsigned long data)
{
struct xfrm_policy *xp = (struct xfrm_policy*)data;
unsigned long now = (unsigned long)xtime.tv_sec;
long next = LONG_MAX;
int warn = 0;
int dir;
// 加锁
read_lock(&xp->lock);
// 如果策略已经是死的, 退出
if (xp->dead)
goto out;
{
struct xfrm_policy *xp = (struct xfrm_policy*)data;
unsigned long now = (unsigned long)xtime.tv_sec;
long next = LONG_MAX;
int warn = 0;
int dir;
// 加锁
read_lock(&xp->lock);
// 如果策略已经是死的, 退出
if (xp->dead)
goto out;
// 根据策略索引号确定策略处理的数据的方向, 看索引号的后3位
dir = xfrm_policy_id2dir(xp->index);
dir = xfrm_policy_id2dir(xp->index);
// 如果到期了还要强制要增加一些时间
if (xp->lft.hard_add_expires_seconds) {
// 计算强制增加的超时时间
long tmo = xp->lft.hard_add_expires_seconds +
xp->curlft.add_time - now;
// 没法增加超时了, 到期
if (tmo <= 0)
goto expired;
if (tmo < next)
next = tmo;
}
// 如果到期了还要强制要增加的使用时间
if (xp->lft.hard_use_expires_seconds) {
// 计算强制增加的使用时间
long tmo = xp->lft.hard_use_expires_seconds +
(xp->curlft.use_time ? : xp->curlft.add_time) - now;
// 没法增加超时了, 到期
if (tmo <= 0)
goto expired;
if (tmo < next)
next = tmo;
}
// 如果到期了还要软性要增加一些时间
if (xp->lft.soft_add_expires_seconds) {
// 计算软性增加的时间
long tmo = xp->lft.soft_add_expires_seconds +
xp->curlft.add_time - now;
// 软性增加超时小于0, 设置报警标志, 并将超时设置为XFRM_KM_TIMEOUT, 这点和其他不同
if (tmo <= 0) {
warn = 1;
tmo = XFRM_KM_TIMEOUT;
}
if (tmo < next)
next = tmo;
}
// 如果到期了还要软性要增加的使用时间
if (xp->lft.soft_use_expires_seconds) {
// 计算软性增加的使用时间
long tmo = xp->lft.soft_use_expires_seconds +
(xp->curlft.use_time ? : xp->curlft.add_time) - now;
// 软性增加超时小于0, 设置报警标志, 并将超时设置为XFRM_KM_TIMEOUT, 这点和其他不同
if (tmo <= 0) {
warn = 1;
tmo = XFRM_KM_TIMEOUT;
}
if (tmo < next)
next = tmo;
}
// 需要报警, 调用到期回调
if (warn)
km_policy_expired(xp, dir, 0, 0);
// 如果更新的超时值有效, 修改定时器超时, 增加策略使用计数
if (next != LONG_MAX &&
!mod_timer(&xp->timer, jiffies + make_jiffies(next)))
xfrm_pol_hold(xp);
if (xp->lft.hard_add_expires_seconds) {
// 计算强制增加的超时时间
long tmo = xp->lft.hard_add_expires_seconds +
xp->curlft.add_time - now;
// 没法增加超时了, 到期
if (tmo <= 0)
goto expired;
if (tmo < next)
next = tmo;
}
// 如果到期了还要强制要增加的使用时间
if (xp->lft.hard_use_expires_seconds) {
// 计算强制增加的使用时间
long tmo = xp->lft.hard_use_expires_seconds +
(xp->curlft.use_time ? : xp->curlft.add_time) - now;
// 没法增加超时了, 到期
if (tmo <= 0)
goto expired;
if (tmo < next)
next = tmo;
}
// 如果到期了还要软性要增加一些时间
if (xp->lft.soft_add_expires_seconds) {
// 计算软性增加的时间
long tmo = xp->lft.soft_add_expires_seconds +
xp->curlft.add_time - now;
// 软性增加超时小于0, 设置报警标志, 并将超时设置为XFRM_KM_TIMEOUT, 这点和其他不同
if (tmo <= 0) {
warn = 1;
tmo = XFRM_KM_TIMEOUT;
}
if (tmo < next)
next = tmo;
}
// 如果到期了还要软性要增加的使用时间
if (xp->lft.soft_use_expires_seconds) {
// 计算软性增加的使用时间
long tmo = xp->lft.soft_use_expires_seconds +
(xp->curlft.use_time ? : xp->curlft.add_time) - now;
// 软性增加超时小于0, 设置报警标志, 并将超时设置为XFRM_KM_TIMEOUT, 这点和其他不同
if (tmo <= 0) {
warn = 1;
tmo = XFRM_KM_TIMEOUT;
}
if (tmo < next)
next = tmo;
}
// 需要报警, 调用到期回调
if (warn)
km_policy_expired(xp, dir, 0, 0);
// 如果更新的超时值有效, 修改定时器超时, 增加策略使用计数
if (next != LONG_MAX &&
!mod_timer(&xp->timer, jiffies + make_jiffies(next)))
xfrm_pol_hold(xp);
out:
read_unlock(&xp->lock);
xfrm_pol_put(xp);
return;
read_unlock(&xp->lock);
xfrm_pol_put(xp);
return;
expired:
read_unlock(&xp->lock);
// 如果确实到期, 删除策略
if (!xfrm_policy_delete(xp, dir))
// 1表示是硬性到期了
km_policy_expired(xp, dir, 1, 0);
xfrm_pol_put(xp);
}
read_unlock(&xp->lock);
// 如果确实到期, 删除策略
if (!xfrm_policy_delete(xp, dir))
// 1表示是硬性到期了
km_policy_expired(xp, dir, 1, 0);
xfrm_pol_put(xp);
}
5.2 策略插入
策略插入函数为xfrm_policy_insert(), 该函数被pfkey_spdadd()函数调用, 注意策略链表是按优先权大小进行排序的有序链表, 因此插入策略时要进行优先权比较后插入到合适的位置.
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链表
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);
// 该方向的策略数增1
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);
// 找到具体的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);
// 该方向的策略数增1
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);
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;
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;
// 策略的路由链表头
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);
}
dst_free(dst);
}
return 0;
}
EXPORT_SYMBOL(xfrm_policy_insert);
}
EXPORT_SYMBOL(xfrm_policy_insert);
5.3 删除某类型的全部安全策略
该函数被pfkey_spdflush()等函数调用
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;
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++;
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;
}
// 遍历所有目的HASH链表
for (i = xfrm_policy_bydst[dir].hmask; i >= 0; i--) {
again2:
// 遍历按目的地址HASH的链表
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++;
goto again1;
}
// 遍历所有目的HASH链表
for (i = xfrm_policy_bydst[dir].hmask; i >= 0; i--) {
again2:
// 遍历按目的地址HASH的链表
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;
}
}
goto again2;
}
}
xfrm_policy_count[dir] -= killed;
}
atomic_inc(&flow_cache_genid);
write_unlock_bh(&xfrm_policy_lock);
}
EXPORT_SYMBOL(xfrm_policy_flush);
}
atomic_inc(&flow_cache_genid);
write_unlock_bh(&xfrm_policy_lock);
}
EXPORT_SYMBOL(xfrm_policy_flush);
/* Rule must be locked. Release descentant resources, announce
* entry dead. The rule must be unlinked from lists to the moment.
*/
// 策略释放到垃圾链表
static void xfrm_policy_kill(struct xfrm_policy *policy)
{
int dead;
* entry dead. The rule must be unlinked from lists to the moment.
*/
// 策略释放到垃圾链表
static void xfrm_policy_kill(struct xfrm_policy *policy)
{
int dead;
write_lock_bh(&policy->lock);
// 保留老的DEAD标志
dead = policy->dead;
// 设置策略DEAD标志
policy->dead = 1;
write_unlock_bh(&policy->lock);
// 保留老的DEAD标志
dead = policy->dead;
// 设置策略DEAD标志
policy->dead = 1;
write_unlock_bh(&policy->lock);
// 为什么不在前面判断DEAD呢?
if (unlikely(dead)) {
WARN_ON(1);
return;
}
if (unlikely(dead)) {
WARN_ON(1);
return;
}
spin_lock(&xfrm_policy_gc_lock);
// 将该策略节点从当前链表断开, 插入策略垃圾链表
hlist_add_head(&policy->bydst, &xfrm_policy_gc_list);
spin_unlock(&xfrm_policy_gc_lock);
// 调度策略垃圾策略工作结构
schedule_work(&xfrm_policy_gc_work);
}
// 将该策略节点从当前链表断开, 插入策略垃圾链表
hlist_add_head(&policy->bydst, &xfrm_policy_gc_list);
spin_unlock(&xfrm_policy_gc_lock);
// 调度策略垃圾策略工作结构
schedule_work(&xfrm_policy_gc_work);
}
5.4 策略查找
5.4.1 策略查找并删除
根据选择子和安全上下文查找策略, 可查找策略并删除, 被pfkey_spddelete()函数调用
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);
// 定位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) {
// 增加genid
atomic_inc(&flow_cache_genid);
// 将策略状态置为dead, 并添加到系统的策略垃圾链表进行调度处理准备删除
xfrm_policy_kill(ret);
}
return ret;
}
EXPORT_SYMBOL(xfrm_policy_bysel_ctx);
// 增加genid
atomic_inc(&flow_cache_genid);
// 将策略状态置为dead, 并添加到系统的策略垃圾链表进行调度处理准备删除
xfrm_policy_kill(ret);
}
return ret;
}
EXPORT_SYMBOL(xfrm_policy_bysel_ctx);
5.4.2 按索引号查找并删除
struct xfrm_policy *xfrm_policy_byid(u8 type, int dir, u32 id, int delete)
{
struct xfrm_policy *pol, *ret;
struct hlist_head *chain;
struct hlist_node *entry;
write_lock_bh(&xfrm_policy_lock);
// 根据索引号定位链表
chain = xfrm_policy_byidx + idx_hash(id);
ret = NULL;
// 遍历链表
hlist_for_each_entry(pol, entry, chain, byidx) {
// 策略的类型和索引号相同
if (pol->type == type && pol->index == id) {
xfrm_pol_hold(pol);
// 如果要删除, 将策略节点从链表中删除
if (delete) {
hlist_del(&pol->bydst);
hlist_del(&pol->byidx);
xfrm_policy_count[dir]--;
}
ret = pol;
break;
}
}
write_unlock_bh(&xfrm_policy_lock);
// 根据索引号定位链表
chain = xfrm_policy_byidx + idx_hash(id);
ret = NULL;
// 遍历链表
hlist_for_each_entry(pol, entry, chain, byidx) {
// 策略的类型和索引号相同
if (pol->type == type && pol->index == id) {
xfrm_pol_hold(pol);
// 如果要删除, 将策略节点从链表中删除
if (delete) {
hlist_del(&pol->bydst);
hlist_del(&pol->byidx);
xfrm_policy_count[dir]--;
}
ret = pol;
break;
}
}
write_unlock_bh(&xfrm_policy_lock);
if (ret && delete) {
// 增加genid
atomic_inc(&flow_cache_genid);
// 将策略状态置为dead, 并添加到系统的策略垃圾链表进行调度处理准备删除
xfrm_policy_kill(ret);
}
return ret;
}
EXPORT_SYMBOL(xfrm_policy_byid);
// 增加genid
atomic_inc(&flow_cache_genid);
// 将策略状态置为dead, 并添加到系统的策略垃圾链表进行调度处理准备删除
xfrm_policy_kill(ret);
}
return ret;
}
EXPORT_SYMBOL(xfrm_policy_byid);
5.4.3 根据路由查找策略
// 参数fl是路由相关的结构, 常用于路由查找中
// 注意返回值是整数, 0成功, 非0失败, 找到的策略通过参数objp进行传递
static int xfrm_policy_lookup(struct flowi *fl, u16 family, u8 dir,
void **objp, atomic_t **obj_refp)
{
struct xfrm_policy *pol;
int err = 0;
#ifdef CONFIG_XFRM_SUB_POLICY
// 子策略查找, 属于Linux自己的扩展功能, 非标准功能
pol = xfrm_policy_lookup_bytype(XFRM_POLICY_TYPE_SUB, fl, family, dir);
if (IS_ERR(pol)) {
err = PTR_ERR(pol);
pol = NULL;
}
if (pol || err)
goto end;
#endif
// 查找MAIN类型的策略
pol = xfrm_policy_lookup_bytype(XFRM_POLICY_TYPE_MAIN, fl, family, dir);
if (IS_ERR(pol)) {
err = PTR_ERR(pol);
pol = NULL;
}
#ifdef CONFIG_XFRM_SUB_POLICY
end:
#endif
// 将找到的策略赋值给objp返回
if ((*objp = (void *) pol) != NULL)
*obj_refp = &pol->refcnt;
return err;
}
// 子策略查找, 属于Linux自己的扩展功能, 非标准功能
pol = xfrm_policy_lookup_bytype(XFRM_POLICY_TYPE_SUB, fl, family, dir);
if (IS_ERR(pol)) {
err = PTR_ERR(pol);
pol = NULL;
}
if (pol || err)
goto end;
#endif
// 查找MAIN类型的策略
pol = xfrm_policy_lookup_bytype(XFRM_POLICY_TYPE_MAIN, fl, family, dir);
if (IS_ERR(pol)) {
err = PTR_ERR(pol);
pol = NULL;
}
#ifdef CONFIG_XFRM_SUB_POLICY
end:
#endif
// 将找到的策略赋值给objp返回
if ((*objp = (void *) pol) != NULL)
*obj_refp = &pol->refcnt;
return err;
}
// 按类型查找策略
static struct xfrm_policy *xfrm_policy_lookup_bytype(u8 type, struct flowi *fl,
u16 family, u8 dir)
{
int err;
struct xfrm_policy *pol, *ret;
xfrm_address_t *daddr, *saddr;
struct hlist_node *entry;
struct hlist_head *chain;
u32 priority = ~0U;
// 由流结构的目的和源地址
daddr = xfrm_flowi_daddr(fl, family);
saddr = xfrm_flowi_saddr(fl, family);
if (unlikely(!daddr || !saddr))
return NULL;
daddr = xfrm_flowi_daddr(fl, family);
saddr = xfrm_flowi_saddr(fl, family);
if (unlikely(!daddr || !saddr))
return NULL;
read_lock_bh(&xfrm_policy_lock);
// 根据地址信息查找HASH链表
chain = policy_hash_direct(daddr, saddr, family, dir);
ret = NULL;
// 循环HASH链表
hlist_for_each_entry(pol, entry, chain, bydst) {
// 检查流结构,类型和协议族是否匹配策略, 返回0表示匹配
err = xfrm_policy_match(pol, fl, type, family, dir);
if (err) {
if (err == -ESRCH)
continue;
else {
ret = ERR_PTR(err);
goto fail;
}
} else {
// 备份找到的策略和优先级
ret = pol;
priority = ret->priority;
break;
}
}
// 再在inexact链表中查找策略, 如果也找到策略, 而且优先级更小,
// 将新找到的策略替代前面找到的策略
chain = &xfrm_policy_inexact[dir];
// 循环HASH链表
hlist_for_each_entry(pol, entry, chain, bydst) {
// 检查流结构,类型和协议族是否匹配策略, 返回0表示匹配
err = xfrm_policy_match(pol, fl, type, family, dir);
if (err) {
if (err == -ESRCH)
continue;
else {
ret = ERR_PTR(err);
goto fail;
}
} else if (pol->priority < priority) {
// 如果新找到的策略优先级更小, 将其取代原来找到的策略
ret = pol;
break;
}
}
if (ret)
xfrm_pol_hold(ret);
fail:
read_unlock_bh(&xfrm_policy_lock);
// 根据地址信息查找HASH链表
chain = policy_hash_direct(daddr, saddr, family, dir);
ret = NULL;
// 循环HASH链表
hlist_for_each_entry(pol, entry, chain, bydst) {
// 检查流结构,类型和协议族是否匹配策略, 返回0表示匹配
err = xfrm_policy_match(pol, fl, type, family, dir);
if (err) {
if (err == -ESRCH)
continue;
else {
ret = ERR_PTR(err);
goto fail;
}
} else {
// 备份找到的策略和优先级
ret = pol;
priority = ret->priority;
break;
}
}
// 再在inexact链表中查找策略, 如果也找到策略, 而且优先级更小,
// 将新找到的策略替代前面找到的策略
chain = &xfrm_policy_inexact[dir];
// 循环HASH链表
hlist_for_each_entry(pol, entry, chain, bydst) {
// 检查流结构,类型和协议族是否匹配策略, 返回0表示匹配
err = xfrm_policy_match(pol, fl, type, family, dir);
if (err) {
if (err == -ESRCH)
continue;
else {
ret = ERR_PTR(err);
goto fail;
}
} else if (pol->priority < priority) {
// 如果新找到的策略优先级更小, 将其取代原来找到的策略
ret = pol;
break;
}
}
if (ret)
xfrm_pol_hold(ret);
fail:
read_unlock_bh(&xfrm_policy_lock);
return ret;
}
}
// 检查xfrm策略是否和流参数匹配
// 返回0表示匹配成功
static int xfrm_policy_match(struct xfrm_policy *pol, struct flowi *fl,
u8 type, u16 family, int dir)
{
// 选择子
struct xfrm_selector *sel = &pol->selector;
int match, ret = -ESRCH;
// 检查策略协议族和类型是否匹配
if (pol->family != family ||
pol->type != type)
return ret;
// 检查选择子是否匹配, 返回非0值表示匹配成功
match = xfrm_selector_match(sel, fl, family);
if (match)
// 这种security函数可以不用考虑, 当作返回0的函数即可
ret = security_xfrm_policy_lookup(pol, fl->secid, dir);
// 返回0表示匹配成功
static int xfrm_policy_match(struct xfrm_policy *pol, struct flowi *fl,
u8 type, u16 family, int dir)
{
// 选择子
struct xfrm_selector *sel = &pol->selector;
int match, ret = -ESRCH;
// 检查策略协议族和类型是否匹配
if (pol->family != family ||
pol->type != type)
return ret;
// 检查选择子是否匹配, 返回非0值表示匹配成功
match = xfrm_selector_match(sel, fl, family);
if (match)
// 这种security函数可以不用考虑, 当作返回0的函数即可
ret = security_xfrm_policy_lookup(pol, fl->secid, dir);
return ret;
}
}
// 选择子匹配,分别对IPV4和IPV6协议族比较
static inline int
xfrm_selector_match(struct xfrm_selector *sel, struct flowi *fl,
unsigned short family)
{
switch (family) {
case AF_INET:
return __xfrm4_selector_match(sel, fl);
case AF_INET6:
return __xfrm6_selector_match(sel, fl);
}
return 0;
}
static inline int
xfrm_selector_match(struct xfrm_selector *sel, struct flowi *fl,
unsigned short family)
{
switch (family) {
case AF_INET:
return __xfrm4_selector_match(sel, fl);
case AF_INET6:
return __xfrm6_selector_match(sel, fl);
}
return 0;
}
//IPV4协议族选择子比较
static inline int
__xfrm4_selector_match(struct xfrm_selector *sel, struct flowi *fl)
{
// 比较V4目的地址, V4源地址, 目的端口, 源端口, 协议, 网卡索引号
return addr_match(&fl->fl4_dst, &sel->daddr, sel->prefixlen_d) &&
addr_match(&fl->fl4_src, &sel->saddr, sel->prefixlen_s) &&
!((xfrm_flowi_dport(fl) ^ sel->dport) & sel->dport_mask) &&
!((xfrm_flowi_sport(fl) ^ sel->sport) & sel->sport_mask) &&
(fl->proto == sel->proto || !sel->proto) &&
(fl->oif == sel->ifindex || !sel->ifindex);
}
static inline int
__xfrm4_selector_match(struct xfrm_selector *sel, struct flowi *fl)
{
// 比较V4目的地址, V4源地址, 目的端口, 源端口, 协议, 网卡索引号
return addr_match(&fl->fl4_dst, &sel->daddr, sel->prefixlen_d) &&
addr_match(&fl->fl4_src, &sel->saddr, sel->prefixlen_s) &&
!((xfrm_flowi_dport(fl) ^ sel->dport) & sel->dport_mask) &&
!((xfrm_flowi_sport(fl) ^ sel->sport) & sel->sport_mask) &&
(fl->proto == sel->proto || !sel->proto) &&
(fl->oif == sel->ifindex || !sel->ifindex);
}
//IPV6协议族选择子比较
static inline int
__xfrm6_selector_match(struct xfrm_selector *sel, struct flowi *fl)
{
// 比较V6目的地址, V6源地址, 目的端口, 源端口, 协议, 网卡索引号
return addr_match(&fl->fl6_dst, &sel->daddr, sel->prefixlen_d) &&
addr_match(&fl->fl6_src, &sel->saddr, sel->prefixlen_s) &&
!((xfrm_flowi_dport(fl) ^ sel->dport) & sel->dport_mask) &&
!((xfrm_flowi_sport(fl) ^ sel->sport) & sel->sport_mask) &&
(fl->proto == sel->proto || !sel->proto) &&
(fl->oif == sel->ifindex || !sel->ifindex);
}
static inline int
__xfrm6_selector_match(struct xfrm_selector *sel, struct flowi *fl)
{
// 比较V6目的地址, V6源地址, 目的端口, 源端口, 协议, 网卡索引号
return addr_match(&fl->fl6_dst, &sel->daddr, sel->prefixlen_d) &&
addr_match(&fl->fl6_src, &sel->saddr, sel->prefixlen_s) &&
!((xfrm_flowi_dport(fl) ^ sel->dport) & sel->dport_mask) &&
!((xfrm_flowi_sport(fl) ^ sel->sport) & sel->sport_mask) &&
(fl->proto == sel->proto || !sel->proto) &&
(fl->oif == sel->ifindex || !sel->ifindex);
}
5.4.4 查找和sock对应的策略
static struct xfrm_policy *xfrm_sk_policy_lookup(struct sock *sk, int dir, struct flowi *fl)
{
struct xfrm_policy *pol;
{
struct xfrm_policy *pol;
read_lock_bh(&xfrm_policy_lock);
// sock结构中有sk_policy用来指向双向数据的安全策略
if ((pol = sk->sk_policy[dir]) != NULL) {
// 检查该策略的选择子是否和流结构匹配
int match = xfrm_selector_match(&pol->selector, fl,
sk->sk_family);
int err = 0;
// 如果匹配的话将策略作为结果返回
if (match) {
// 这个security函数可视为返回0的空函数
err = security_xfrm_policy_lookup(pol, fl->secid,
policy_to_flow_dir(dir));
if (!err)
xfrm_pol_hold(pol);
else if (err == -ESRCH)
pol = NULL;
else
pol = ERR_PTR(err);
} else
pol = NULL;
}
read_unlock_bh(&xfrm_policy_lock);
return pol;
}
// sock结构中有sk_policy用来指向双向数据的安全策略
if ((pol = sk->sk_policy[dir]) != NULL) {
// 检查该策略的选择子是否和流结构匹配
int match = xfrm_selector_match(&pol->selector, fl,
sk->sk_family);
int err = 0;
// 如果匹配的话将策略作为结果返回
if (match) {
// 这个security函数可视为返回0的空函数
err = security_xfrm_policy_lookup(pol, fl->secid,
policy_to_flow_dir(dir));
if (!err)
xfrm_pol_hold(pol);
else if (err == -ESRCH)
pol = NULL;
else
pol = ERR_PTR(err);
} else
pol = NULL;
}
read_unlock_bh(&xfrm_policy_lock);
return pol;
}
5.5 遍历安全策略
该函数被pfkey_spddump()等函数中调用
// func函数用来指定对遍历的策略进行的查找
// 实际遍历了两次所有策略
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++;
}
// 遍历按地址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++;
}
}
}
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++;
}
// 遍历按地址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;
// 遍历inexact链表
hlist_for_each_entry(pol, entry,
&xfrm_policy_inexact[dir], bydst) {
if (pol->type != type)
continue;
// 对符合类型的策略调用func函数
error = func(pol, dir % XFRM_POLICY_MAX, --count, data);
if (error)
goto out;
}
// 遍历按地址HASH的链表
for (i = xfrm_policy_bydst[dir].hmask; i >= 0; i--) {
hlist_for_each_entry(pol, entry, table + i, bydst) {
if (pol->type != type)
continue;
// 对符合类型的策略调用func函数, 当count递减到0时表示是最后一个策略了
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);
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;
// 遍历inexact链表
hlist_for_each_entry(pol, entry,
&xfrm_policy_inexact[dir], bydst) {
if (pol->type != type)
continue;
// 对符合类型的策略调用func函数
error = func(pol, dir % XFRM_POLICY_MAX, --count, data);
if (error)
goto out;
}
// 遍历按地址HASH的链表
for (i = xfrm_policy_bydst[dir].hmask; i >= 0; i--) {
hlist_for_each_entry(pol, entry, table + i, bydst) {
if (pol->type != type)
continue;
// 对符合类型的策略调用func函数, 当count递减到0时表示是最后一个策略了
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.5 策略检查
__xfrm_policy_check函数也是一个比较重要的函数, 被xfrm_policy_check()调用, 又被xfrm4_policy_check()和xfrm6_policy_check()调用, 而这两个函数在网络层的输入和转发处调用.
对普通包就返回合法, 对IPSEC包检查策略是否合法, 是否和路由方向匹配
// 返回1表示合法, 0表示不合法, 对于该函数返回0的数据包通常是被丢弃
int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
unsigned short family)
{
struct xfrm_policy *pol;
struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX];
int npols = 0;
int xfrm_nr;
int pi;
struct flowi fl;
// 将策略方向转换为流方向, 其实值是一样的
u8 fl_dir = policy_to_flow_dir(dir);
int xerr_idx = -1;
// 调用协议族的decode_session()函数, 对IPV4来说就是_decode_session4
// 将skb中的地址端口等信息填入流结构fl中
if (xfrm_decode_session(skb, &fl, family) < 0)
return 0;
// 如果内核支持NETFILTER, 将调用ip_nat_decode_session函数填写NAT信息
// 否则的话就是个空函数
nf_nat_decode_session(skb, &fl, family);
// 将skb中的地址端口等信息填入流结构fl中
if (xfrm_decode_session(skb, &fl, family) < 0)
return 0;
// 如果内核支持NETFILTER, 将调用ip_nat_decode_session函数填写NAT信息
// 否则的话就是个空函数
nf_nat_decode_session(skb, &fl, family);
/* First, check used SA against their selectors. */
if (skb->sp) {
// 该包是进行了解密后的IPSEC包
int i;
if (skb->sp) {
// 该包是进行了解密后的IPSEC包
int i;
for (i=skb->sp->len-1; i>=0; i--) {
// 获取该包相关的SA信息
struct xfrm_state *x = skb->sp->xvec[i];
// 检查SA选择子和流参数(路由)是否匹配, 结果为0表示不匹配, 不匹配的话返回
if (!xfrm_selector_match(&x->sel, &fl, family))
return 0;
}
}
// 获取该包相关的SA信息
struct xfrm_state *x = skb->sp->xvec[i];
// 检查SA选择子和流参数(路由)是否匹配, 结果为0表示不匹配, 不匹配的话返回
if (!xfrm_selector_match(&x->sel, &fl, family))
return 0;
}
}
pol = NULL;
// 如果sock结构中有策略
if (sk && sk->sk_policy[dir]) {
// 检查策略是否和流结构匹配, 匹配的话返回策略
pol = xfrm_sk_policy_lookup(sk, dir, &fl);
if (IS_ERR(pol))
return 0;
}
// 如果sock结构中有策略
if (sk && sk->sk_policy[dir]) {
// 检查策略是否和流结构匹配, 匹配的话返回策略
pol = xfrm_sk_policy_lookup(sk, dir, &fl);
if (IS_ERR(pol))
return 0;
}
// 查找路由信息, 如果没有就创建路由, xfrm_policy_lookup()函数作为参数传递给
// flow_cache_lookup()函数, 查找和该路由对应的安全策略
if (!pol)
pol = flow_cache_lookup(&fl, family, fl_dir,
xfrm_policy_lookup);
// 查找过程中出错,返回0
if (IS_ERR(pol))
return 0;
// flow_cache_lookup()函数, 查找和该路由对应的安全策略
if (!pol)
pol = flow_cache_lookup(&fl, family, fl_dir,
xfrm_policy_lookup);
// 查找过程中出错,返回0
if (IS_ERR(pol))
return 0;
// 策略不存在
if (!pol) {
// 如果该包是IPSEC包而且安全路径中的SA不是传输模式,
// 转发时, 对于已经封装的包没必要再次封装;
// 输入时, 是自身的IPSEC通信包封装基本也无意义
if (skb->sp && secpath_has_nontransport(skb->sp, 0, &xerr_idx)) {
// 拒绝该安全路径, 返回0失败
xfrm_secpath_reject(xerr_idx, skb, &fl);
return 0;
}
// 普通包处理, 安全策略不存在, 返回1
return 1;
}
if (!pol) {
// 如果该包是IPSEC包而且安全路径中的SA不是传输模式,
// 转发时, 对于已经封装的包没必要再次封装;
// 输入时, 是自身的IPSEC通信包封装基本也无意义
if (skb->sp && secpath_has_nontransport(skb->sp, 0, &xerr_idx)) {
// 拒绝该安全路径, 返回0失败
xfrm_secpath_reject(xerr_idx, skb, &fl);
return 0;
}
// 普通包处理, 安全策略不存在, 返回1
return 1;
}
// 找到安全策略, 对该包要根据策略进行IPSEC处理
// 更新策略当前使用时间
pol->curlft.use_time = (unsigned long)xtime.tv_sec;
// 更新策略当前使用时间
pol->curlft.use_time = (unsigned long)xtime.tv_sec;
pols[0] = pol;
npols ++;
#ifdef CONFIG_XFRM_SUB_POLICY
// 如果定义了子策略的话极限查找子策略, 这是标准IPSEC中没定义的, 可以不考虑
if (pols[0]->type != XFRM_POLICY_TYPE_MAIN) {
pols[1] = xfrm_policy_lookup_bytype(XFRM_POLICY_TYPE_MAIN,
&fl, family,
XFRM_POLICY_IN);
if (pols[1]) {
if (IS_ERR(pols[1]))
return 0;
pols[1]->curlft.use_time = (unsigned long)xtime.tv_sec;
npols ++;
}
}
#endif
npols ++;
#ifdef CONFIG_XFRM_SUB_POLICY
// 如果定义了子策略的话极限查找子策略, 这是标准IPSEC中没定义的, 可以不考虑
if (pols[0]->type != XFRM_POLICY_TYPE_MAIN) {
pols[1] = xfrm_policy_lookup_bytype(XFRM_POLICY_TYPE_MAIN,
&fl, family,
XFRM_POLICY_IN);
if (pols[1]) {
if (IS_ERR(pols[1]))
return 0;
pols[1]->curlft.use_time = (unsigned long)xtime.tv_sec;
npols ++;
}
}
#endif
// 策略动作是允许通过
if (pol->action == XFRM_POLICY_ALLOW) {
struct sec_path *sp;
// 先伪造个安全路径
static struct sec_path dummy;
struct xfrm_tmpl *tp[XFRM_MAX_DEPTH];
struct xfrm_tmpl *stp[XFRM_MAX_DEPTH];
struct xfrm_tmpl **tpp = tp;
int ti = 0;
int i, k;
if (pol->action == XFRM_POLICY_ALLOW) {
struct sec_path *sp;
// 先伪造个安全路径
static struct sec_path dummy;
struct xfrm_tmpl *tp[XFRM_MAX_DEPTH];
struct xfrm_tmpl *stp[XFRM_MAX_DEPTH];
struct xfrm_tmpl **tpp = tp;
int ti = 0;
int i, k;
// 如果数据包没有安全路径, 路径指针初始化为伪造的安全路径
if ((sp = skb->sp) == NULL)
sp = &dummy;
if ((sp = skb->sp) == NULL)
sp = &dummy;
// 遍历策略数组, 包括主策略和子策略(内核支持子策略的话),一般情况下就一个策略
for (pi = 0; pi < npols; pi++) {
// 如果有非允许通过的其他安全策略, 放弃
if (pols[pi] != pol &&
pols[pi]->action != XFRM_POLICY_ALLOW)
goto reject;
// 如果策略层次太多, 放弃
if (ti + pols[pi]->xfrm_nr >= XFRM_MAX_DEPTH)
goto reject_error;
// 备份策略中的xfrm向量模板, ti是数量
for (i = 0; i < pols[pi]->xfrm_nr; i++)
tpp[ti++] = &pols[pi]->xfrm_vec[i];
}
// 策略数量
xfrm_nr = ti;
if (npols > 1) {
// 如果超过一个策略,进行排序, 只是在内核支持子系统时才用, 否则只是返回错误
// 但该错误可以忽略
xfrm_tmpl_sort(stp, tpp, xfrm_nr, family);
tpp = stp;
}
for (pi = 0; pi < npols; pi++) {
// 如果有非允许通过的其他安全策略, 放弃
if (pols[pi] != pol &&
pols[pi]->action != XFRM_POLICY_ALLOW)
goto reject;
// 如果策略层次太多, 放弃
if (ti + pols[pi]->xfrm_nr >= XFRM_MAX_DEPTH)
goto reject_error;
// 备份策略中的xfrm向量模板, ti是数量
for (i = 0; i < pols[pi]->xfrm_nr; i++)
tpp[ti++] = &pols[pi]->xfrm_vec[i];
}
// 策略数量
xfrm_nr = ti;
if (npols > 1) {
// 如果超过一个策略,进行排序, 只是在内核支持子系统时才用, 否则只是返回错误
// 但该错误可以忽略
xfrm_tmpl_sort(stp, tpp, xfrm_nr, family);
tpp = stp;
}
/* For each tunnel xfrm, find the first matching tmpl.
* For each tmpl before that, find corresponding xfrm.
* Order is _important_. Later we will implement
* some barriers, but at the moment barriers
* are implied between each two transformations.
*/
// 遍历检查策略模板是否OK
for (i = xfrm_nr-1, k = 0; i >= 0; i--) {
// 注意k既是输入, 也是输出值, k初始化为0
// 返回值大于等于0表示策略合法可用
k = xfrm_policy_ok(tpp[i], sp, k, family);
if (k < 0) {
if (k < -1)
/* "-2 - errored_index" returned */
xerr_idx = -(2+k);
goto reject;
}
}
// 存在非传输模式的策略, 放弃
if (secpath_has_nontransport(sp, k, &xerr_idx))
goto reject;
* For each tmpl before that, find corresponding xfrm.
* Order is _important_. Later we will implement
* some barriers, but at the moment barriers
* are implied between each two transformations.
*/
// 遍历检查策略模板是否OK
for (i = xfrm_nr-1, k = 0; i >= 0; i--) {
// 注意k既是输入, 也是输出值, k初始化为0
// 返回值大于等于0表示策略合法可用
k = xfrm_policy_ok(tpp[i], sp, k, family);
if (k < 0) {
if (k < -1)
/* "-2 - errored_index" returned */
xerr_idx = -(2+k);
goto reject;
}
}
// 存在非传输模式的策略, 放弃
if (secpath_has_nontransport(sp, k, &xerr_idx))
goto reject;
xfrm_pols_put(pols, npols);
return 1;
}
// 放弃, 返回0表示检查不通过
reject:
xfrm_secpath_reject(xerr_idx, skb, &fl);
reject_error:
xfrm_pols_put(pols, npols);
return 0;
}
EXPORT_SYMBOL(__xfrm_policy_check);
return 1;
}
// 放弃, 返回0表示检查不通过
reject:
xfrm_secpath_reject(xerr_idx, skb, &fl);
reject_error:
xfrm_pols_put(pols, npols);
return 0;
}
EXPORT_SYMBOL(__xfrm_policy_check);
/*
* 0 or more than 0 is returned when validation is succeeded (either bypass
* because of optional transport mode, or next index of the mathced secpath
* state with the template.
* -1 is returned when no matching template is found.
* Otherwise "-2 - errored_index" is returned.
*/
static inline int
xfrm_policy_ok(struct xfrm_tmpl *tmpl, struct sec_path *sp, int start,
unsigned short family)
{
int idx = start;
if (tmpl->optional) {
// 如果是传输模式, 直接返回
if (tmpl->mode == XFRM_MODE_TRANSPORT)
return start;
} else
start = -1;
for (; idx < sp->len; idx++) {
// sp->xvec是xfrm状态
// 如果安全路径和模板匹配,返回索引位置
if (xfrm_state_ok(tmpl, sp->xvec[idx], family))
return ++idx;
// 如果安全路径中的SA不是传输模式,返回错误
if (sp->xvec[idx]->props.mode != XFRM_MODE_TRANSPORT) {
if (start == -1)
start = -2-idx;
break;
}
}
return start;
}
// 如果是传输模式, 直接返回
if (tmpl->mode == XFRM_MODE_TRANSPORT)
return start;
} else
start = -1;
for (; idx < sp->len; idx++) {
// sp->xvec是xfrm状态
// 如果安全路径和模板匹配,返回索引位置
if (xfrm_state_ok(tmpl, sp->xvec[idx], family))
return ++idx;
// 如果安全路径中的SA不是传输模式,返回错误
if (sp->xvec[idx]->props.mode != XFRM_MODE_TRANSPORT) {
if (start == -1)
start = -2-idx;
break;
}
}
return start;
}
5.6 安全策略路由查找
xfrm_lookup函数是个非常重要的函数, 用来根据安全策略构造数据包的路由项链表, 该路由项链表反映了对数据包进行IPSEC封装的多层次的处理, 每封装一次, 就增加一个路由项.
该函数被路由查找函数ip_route_output_flow()调用, 针对的是转发或发出的数据包.
该函数被路由查找函数ip_route_output_flow()调用, 针对的是转发或发出的数据包.
/* Main function: finds/creates a bundle for given flow.
*
* At the moment we eat a raw IP route. Mostly to speed up lookups
* on interfaces with disabled IPsec.
*/
// 返回0表示超过, 负数表示失败
int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl,
struct sock *sk, int flags)
{
struct xfrm_policy *policy;
struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX];
int npols;
int pol_dead;
int xfrm_nr;
int pi;
struct xfrm_state *xfrm[XFRM_MAX_DEPTH];
struct dst_entry *dst, *dst_orig = *dst_p;
int nx = 0;
int err;
u32 genid;
u16 family;
u8 dir = policy_to_flow_dir(XFRM_POLICY_OUT);
restart:
// 初始化清零操作
genid = atomic_read(&flow_cache_genid);
policy = NULL;
for (pi = 0; pi < ARRAY_SIZE(pols); pi++)
pols[pi] = NULL;
npols = 0;
pol_dead = 0;
xfrm_nr = 0;
// 初始化清零操作
genid = atomic_read(&flow_cache_genid);
policy = NULL;
for (pi = 0; pi < ARRAY_SIZE(pols); pi++)
pols[pi] = NULL;
npols = 0;
pol_dead = 0;
xfrm_nr = 0;
if (sk && sk->sk_policy[1]) {
// 如果在sock中定义了安全策略, 查找该sock相关的策略
// 一个socket的安全策略可通过setsockopt()设置, socket选项为
// IP_IPSEC_POLICY或IP_XFRM_POLICY(net/ipv4/ip_sockglue.c)
policy = xfrm_sk_policy_lookup(sk, XFRM_POLICY_OUT, fl);
if (IS_ERR(policy))
return PTR_ERR(policy);
}
// 如果在sock中定义了安全策略, 查找该sock相关的策略
// 一个socket的安全策略可通过setsockopt()设置, socket选项为
// IP_IPSEC_POLICY或IP_XFRM_POLICY(net/ipv4/ip_sockglue.c)
policy = xfrm_sk_policy_lookup(sk, XFRM_POLICY_OUT, fl);
if (IS_ERR(policy))
return PTR_ERR(policy);
}
if (!policy) {
// 没找到sock自身定义的安全策略
/* To accelerate a bit... */
// 如果初始路由中设置了非IPSEC标志或没有发出方向的安全策略, 直接返回
if ((dst_orig->flags & DST_NOXFRM) ||
!xfrm_policy_count[XFRM_POLICY_OUT])
return 0;
// 没找到sock自身定义的安全策略
/* To accelerate a bit... */
// 如果初始路由中设置了非IPSEC标志或没有发出方向的安全策略, 直接返回
if ((dst_orig->flags & DST_NOXFRM) ||
!xfrm_policy_count[XFRM_POLICY_OUT])
return 0;
// 查找路由信息, 如果没有就创建路由, xfrm_policy_lookup()函数作为参数传递给
// flow_cache_lookup()函数, 查找和该路由对应的安全策略
policy = flow_cache_lookup(fl, dst_orig->ops->family,
dir, xfrm_policy_lookup);
if (IS_ERR(policy))
return PTR_ERR(policy);
}
// 找不到策略的话返回, 就是普通包普通路由项
if (!policy)
return 0;
// flow_cache_lookup()函数, 查找和该路由对应的安全策略
policy = flow_cache_lookup(fl, dst_orig->ops->family,
dir, xfrm_policy_lookup);
if (IS_ERR(policy))
return PTR_ERR(policy);
}
// 找不到策略的话返回, 就是普通包普通路由项
if (!policy)
return 0;
// 以下是存在安全策略的情况, 要对该包建立安全路由链表
// 初始路由的协议族
family = dst_orig->ops->family;
// 安全策略最近使用时间
policy->curlft.use_time = (unsigned long)xtime.tv_sec;
// 将找到的策略作为策略数组的第一项
pols[0] = policy;
npols ++;
xfrm_nr += pols[0]->xfrm_nr;
// 初始路由的协议族
family = dst_orig->ops->family;
// 安全策略最近使用时间
policy->curlft.use_time = (unsigned long)xtime.tv_sec;
// 将找到的策略作为策略数组的第一项
pols[0] = policy;
npols ++;
xfrm_nr += pols[0]->xfrm_nr;
// 根据策略操作结果进行相关处理, 只有两种情况: 阻塞或通过
switch (policy->action) {
case XFRM_POLICY_BLOCK:
// 阻塞该数据包, 返回错误
/* Prohibit the flow */
err = -EPERM;
goto error;
switch (policy->action) {
case XFRM_POLICY_BLOCK:
// 阻塞该数据包, 返回错误
/* Prohibit the flow */
err = -EPERM;
goto error;
case XFRM_POLICY_ALLOW:
// 允许该包通过, 这样就要对该包进行IPSEC处理
#ifndef CONFIG_XFRM_SUB_POLICY
// 对子策略操作忽略
if (policy->xfrm_nr == 0) {
/* Flow passes not transformed. */
xfrm_pol_put(policy);
return 0;
}
#endif
// 允许该包通过, 这样就要对该包进行IPSEC处理
#ifndef CONFIG_XFRM_SUB_POLICY
// 对子策略操作忽略
if (policy->xfrm_nr == 0) {
/* Flow passes not transformed. */
xfrm_pol_put(policy);
return 0;
}
#endif
/* Try to find matching bundle.
*
* LATER: help from flow cache. It is optional, this
* is required only for output policy.
*/
// 查找是否已经存在安全路由, bundle可以理解为描述安全处理的安全路由, 数据包走该路由
// 就是进行某种安全封装, 和普通路由项一样, 用过的安全路由也被缓存起来
dst = xfrm_find_bundle(fl, policy, family);
if (IS_ERR(dst)) {
err = PTR_ERR(dst);
goto error;
}
// 如果找到安全路由, 退出switch
if (dst)
break;
*
* LATER: help from flow cache. It is optional, this
* is required only for output policy.
*/
// 查找是否已经存在安全路由, bundle可以理解为描述安全处理的安全路由, 数据包走该路由
// 就是进行某种安全封装, 和普通路由项一样, 用过的安全路由也被缓存起来
dst = xfrm_find_bundle(fl, policy, family);
if (IS_ERR(dst)) {
err = PTR_ERR(dst);
goto error;
}
// 如果找到安全路由, 退出switch
if (dst)
break;
#ifdef CONFIG_XFRM_SUB_POLICY
// 对子策略操作, 由于是非标准IPSEC,忽略
if (pols[0]->type != XFRM_POLICY_TYPE_MAIN) {
pols[1] = xfrm_policy_lookup_bytype(XFRM_POLICY_TYPE_MAIN,
fl, family,
XFRM_POLICY_OUT);
if (pols[1]) {
if (IS_ERR(pols[1])) {
err = PTR_ERR(pols[1]);
goto error;
}
if (pols[1]->action == XFRM_POLICY_BLOCK) {
err = -EPERM;
goto error;
}
npols ++;
xfrm_nr += pols[1]->xfrm_nr;
}
}
// 对子策略操作, 由于是非标准IPSEC,忽略
if (pols[0]->type != XFRM_POLICY_TYPE_MAIN) {
pols[1] = xfrm_policy_lookup_bytype(XFRM_POLICY_TYPE_MAIN,
fl, family,
XFRM_POLICY_OUT);
if (pols[1]) {
if (IS_ERR(pols[1])) {
err = PTR_ERR(pols[1]);
goto error;
}
if (pols[1]->action == XFRM_POLICY_BLOCK) {
err = -EPERM;
goto error;
}
npols ++;
xfrm_nr += pols[1]->xfrm_nr;
}
}
/*
* Because neither flowi nor bundle information knows about
* transformation template size. On more than one policy usage
* we can realize whether all of them is bypass or not after
* they are searched. See above not-transformed bypass
* is surrounded by non-sub policy configuration, too.
*/
if (xfrm_nr == 0) {
/* Flow passes not transformed. */
xfrm_pols_put(pols, npols);
return 0;
}
* Because neither flowi nor bundle information knows about
* transformation template size. On more than one policy usage
* we can realize whether all of them is bypass or not after
* they are searched. See above not-transformed bypass
* is surrounded by non-sub policy configuration, too.
*/
if (xfrm_nr == 0) {
/* Flow passes not transformed. */
xfrm_pols_put(pols, npols);
return 0;
}
#endif
// 没找到安全路由, 准备构造新的路由项
// 利用策略, 流等参数构造相关SA(xfrm_state)保存在xfrm中, nx为SA数量
nx = xfrm_tmpl_resolve(pols, npols, fl, xfrm, family);
// 没找到安全路由, 准备构造新的路由项
// 利用策略, 流等参数构造相关SA(xfrm_state)保存在xfrm中, nx为SA数量
nx = xfrm_tmpl_resolve(pols, npols, fl, xfrm, family);
if (unlikely(nx<0)) {
// nx<0表示失败, 没找到SA
// 但如果是-EAGAIN表示已经通知用户空间的IKE进行协商新的SA了,
// 目前只生成了ACQUIRE类型的xfrm_state
err = nx;
if (err == -EAGAIN && flags) {
// 进程进入阻塞状态
DECLARE_WAITQUEUE(wait, current);
// nx<0表示失败, 没找到SA
// 但如果是-EAGAIN表示已经通知用户空间的IKE进行协商新的SA了,
// 目前只生成了ACQUIRE类型的xfrm_state
err = nx;
if (err == -EAGAIN && flags) {
// 进程进入阻塞状态
DECLARE_WAITQUEUE(wait, current);
add_wait_queue(&km_waitq, &wait);
set_current_state(TASK_INTERRUPTIBLE);
schedule();
set_current_state(TASK_RUNNING);
remove_wait_queue(&km_waitq, &wait);
// 阻塞解除, 重新解析SA
nx = xfrm_tmpl_resolve(pols, npols, fl, xfrm, family);
set_current_state(TASK_INTERRUPTIBLE);
schedule();
set_current_state(TASK_RUNNING);
remove_wait_queue(&km_waitq, &wait);
// 阻塞解除, 重新解析SA
nx = xfrm_tmpl_resolve(pols, npols, fl, xfrm, family);
if (nx == -EAGAIN && signal_pending(current)) {
err = -ERESTART;
goto error;
}
if (nx == -EAGAIN ||
genid != atomic_read(&flow_cache_genid)) {
xfrm_pols_put(pols, npols);
goto restart;
}
err = nx;
}
if (err < 0)
goto error;
}
if (nx == 0) {
// nx==0表示数据是不需要进行IPSEC处理的, 返回
/* Flow passes not transformed. */
xfrm_pols_put(pols, npols);
return 0;
}
// 保存初始路由
dst = dst_orig;
// 创建新的安全路由, 返回0 表示成功, 失败返回负数
// dst在成功返回时保存安全路由项, 每个SA处理对应一个安全路由, 这些安全路由通过
// 路由项中的child链接为一个链表, 这样就可以对数据包进行连续变换, 如先压缩,
// 再ESP封装, 再AH封装等.
// 路由项链表的构造和协议族相关, 后续文章中介绍具体协议族中的实现时再详细描述
// 所构造出的路由项的具体结构情况
err = xfrm_bundle_create(policy, xfrm, nx, fl, &dst, family);
err = -ERESTART;
goto error;
}
if (nx == -EAGAIN ||
genid != atomic_read(&flow_cache_genid)) {
xfrm_pols_put(pols, npols);
goto restart;
}
err = nx;
}
if (err < 0)
goto error;
}
if (nx == 0) {
// nx==0表示数据是不需要进行IPSEC处理的, 返回
/* Flow passes not transformed. */
xfrm_pols_put(pols, npols);
return 0;
}
// 保存初始路由
dst = dst_orig;
// 创建新的安全路由, 返回0 表示成功, 失败返回负数
// dst在成功返回时保存安全路由项, 每个SA处理对应一个安全路由, 这些安全路由通过
// 路由项中的child链接为一个链表, 这样就可以对数据包进行连续变换, 如先压缩,
// 再ESP封装, 再AH封装等.
// 路由项链表的构造和协议族相关, 后续文章中介绍具体协议族中的实现时再详细描述
// 所构造出的路由项的具体结构情况
err = xfrm_bundle_create(policy, xfrm, nx, fl, &dst, family);
if (unlikely(err)) {
// 失败的话释放刚获取的SA
int i;
for (i=0; i<nx; i++)
xfrm_state_put(xfrm[i]);
goto error;
}
// 检查所有策略的dead状态
for (pi = 0; pi < npols; pi++) {
read_lock_bh(&pols[pi]->lock);
pol_dead |= pols[pi]->dead;
read_unlock_bh(&pols[pi]->lock);
}
// 失败的话释放刚获取的SA
int i;
for (i=0; i<nx; i++)
xfrm_state_put(xfrm[i]);
goto error;
}
// 检查所有策略的dead状态
for (pi = 0; pi < npols; pi++) {
read_lock_bh(&pols[pi]->lock);
pol_dead |= pols[pi]->dead;
read_unlock_bh(&pols[pi]->lock);
}
write_lock_bh(&policy->lock);
// 如果有策略是dead或获取的安全路由项有问题, 释放安全路由
if (unlikely(pol_dead || stale_bundle(dst))) {
/* Wow! While we worked on resolving, this
* policy has gone. Retry. It is not paranoia,
* we just cannot enlist new bundle to dead object.
* We can't enlist stable bundles either.
*/
write_unlock_bh(&policy->lock);
if (dst)
dst_free(dst);
// 如果有策略是dead或获取的安全路由项有问题, 释放安全路由
if (unlikely(pol_dead || stale_bundle(dst))) {
/* Wow! While we worked on resolving, this
* policy has gone. Retry. It is not paranoia,
* we just cannot enlist new bundle to dead object.
* We can't enlist stable bundles either.
*/
write_unlock_bh(&policy->lock);
if (dst)
dst_free(dst);
err = -EHOSTUNREACH;
goto error;
}
// 将安全路由加入到策略的路由项链表头, 该链表是以NULL结尾的单向链表
// 不过一般情况下应该只有一个元素
dst->next = policy->bundles;
policy->bundles = dst;
dst_hold(dst);
write_unlock_bh(&policy->lock);
}
// 将安全链表作为
*dst_p = dst;
dst_release(dst_orig);
xfrm_pols_put(pols, npols);
return 0;
goto error;
}
// 将安全路由加入到策略的路由项链表头, 该链表是以NULL结尾的单向链表
// 不过一般情况下应该只有一个元素
dst->next = policy->bundles;
policy->bundles = dst;
dst_hold(dst);
write_unlock_bh(&policy->lock);
}
// 将安全链表作为
*dst_p = dst;
dst_release(dst_orig);
xfrm_pols_put(pols, npols);
return 0;
error:
dst_release(dst_orig);
xfrm_pols_put(pols, npols);
*dst_p = NULL;
return err;
}
EXPORT_SYMBOL(xfrm_lookup);
dst_release(dst_orig);
xfrm_pols_put(pols, npols);
*dst_p = NULL;
return err;
}
EXPORT_SYMBOL(xfrm_lookup);
以下是在xfrm_lookup中用到的两个bundle的操作函数: 查找和创建, 由于使用了地址参数, 是和协议族相关的, 因此具体实现是在各协议族中实现的, 在后续文章中介绍协议族中的xfrm实现时再详细介绍.
static struct dst_entry *
xfrm_find_bundle(struct flowi *fl, struct xfrm_policy *policy, unsigned short family)
{
struct dst_entry *x;
struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
if (unlikely(afinfo == NULL))
return ERR_PTR(-EINVAL);
x = afinfo->find_bundle(fl, policy);
xfrm_policy_put_afinfo(afinfo);
return x;
}
static struct dst_entry *
xfrm_find_bundle(struct flowi *fl, struct xfrm_policy *policy, unsigned short family)
{
struct dst_entry *x;
struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
if (unlikely(afinfo == NULL))
return ERR_PTR(-EINVAL);
x = afinfo->find_bundle(fl, policy);
xfrm_policy_put_afinfo(afinfo);
return x;
}
/* Allocate chain of dst_entry's, attach known xfrm's, calculate
* all the metrics... Shortly, bundle a bundle.
*/
* all the metrics... Shortly, bundle a bundle.
*/
static int
xfrm_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int nx,
struct flowi *fl, struct dst_entry **dst_p,
unsigned short family)
{
int err;
struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
if (unlikely(afinfo == NULL))
return -EINVAL;
err = afinfo->bundle_create(policy, xfrm, nx, fl, dst_p);
xfrm_policy_put_afinfo(afinfo);
return err;
}
xfrm_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int nx,
struct flowi *fl, struct dst_entry **dst_p,
unsigned short family)
{
int err;
struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
if (unlikely(afinfo == NULL))
return -EINVAL;
err = afinfo->bundle_create(policy, xfrm, nx, fl, dst_p);
xfrm_policy_put_afinfo(afinfo);
return err;
}
// 策略解析, 生成SA
static int
xfrm_tmpl_resolve(struct xfrm_policy **pols, int npols, struct flowi *fl,
struct xfrm_state **xfrm,
unsigned short family)
{
struct xfrm_state *tp[XFRM_MAX_DEPTH];
// npols > 1是定义了子策略的情况, 这时用tp数组保存找到的SA, 但没法返回原函数中了
// 不明白为什么这么作
struct xfrm_state **tpp = (npols > 1) ? tp : xfrm;
int cnx = 0;
int error;
int ret;
int i;
// 遍历策略, 一般情况下npols其实只是1
for (i = 0; i < npols; i++) {
// 检查保存SA的缓冲区是否还够大
if (cnx + pols[i]->xfrm_nr >= XFRM_MAX_DEPTH) {
error = -ENOBUFS;
goto fail;
}
// 协议一个策略模板
ret = xfrm_tmpl_resolve_one(pols[i], fl, &tpp[cnx], family);
if (ret < 0) {
error = ret;
goto fail;
} else
cnx += ret;
}
for (i = 0; i < npols; i++) {
// 检查保存SA的缓冲区是否还够大
if (cnx + pols[i]->xfrm_nr >= XFRM_MAX_DEPTH) {
error = -ENOBUFS;
goto fail;
}
// 协议一个策略模板
ret = xfrm_tmpl_resolve_one(pols[i], fl, &tpp[cnx], family);
if (ret < 0) {
error = ret;
goto fail;
} else
cnx += ret;
}
/* found states are sorted for outbound processing */
// 多个策略的话对找到的SA排序, 在没定义子策略的情况下是个空函数
if (npols > 1)
xfrm_state_sort(xfrm, tpp, cnx, family);
// 多个策略的话对找到的SA排序, 在没定义子策略的情况下是个空函数
if (npols > 1)
xfrm_state_sort(xfrm, tpp, cnx, family);
return cnx;
fail:
for (cnx--; cnx>=0; cnx--)
xfrm_state_put(tpp[cnx]);
return error;
for (cnx--; cnx>=0; cnx--)
xfrm_state_put(tpp[cnx]);
return error;
}
/* Resolve list of templates for the flow, given policy. */
static int
xfrm_tmpl_resolve_one(struct xfrm_policy *policy, struct flowi *fl,
struct xfrm_state **xfrm,
unsigned short family)
{
int nx;
int i, error;
// 从流结构中获取地址信息
xfrm_address_t *daddr = xfrm_flowi_daddr(fl, family);
xfrm_address_t *saddr = xfrm_flowi_saddr(fl, family);
xfrm_address_t tmp;
// 遍历策略中的所有SA
for (nx=0, i = 0; i < policy->xfrm_nr; i++) {
struct xfrm_state *x;
xfrm_address_t *remote = daddr;
xfrm_address_t *local = saddr;
struct xfrm_tmpl *tmpl = &policy->xfrm_vec[i];
xfrm_tmpl_resolve_one(struct xfrm_policy *policy, struct flowi *fl,
struct xfrm_state **xfrm,
unsigned short family)
{
int nx;
int i, error;
// 从流结构中获取地址信息
xfrm_address_t *daddr = xfrm_flowi_daddr(fl, family);
xfrm_address_t *saddr = xfrm_flowi_saddr(fl, family);
xfrm_address_t tmp;
// 遍历策略中的所有SA
for (nx=0, i = 0; i < policy->xfrm_nr; i++) {
struct xfrm_state *x;
xfrm_address_t *remote = daddr;
xfrm_address_t *local = saddr;
struct xfrm_tmpl *tmpl = &policy->xfrm_vec[i];
if (tmpl->mode == XFRM_MODE_TUNNEL) {
// 如果是通道模式, 会添加外部IP头, 内部IP头都封装在内部, 因此地址信息使用外部地址
// 即策略的SA模板中的地址信息
remote = &tmpl->id.daddr;
local = &tmpl->saddr;
// 如果local地址没定义, 选取个源地址作为本地地址, 选取过程是协议族相关的
if (xfrm_addr_any(local, family)) {
error = xfrm_get_saddr(&tmp, remote, family);
if (error)
goto fail;
local = &tmp;
}
}
// 根据地址,流,策略等新查找SA(xfrm_state),如果找不到现成的会通知IKE程序进行协商
// 生成新的SA, 但生成可用SA前先返回ACQUIRE类型的SA, 见前一篇文章
x = xfrm_state_find(remote, local, fl, tmpl, policy, &error, family);
// 如果是通道模式, 会添加外部IP头, 内部IP头都封装在内部, 因此地址信息使用外部地址
// 即策略的SA模板中的地址信息
remote = &tmpl->id.daddr;
local = &tmpl->saddr;
// 如果local地址没定义, 选取个源地址作为本地地址, 选取过程是协议族相关的
if (xfrm_addr_any(local, family)) {
error = xfrm_get_saddr(&tmp, remote, family);
if (error)
goto fail;
local = &tmp;
}
}
// 根据地址,流,策略等新查找SA(xfrm_state),如果找不到现成的会通知IKE程序进行协商
// 生成新的SA, 但生成可用SA前先返回ACQUIRE类型的SA, 见前一篇文章
x = xfrm_state_find(remote, local, fl, tmpl, policy, &error, family);
if (x && x->km.state == XFRM_STATE_VALID) {
// 如果SA是合法, 保存
xfrm[nx++] = x;
daddr = remote;
saddr = local;
continue;
}
if (x) {
// x存在但不是VALID的, 只要不出错, 应该是ACQUIRE类型的, 等IKE进程协商结果, 返回-EAGAIN
error = (x->km.state == XFRM_STATE_ERROR ?
-EINVAL : -EAGAIN);
xfrm_state_put(x);
}
// 如果SA是合法, 保存
xfrm[nx++] = x;
daddr = remote;
saddr = local;
continue;
}
if (x) {
// x存在但不是VALID的, 只要不出错, 应该是ACQUIRE类型的, 等IKE进程协商结果, 返回-EAGAIN
error = (x->km.state == XFRM_STATE_ERROR ?
-EINVAL : -EAGAIN);
xfrm_state_put(x);
}
if (!tmpl->optional)
goto fail;
}
return nx;
goto fail;
}
return nx;
fail:
for (nx--; nx>=0; nx--)
xfrm_state_put(xfrm[nx]);
return error;
}
for (nx--; nx>=0; nx--)
xfrm_state_put(xfrm[nx]);
return error;
}
关于路由处理过程在后面介绍IPSEC包的发出过程时会介绍路由处理过程, 从而了解安全路由的作用.
5.6 变更HASH表大小
改变策略状态表的是通过工作队列来实现的, 和xfrm_state类似
工作定义:
static DECLARE_WORK(xfrm_hash_work, xfrm_hash_resize, NULL);
// 更改HASH表大小
static void xfrm_hash_resize(void *__unused)
{
int dir, total;
mutex_lock(&hash_resize_mutex);
total = 0;
// 注意策略都是双向的
for (dir = 0; dir < XFRM_POLICY_MAX * 2; dir++) {
// 按目的地址进行HASH的链表: 如果需要更改HASH表大小, 修改之
if (xfrm_bydst_should_resize(dir, &total))
xfrm_bydst_resize(dir);
}
// 按索引号进行HASH的链表更新
if (xfrm_byidx_should_resize(total))
xfrm_byidx_resize(total);
// 注意策略都是双向的
for (dir = 0; dir < XFRM_POLICY_MAX * 2; dir++) {
// 按目的地址进行HASH的链表: 如果需要更改HASH表大小, 修改之
if (xfrm_bydst_should_resize(dir, &total))
xfrm_bydst_resize(dir);
}
// 按索引号进行HASH的链表更新
if (xfrm_byidx_should_resize(total))
xfrm_byidx_resize(total);
mutex_unlock(&hash_resize_mutex);
}
}
// 检查按目的地址HASH的HASH链表
static inline int xfrm_bydst_should_resize(int dir, int *total)
{
// 该方向是策略的数量
unsigned int cnt = xfrm_policy_count[dir];
// 该方向是策略的掩码
unsigned int hmask = xfrm_policy_bydst[dir].hmask;
static inline int xfrm_bydst_should_resize(int dir, int *total)
{
// 该方向是策略的数量
unsigned int cnt = xfrm_policy_count[dir];
// 该方向是策略的掩码
unsigned int hmask = xfrm_policy_bydst[dir].hmask;
// 累加策略数量
if (total)
*total += cnt;
if (total)
*total += cnt;
// 如果策略数量大于策略掩码量, 该增加了
if ((hmask + 1) < xfrm_policy_hashmax &&
cnt > hmask)
return 1;
// 否则不用
return 0;
}
if ((hmask + 1) < xfrm_policy_hashmax &&
cnt > hmask)
return 1;
// 否则不用
return 0;
}
// 检查按索引号HASH的HASH链表
static inline int xfrm_byidx_should_resize(int total)
{
unsigned int hmask = xfrm_idx_hmask;
// 策略总量超过当前的索引号掩码, 该扩大了
if ((hmask + 1) < xfrm_policy_hashmax &&
total > hmask)
return 1;
static inline int xfrm_byidx_should_resize(int total)
{
unsigned int hmask = xfrm_idx_hmask;
// 策略总量超过当前的索引号掩码, 该扩大了
if ((hmask + 1) < xfrm_policy_hashmax &&
total > hmask)
return 1;
return 0;
}
}
// 更改按目的地址HASH的HASH链表大小
static void xfrm_bydst_resize(int dir)
{
// 该方向的HASH表掩码(最大值, 一般是2^N-1)
unsigned int hmask = xfrm_policy_bydst[dir].hmask;
// 新HASH表掩码(2^(N+1)-1)
unsigned int nhashmask = xfrm_new_hash_mask(hmask);
// 新HASH表大小
unsigned int nsize = (nhashmask + 1) * sizeof(struct hlist_head);
// 老HAHS表
struct hlist_head *odst = xfrm_policy_bydst[dir].table;
// 新HASH表
struct hlist_head *ndst = xfrm_hash_alloc(nsize);
int i;
// 新HASH表空间分配不出来, 返回
if (!ndst)
return;
if (!ndst)
return;
write_lock_bh(&xfrm_policy_lock);
// 将所有策略节点转到新HASH表
for (i = hmask; i >= 0; i--)
xfrm_dst_hash_transfer(odst + i, ndst, nhashmask);
for (i = hmask; i >= 0; i--)
xfrm_dst_hash_transfer(odst + i, ndst, nhashmask);
// 将全局变量值更新为新HASH表参数
xfrm_policy_bydst[dir].table = ndst;
xfrm_policy_bydst[dir].hmask = nhashmask;
xfrm_policy_bydst[dir].table = ndst;
xfrm_policy_bydst[dir].hmask = nhashmask;
write_unlock_bh(&xfrm_policy_lock);
// 释放老HASH表参数
xfrm_hash_free(odst, (hmask + 1) * sizeof(struct hlist_head));
}
// 释放老HASH表参数
xfrm_hash_free(odst, (hmask + 1) * sizeof(struct hlist_head));
}
// 更改按索引号HASH的HASH链表大小, 操作和上面类似
static void xfrm_byidx_resize(int total)
{
unsigned int hmask = xfrm_idx_hmask;
unsigned int nhashmask = xfrm_new_hash_mask(hmask);
unsigned int nsize = (nhashmask + 1) * sizeof(struct hlist_head);
struct hlist_head *oidx = xfrm_policy_byidx;
struct hlist_head *nidx = xfrm_hash_alloc(nsize);
int i;
if (!nidx)
return;
return;
write_lock_bh(&xfrm_policy_lock);
for (i = hmask; i >= 0; i--)
xfrm_idx_hash_transfer(oidx + i, nidx, nhashmask);
xfrm_idx_hash_transfer(oidx + i, nidx, nhashmask);
xfrm_policy_byidx = nidx;
xfrm_idx_hmask = nhashmask;
xfrm_idx_hmask = nhashmask;
write_unlock_bh(&xfrm_policy_lock);
xfrm_hash_free(oidx, (hmask + 1) * sizeof(struct hlist_head));
}
}
5.7 垃圾搜集
垃圾搜集的是不用的安全路由项, 是和协议族相关的
afinfo->garbage_collect = __xfrm_garbage_collect;
// 就是xfrm_prune_bundles()函数的包装函数,条件是unused_bundle()函数定义
static void __xfrm_garbage_collect(void)
{
xfrm_prune_bundles(unused_bundle);
}
// 删减安全路由
static void xfrm_prune_bundles(int (*func)(struct dst_entry *))
{
// 垃圾链表
struct dst_entry *gc_list = NULL;
int dir;
read_lock_bh(&xfrm_policy_lock);
// 循环所有方向
for (dir = 0; dir < XFRM_POLICY_MAX * 2; dir++) {
struct xfrm_policy *pol;
struct hlist_node *entry;
struct hlist_head *table;
int i;
// 遍历inexact链表
hlist_for_each_entry(pol, entry,
&xfrm_policy_inexact[dir], bydst)
// 如果节点满足条件就删除挂接到垃圾链表
prune_one_bundle(pol, func, &gc_list);
// 循环所有方向
for (dir = 0; dir < XFRM_POLICY_MAX * 2; dir++) {
struct xfrm_policy *pol;
struct hlist_node *entry;
struct hlist_head *table;
int i;
// 遍历inexact链表
hlist_for_each_entry(pol, entry,
&xfrm_policy_inexact[dir], bydst)
// 如果节点满足条件就删除挂接到垃圾链表
prune_one_bundle(pol, func, &gc_list);
// 遍历目的地址HASH的链表
table = xfrm_policy_bydst[dir].table;
for (i = xfrm_policy_bydst[dir].hmask; i >= 0; i--) {
// 如果节点满足条件就删除挂接到垃圾链表
hlist_for_each_entry(pol, entry, table + i, bydst)
prune_one_bundle(pol, func, &gc_list);
}
}
read_unlock_bh(&xfrm_policy_lock);
// 如果搜集到的垃圾, 释放安全路由
while (gc_list) {
struct dst_entry *dst = gc_list;
gc_list = dst->next;
dst_free(dst);
}
}
// 没用的路由, 使用数为0
static int unused_bundle(struct dst_entry *dst)
{
return !atomic_read(&dst->__refcnt);
}
static int unused_bundle(struct dst_entry *dst)
{
return !atomic_read(&dst->__refcnt);
}
// 删除单个路由
static void prune_one_bundle(struct xfrm_policy *pol, int (*func)(struct dst_entry *), struct dst_entry **gc_list_p)
{
struct dst_entry *dst, **dstp;
// 策略写锁
write_lock(&pol->lock);
// 策略的路由项链表起点
dstp = &pol->bundles;
// 遍历链表
while ((dst=*dstp) != NULL) {
if (func(dst)) {
// 如果满足条件, 将节点从链表中删除, 添加到垃圾链表
*dstp = dst->next;
dst->next = *gc_list_p;
*gc_list_p = dst;
} else {
dstp = &dst->next;
}
}
write_unlock(&pol->lock);
}
static void prune_one_bundle(struct xfrm_policy *pol, int (*func)(struct dst_entry *), struct dst_entry **gc_list_p)
{
struct dst_entry *dst, **dstp;
// 策略写锁
write_lock(&pol->lock);
// 策略的路由项链表起点
dstp = &pol->bundles;
// 遍历链表
while ((dst=*dstp) != NULL) {
if (func(dst)) {
// 如果满足条件, 将节点从链表中删除, 添加到垃圾链表
*dstp = dst->next;
dst->next = *gc_list_p;
*gc_list_p = dst;
} else {
dstp = &dst->next;
}
}
write_unlock(&pol->lock);
}
5.8 杂项
这些杂项并不是策略的直接处理函数, 而是xfrm的一些相关处理, 只是也放在xfrm_policy.c中了.
5.8.1 协议处理类型处理
xfrm_type用来定义各种协议处理类型, 如AH,ESP, IPCOMP, IPIP等
// 登记协议处理类型, 返回0成功, 非0失败
int xfrm_register_type(struct xfrm_type *type, unsigned short family)
{
// 找到协议族相关的策略信息结构
struct xfrm_policy_afinfo *afinfo = xfrm_policy_lock_afinfo(family);
struct xfrm_type **typemap;
int err = 0;
int xfrm_register_type(struct xfrm_type *type, unsigned short family)
{
// 找到协议族相关的策略信息结构
struct xfrm_policy_afinfo *afinfo = xfrm_policy_lock_afinfo(family);
struct xfrm_type **typemap;
int err = 0;
if (unlikely(afinfo == NULL))
return -EAFNOSUPPORT;
// 策略信息结构中的类型数组
typemap = afinfo->type_map;
return -EAFNOSUPPORT;
// 策略信息结构中的类型数组
typemap = afinfo->type_map;
// 如果数组中相应协议对应元素非空, 则赋值, 否则发生错误
if (likely(typemap[type->proto] == NULL))
typemap[type->proto] = type;
else
err = -EEXIST;
xfrm_policy_unlock_afinfo(afinfo);
return err;
}
EXPORT_SYMBOL(xfrm_register_type);
if (likely(typemap[type->proto] == NULL))
typemap[type->proto] = type;
else
err = -EEXIST;
xfrm_policy_unlock_afinfo(afinfo);
return err;
}
EXPORT_SYMBOL(xfrm_register_type);
// 拆除协议处理类型, 返回0成功, 非0失败
int xfrm_unregister_type(struct xfrm_type *type, unsigned short family)
{
// 找到协议族相关的策略信息结构
struct xfrm_policy_afinfo *afinfo = xfrm_policy_lock_afinfo(family);
struct xfrm_type **typemap;
int err = 0;
if (unlikely(afinfo == NULL))
return -EAFNOSUPPORT;
// 策略信息结构中的类型数组
typemap = afinfo->type_map;
return -EAFNOSUPPORT;
// 策略信息结构中的类型数组
typemap = afinfo->type_map;
// 如果数组中相应协议对应元素等于要删除的结构, 元素清空, 否则发生错误
if (unlikely(typemap[type->proto] != type))
err = -ENOENT;
else
typemap[type->proto] = NULL;
xfrm_policy_unlock_afinfo(afinfo);
return err;
}
EXPORT_SYMBOL(xfrm_unregister_type);
if (unlikely(typemap[type->proto] != type))
err = -ENOENT;
else
typemap[type->proto] = NULL;
xfrm_policy_unlock_afinfo(afinfo);
return err;
}
EXPORT_SYMBOL(xfrm_unregister_type);
// 根据协议号和协议族查找类型
struct xfrm_type *xfrm_get_type(u8 proto, unsigned short family)
{
struct xfrm_policy_afinfo *afinfo;
struct xfrm_type **typemap;
struct xfrm_type *type;
int modload_attempted = 0;
struct xfrm_type *xfrm_get_type(u8 proto, unsigned short family)
{
struct xfrm_policy_afinfo *afinfo;
struct xfrm_type **typemap;
struct xfrm_type *type;
int modload_attempted = 0;
retry:
// 找到协议族相关的策略信息结构
afinfo = xfrm_policy_get_afinfo(family);
if (unlikely(afinfo == NULL))
return NULL;
// 策略信息结构中的类型数组
typemap = afinfo->type_map;
// 找到协议族相关的策略信息结构
afinfo = xfrm_policy_get_afinfo(family);
if (unlikely(afinfo == NULL))
return NULL;
// 策略信息结构中的类型数组
typemap = afinfo->type_map;
// 数组中对应指定协议的元素
type = typemap[proto];
// 增加type模块的使用计数
if (unlikely(type && !try_module_get(type->owner)))
type = NULL;
// 如果当前type为空, 则加载type的内核模块, 重新查找
if (!type && !modload_attempted) {
xfrm_policy_put_afinfo(afinfo);
request_module("xfrm-type-%d-%d",
(int) family, (int) proto);
modload_attempted = 1;
goto retry;
}
type = typemap[proto];
// 增加type模块的使用计数
if (unlikely(type && !try_module_get(type->owner)))
type = NULL;
// 如果当前type为空, 则加载type的内核模块, 重新查找
if (!type && !modload_attempted) {
xfrm_policy_put_afinfo(afinfo);
request_module("xfrm-type-%d-%d",
(int) family, (int) proto);
modload_attempted = 1;
goto retry;
}
xfrm_policy_put_afinfo(afinfo);
return type;
}
return type;
}
// 释放类型模块使用计数
void xfrm_put_type(struct xfrm_type *type)
{
module_put(type->owner);
}
void xfrm_put_type(struct xfrm_type *type)
{
module_put(type->owner);
}
5.8.2 协议模式处理
模式目前包括通道和传输两种.
// 登记模式, 返回0成功, 非0失败
int xfrm_register_mode(struct xfrm_mode *mode, int family)
{
struct xfrm_policy_afinfo *afinfo;
struct xfrm_mode **modemap;
int err;
if (unlikely(mode->encap >= XFRM_MODE_MAX))
return -EINVAL;
return -EINVAL;
// 找到协议族相关的策略信息结构
afinfo = xfrm_policy_lock_afinfo(family);
if (unlikely(afinfo == NULL))
return -EAFNOSUPPORT;
afinfo = xfrm_policy_lock_afinfo(family);
if (unlikely(afinfo == NULL))
return -EAFNOSUPPORT;
err = -EEXIST;
// 策略信息结构中的模式数组
modemap = afinfo->mode_map;
// 数组元素非空的话赋值, 返回成功
if (likely(modemap[mode->encap] == NULL)) {
modemap[mode->encap] = mode;
err = 0;
}
// 策略信息结构中的模式数组
modemap = afinfo->mode_map;
// 数组元素非空的话赋值, 返回成功
if (likely(modemap[mode->encap] == NULL)) {
modemap[mode->encap] = mode;
err = 0;
}
xfrm_policy_unlock_afinfo(afinfo);
return err;
}
EXPORT_SYMBOL(xfrm_register_mode);
return err;
}
EXPORT_SYMBOL(xfrm_register_mode);
// 拆除模式, 返回0成功, 非0失败
int xfrm_unregister_mode(struct xfrm_mode *mode, int family)
{
struct xfrm_policy_afinfo *afinfo;
struct xfrm_mode **modemap;
int err;
int xfrm_unregister_mode(struct xfrm_mode *mode, int family)
{
struct xfrm_policy_afinfo *afinfo;
struct xfrm_mode **modemap;
int err;
if (unlikely(mode->encap >= XFRM_MODE_MAX))
return -EINVAL;
return -EINVAL;
// 找到协议族相关的策略信息结构
afinfo = xfrm_policy_lock_afinfo(family);
if (unlikely(afinfo == NULL))
return -EAFNOSUPPORT;
afinfo = xfrm_policy_lock_afinfo(family);
if (unlikely(afinfo == NULL))
return -EAFNOSUPPORT;
err = -ENOENT;
// 策略信息结构中的模式数组
modemap = afinfo->mode_map;
// 数组元素等于要拆除的模式, 清空, 返回成功
if (likely(modemap[mode->encap] == mode)) {
modemap[mode->encap] = NULL;
err = 0;
}
// 策略信息结构中的模式数组
modemap = afinfo->mode_map;
// 数组元素等于要拆除的模式, 清空, 返回成功
if (likely(modemap[mode->encap] == mode)) {
modemap[mode->encap] = NULL;
err = 0;
}
xfrm_policy_unlock_afinfo(afinfo);
return err;
}
EXPORT_SYMBOL(xfrm_unregister_mode);
return err;
}
EXPORT_SYMBOL(xfrm_unregister_mode);
// 查找模式
struct xfrm_mode *xfrm_get_mode(unsigned int encap, int family)
{
struct xfrm_policy_afinfo *afinfo;
struct xfrm_mode *mode;
int modload_attempted = 0;
if (unlikely(encap >= XFRM_MODE_MAX))
return NULL;
return NULL;
retry:
// 找到协议族相关的策略信息结构
afinfo = xfrm_policy_get_afinfo(family);
if (unlikely(afinfo == NULL))
return NULL;
// 找到协议族相关的策略信息结构
afinfo = xfrm_policy_get_afinfo(family);
if (unlikely(afinfo == NULL))
return NULL;
// 策略信息结构中的模式数组
mode = afinfo->mode_map[encap];
// 增加模式模块的使用计数
if (unlikely(mode && !try_module_get(mode->owner)))
mode = NULL;
// 如果当前模式为空, 则加载模式对应的内核模块, 重新查找
if (!mode && !modload_attempted) {
xfrm_policy_put_afinfo(afinfo);
request_module("xfrm-mode-%d-%d", family, encap);
modload_attempted = 1;
goto retry;
}
mode = afinfo->mode_map[encap];
// 增加模式模块的使用计数
if (unlikely(mode && !try_module_get(mode->owner)))
mode = NULL;
// 如果当前模式为空, 则加载模式对应的内核模块, 重新查找
if (!mode && !modload_attempted) {
xfrm_policy_put_afinfo(afinfo);
request_module("xfrm-mode-%d-%d", family, encap);
modload_attempted = 1;
goto retry;
}
xfrm_policy_put_afinfo(afinfo);
return mode;
}
return mode;
}
// 释放模式模块使用计数
void xfrm_put_mode(struct xfrm_mode *mode)
{
module_put(mode->owner);
}
5.8.3 协议信息处理
// 登记协议信息结构
int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo)
{
int err = 0;
if (unlikely(afinfo == NULL))
return -EINVAL;
if (unlikely(afinfo->family >= NPROTO))
return -EAFNOSUPPORT;
write_lock_bh(&xfrm_policy_afinfo_lock);
// 数组中的对应协议的协议信息结构元素应该为空
if (unlikely(xfrm_policy_afinfo[afinfo->family] != NULL))
err = -ENOBUFS;
else {
// 安全路由操作结构
struct dst_ops *dst_ops = afinfo->dst_ops;
// 安全路由操作结构的参数和操作函数赋值
if (likely(dst_ops->kmem_cachep == NULL))
dst_ops->kmem_cachep = xfrm_dst_cache;
if (likely(dst_ops->check == NULL))
dst_ops->check = xfrm_dst_check;
if (likely(dst_ops->negative_advice == NULL))
dst_ops->negative_advice = xfrm_negative_advice;
if (likely(dst_ops->link_failure == NULL))
dst_ops->link_failure = xfrm_link_failure;
if (likely(afinfo->garbage_collect == NULL))
afinfo->garbage_collect = __xfrm_garbage_collect;
// 数组中的对应协议的协议信息结构元素填为协议信息结构
xfrm_policy_afinfo[afinfo->family] = afinfo;
}
write_unlock_bh(&xfrm_policy_afinfo_lock);
return err;
}
EXPORT_SYMBOL(xfrm_policy_register_afinfo);
int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo)
{
int err = 0;
if (unlikely(afinfo == NULL))
return -EINVAL;
if (unlikely(afinfo->family >= NPROTO))
return -EAFNOSUPPORT;
write_lock_bh(&xfrm_policy_afinfo_lock);
// 数组中的对应协议的协议信息结构元素应该为空
if (unlikely(xfrm_policy_afinfo[afinfo->family] != NULL))
err = -ENOBUFS;
else {
// 安全路由操作结构
struct dst_ops *dst_ops = afinfo->dst_ops;
// 安全路由操作结构的参数和操作函数赋值
if (likely(dst_ops->kmem_cachep == NULL))
dst_ops->kmem_cachep = xfrm_dst_cache;
if (likely(dst_ops->check == NULL))
dst_ops->check = xfrm_dst_check;
if (likely(dst_ops->negative_advice == NULL))
dst_ops->negative_advice = xfrm_negative_advice;
if (likely(dst_ops->link_failure == NULL))
dst_ops->link_failure = xfrm_link_failure;
if (likely(afinfo->garbage_collect == NULL))
afinfo->garbage_collect = __xfrm_garbage_collect;
// 数组中的对应协议的协议信息结构元素填为协议信息结构
xfrm_policy_afinfo[afinfo->family] = afinfo;
}
write_unlock_bh(&xfrm_policy_afinfo_lock);
return err;
}
EXPORT_SYMBOL(xfrm_policy_register_afinfo);
// 拆除协议信息结构
int xfrm_policy_unregister_afinfo(struct xfrm_policy_afinfo *afinfo)
{
int err = 0;
if (unlikely(afinfo == NULL))
return -EINVAL;
if (unlikely(afinfo->family >= NPROTO))
return -EAFNOSUPPORT;
write_lock_bh(&xfrm_policy_afinfo_lock);
if (likely(xfrm_policy_afinfo[afinfo->family] != NULL)) {
// 数组中的协议信息结构等于指定的信息结构
if (unlikely(xfrm_policy_afinfo[afinfo->family] != afinfo))
err = -EINVAL;
else {
// 清空协议信息数组元素和路由操作结构参数
struct dst_ops *dst_ops = afinfo->dst_ops;
xfrm_policy_afinfo[afinfo->family] = NULL;
dst_ops->kmem_cachep = NULL;
dst_ops->check = NULL;
dst_ops->negative_advice = NULL;
dst_ops->link_failure = NULL;
afinfo->garbage_collect = NULL;
}
}
write_unlock_bh(&xfrm_policy_afinfo_lock);
return err;
}
EXPORT_SYMBOL(xfrm_policy_unregister_afinfo);
// 查找协议信息结构, 加读锁
static struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family)
{
struct xfrm_policy_afinfo *afinfo;
if (unlikely(family >= NPROTO))
return NULL;
read_lock(&xfrm_policy_afinfo_lock);
// 获取指定协议位置处的协议信息结构
afinfo = xfrm_policy_afinfo[family];
// 如果该协议信息结构不存在, 解锁
if (unlikely(!afinfo))
read_unlock(&xfrm_policy_afinfo_lock);
return afinfo;
}
// 释放协议信息结构, 解读锁
static void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo)
{
read_unlock(&xfrm_policy_afinfo_lock);
}
// 协议信息结构加写锁, 返回指定的协议信息结构, 错误时返回NULL
static struct xfrm_policy_afinfo *xfrm_policy_lock_afinfo(unsigned int family)
{
struct xfrm_policy_afinfo *afinfo;
if (unlikely(family >= NPROTO))
return NULL;
write_lock_bh(&xfrm_policy_afinfo_lock);
// 获取指定协议位置处的协议信息结构
afinfo = xfrm_policy_afinfo[family];
// 如果该协议信息结构不存在, 解锁
if (unlikely(!afinfo))
write_unlock_bh(&xfrm_policy_afinfo_lock);
return afinfo;
}
// 协议信息结构解写锁
static void xfrm_policy_unlock_afinfo(struct xfrm_policy_afinfo *afinfo)
{
write_unlock_bh(&xfrm_policy_afinfo_lock);
}
static void xfrm_policy_unlock_afinfo(struct xfrm_policy_afinfo *afinfo)
{
write_unlock_bh(&xfrm_policy_afinfo_lock);
}
5.8.4 网卡回调
// 网卡通知结构
static struct notifier_block xfrm_dev_notifier = {
xfrm_dev_event,
NULL,
0
};
// 回调函数
static int xfrm_dev_event(struct notifier_block *this, unsigned long event, void *ptr)
{
switch (event) {
// 就只响应网卡停事件, 删除和网卡相关的所有安全路由项
case NETDEV_DOWN:
xfrm_flush_bundles();
}
return NOTIFY_DONE;
}
static int xfrm_dev_event(struct notifier_block *this, unsigned long event, void *ptr)
{
switch (event) {
// 就只响应网卡停事件, 删除和网卡相关的所有安全路由项
case NETDEV_DOWN:
xfrm_flush_bundles();
}
return NOTIFY_DONE;
}
static int xfrm_flush_bundles(void)
{
// 也是使用xfrm_prune_bundles()函数进行删除操作
// 条件函数是stale_bundle
xfrm_prune_bundles(stale_bundle);
return 0;
}
{
// 也是使用xfrm_prune_bundles()函数进行删除操作
// 条件函数是stale_bundle
xfrm_prune_bundles(stale_bundle);
return 0;
}
// 判断安全路由项是否可用
// 返回1表示不可用, 0表示可用
static int stale_bundle(struct dst_entry *dst)
{
return !xfrm_bundle_ok(NULL, (struct xfrm_dst *)dst, NULL, AF_UNSPEC, 0);
}
// 返回1表示不可用, 0表示可用
static int stale_bundle(struct dst_entry *dst)
{
return !xfrm_bundle_ok(NULL, (struct xfrm_dst *)dst, NULL, AF_UNSPEC, 0);
}
// 返回0表示不可用, 1表示可用
int xfrm_bundle_ok(struct xfrm_policy *pol, struct xfrm_dst *first,
struct flowi *fl, int family, int strict)
{
struct dst_entry *dst = &first->u.dst;
struct xfrm_dst *last;
u32 mtu;
int xfrm_bundle_ok(struct xfrm_policy *pol, struct xfrm_dst *first,
struct flowi *fl, int family, int strict)
{
struct dst_entry *dst = &first->u.dst;
struct xfrm_dst *last;
u32 mtu;
// 检查路由项
if (!dst_check(dst->path, ((struct xfrm_dst *)dst)->path_cookie) ||
// 检查网卡是否在运行
(dst->dev && !netif_running(dst->dev)))
return 0;
if (!dst_check(dst->path, ((struct xfrm_dst *)dst)->path_cookie) ||
// 检查网卡是否在运行
(dst->dev && !netif_running(dst->dev)))
return 0;
last = NULL;
do {
// 安全路由
struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
// 安全路由
struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
// 检查SA选择子是否匹配流结构
if (fl && !xfrm_selector_match(&dst->xfrm->sel, fl, family))
return 0;
if (fl && !security_xfrm_flow_state_match(fl, dst->xfrm, pol))
return 0;
// 检查SA状态是否合法
if (dst->xfrm->km.state != XFRM_STATE_VALID)
return 0;
if (xdst->genid != dst->xfrm->genid)
return 0;
if (fl && !xfrm_selector_match(&dst->xfrm->sel, fl, family))
return 0;
if (fl && !security_xfrm_flow_state_match(fl, dst->xfrm, pol))
return 0;
// 检查SA状态是否合法
if (dst->xfrm->km.state != XFRM_STATE_VALID)
return 0;
if (xdst->genid != dst->xfrm->genid)
return 0;
// 严格检查时, 检查非通道模式下的SA地址和流结构参数是否匹配
if (strict && fl && dst->xfrm->props.mode != XFRM_MODE_TUNNEL &&
!xfrm_state_addr_flow_check(dst->xfrm, fl, family))
return 0;
// 子路由项的MTU
mtu = dst_mtu(dst->child);
if (xdst->child_mtu_cached != mtu) {
last = xdst;
xdst->child_mtu_cached = mtu;
}
// 通用路由检查
if (!dst_check(xdst->route, xdst->route_cookie))
return 0;
if (strict && fl && dst->xfrm->props.mode != XFRM_MODE_TUNNEL &&
!xfrm_state_addr_flow_check(dst->xfrm, fl, family))
return 0;
// 子路由项的MTU
mtu = dst_mtu(dst->child);
if (xdst->child_mtu_cached != mtu) {
last = xdst;
xdst->child_mtu_cached = mtu;
}
// 通用路由检查
if (!dst_check(xdst->route, xdst->route_cookie))
return 0;
// 安全路由相关的普通路由的MTU
mtu = dst_mtu(xdst->route);
if (xdst->route_mtu_cached != mtu) {
last = xdst;
xdst->route_mtu_cached = mtu;
}
// 遍历安全路由链表
dst = dst->child;
} while (dst->xfrm);
mtu = dst_mtu(xdst->route);
if (xdst->route_mtu_cached != mtu) {
last = xdst;
xdst->route_mtu_cached = mtu;
}
// 遍历安全路由链表
dst = dst->child;
} while (dst->xfrm);
// last是最后一个和子路由和普通路由的MTU不同的安全路由, 一般都是相同的
if (likely(!last))
return 1;
if (likely(!last))
return 1;
// 调整各路由项中的MTU
mtu = last->child_mtu_cached;
for (;;) {
dst = &last->u.dst;
mtu = last->child_mtu_cached;
for (;;) {
dst = &last->u.dst;
mtu = xfrm_state_mtu(dst->xfrm, mtu);
if (mtu > last->route_mtu_cached)
mtu = last->route_mtu_cached;
dst->metrics[RTAX_MTU-1] = mtu;
if (mtu > last->route_mtu_cached)
mtu = last->route_mtu_cached;
dst->metrics[RTAX_MTU-1] = mtu;
if (last == first)
break;
<di
break;
发表评论
-
Linux发送函数dev_queue_xmit分析 --转
2010-12-14 21:44 14932当上层准备好一个包之后,交给下面这个函数处理: int de ... -
内核污染错误
2010-12-12 21:48 1871一些oops报告在程序记数器之后包含字符串'Tainted: ... -
EXPORT_SYMBOL 与 EXPORT_SYMBOL_GPL 转载
2010-12-12 15:04 122051.EXPORT_SYMBOL EXPORT_SYMB ... -
TCP和UDP在网络层实现的不同--基于linux内核 --转
2010-12-11 20:01 1372由于4层协议实 ... -
ipv6 分片
2010-12-07 21:13 5126519 static int ip6_fragment(str ... -
ipv6 处理扩展头
2010-12-06 09:47 3623160 static int ip6_input_finish ... -
IPV6详解 --转
2010-12-02 14:11 1252一、IPv6基本头 IPv6基本头标包含40字节 ... -
Linux内核中的IPSEC实现(7) ---转载
2010-11-16 20:19 4102本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中的IPSEC实现(6) --转载
2010-11-16 20:09 2242本文档的Copyleft归yfydz所 ... -
Linux内核中的IPSEC实现(5) ---转载
2010-11-15 15:48 4109本文档的Copyleft归yfydz所有,使用GPL发布 ... -
Linux内核中的IPSEC实现(4) ---转载
2010-11-15 15:46 1449本文档的Copyleft归yfydz ... -
Linux内核中的IPSEC实现(2) ---转载
2010-11-15 15:35 2914本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中的IPSEC实现(1) ---转载
2010-11-15 15:31 2797本文档的Copyleft归yfydz所有,使用GPL发布,可以 ...
相关推荐
总结以上知识点,本文对Linux内核加密框架从理论和实践两个层面进行了深入探讨,尤其重点介绍了算法模版的概念、使用方法,以及IPSec协议中的应用实例。此外,还提到了Linux内核版本相关信息和对文章内容的使用声明...
1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
8c71b76fb2ec10cf50fc6b0308d3dcfc_9545878e2b97a84b2e089ece58da9e82
Android逆向过程学习
内容概要:本文详细介绍了基于西门子S7-200 PLC的糖果包装控制系统的设计与实现。首先阐述了PLC在工业自动化领域的优势及其在糖果包装生产线中的重要性。接着深入探讨了系统的硬件连接方式,包括传感器、执行机构与PLC的具体接口配置。随后展示了关键的编程实现部分,如糖果计数、包装执行、送膜控制、称重判断以及热封温度控制等具体梯形图代码片段。此外,还分享了一些实用的经验技巧,如防止信号抖动、PID参数优化、故障诊断方法等。最后总结了该系统的优势,强调其对提高生产效率和产品质量的重要作用。 适合人群:从事工业自动化控制、PLC编程的技术人员,尤其是对小型PLC系统感兴趣的工程师。 使用场景及目标:适用于糖果制造企业,旨在提升包装生产线的自动化程度,确保高效稳定的生产过程,同时降低维护成本并提高产品一致性。 其他说明:文中不仅提供了详细的理论讲解和技术指导,还结合实际案例进行了经验分享,有助于读者更好地理解和掌握相关知识。
内容概要:本文详细介绍了参与西门子杯比赛中关于三部十层电梯系统的博图V15.1程序设计及其WinCC画面展示的内容。文中不仅展示了电梯系统的基本架构,如抢单逻辑、方向决策、状态机管理等核心算法(采用SCL语言编写),还分享了许多实际调试过程中遇到的问题及解决方案,例如未初始化变量导致的异常行为、状态机遗漏空闲状态、WinCC画面动态显示的挑战以及通信配置中的ASCII码解析错误等问题。此外,作者还特别提到一些创意性的设计,如电梯同时到达同一层时楼层显示器变为闪烁爱心的效果,以及节能模式下电梯自动停靠中间楼层的功能。 适合人群:对PLC编程、工业自动化控制、电梯调度算法感兴趣的工程技术人员,尤其是准备参加类似竞赛的学生和技术爱好者。 使用场景及目标:适用于希望深入了解PLC编程实践、掌握电梯群控系统的设计思路和技术要点的人士。通过学习本文可以更好地理解如何利用PLC进行复杂的机电一体化项目的开发,提高解决实际问题的能力。 其他说明:文章风格幽默诙谐,将严肃的技术话题融入轻松的生活化比喻之中,使得原本枯燥的专业知识变得生动有趣。同时,文中提供的经验教训对于从事相关领域的工作者来说非常宝贵,能够帮助他们少走弯路并激发更多创新思维。
慧荣量产工具合集.zip
内容概要:本文详细介绍了永磁同步电机(PMSM)的FOC(磁场定向控制)和SVPWM(空间矢量脉宽调制)算法的仿真模型。首先解释了FOC的基本原理及其核心的坐标变换(Clark变换和Park变换),并给出了相应的Python代码实现。接下来探讨了SVPWM算法的工作机制,包括扇区判断和占空比计算的方法。此外,文章还讨论了电机的PI双闭环控制结构,即速度环和电流环的设计与实现。文中不仅提供了详细的理论背景,还分享了一些实用的编程技巧和注意事项,帮助读者更好地理解和应用这些算法。 适合人群:电气工程专业学生、从事电机控制系统开发的技术人员以及对永磁同步电机控制感兴趣的科研人员。 使用场景及目标:① 学习和掌握永磁同步电机的FOC控制和SVPWM算法的具体实现;② 提供丰富的代码示例和实践经验,便于快速搭建和调试仿真模型;③ 探讨不同参数设置对电机性能的影响,提高系统的稳定性和效率。 其他说明:文章强调了在实际应用中需要注意的一些细节问题,如坐标变换中的系数选择、SVPWM算法中的扇区判断优化以及PI控制器的参数调整等。同时,鼓励读者通过动手实验来加深对各个模块的理解。
# 压缩文件中包含: 中文文档 jar包下载地址 Maven依赖 Gradle依赖 源代码下载地址 # 本文件关键字: jar中文文档.zip,java,jar包,Maven,第三方jar包,组件,开源组件,第三方组件,Gradle,中文API文档,手册,开发手册,使用手册,参考手册 # 使用方法: 解压最外层zip,再解压其中的zip包,双击 【index.html】 文件,即可用浏览器打开、进行查看。 # 特殊说明: ·本文档为人性化翻译,精心制作,请放心使用。 ·只翻译了该翻译的内容,如:注释、说明、描述、用法讲解 等; ·不该翻译的内容保持原样,如:类名、方法名、包名、类型、关键字、代码 等。 # 温馨提示: (1)为了防止解压后路径太长导致浏览器无法打开,推荐在解压时选择“解压到当前文件夹”(放心,自带文件夹,文件不会散落一地); (2)有时,一套Java组件会有多个jar,所以在下载前,请仔细阅读本篇描述,以确保这就是你需要的文件;
Android逆向过程学习
1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
3dmax插件
# 【spring-ai-autoconfigure-vector-store-qdrant-1.0.0-M7.jar中文文档.zip】 中包含: 中文文档:【spring-ai-autoconfigure-vector-store-qdrant-1.0.0-M7-javadoc-API文档-中文(简体)版.zip】 jar包下载地址:【spring-ai-autoconfigure-vector-store-qdrant-1.0.0-M7.jar下载地址(官方地址+国内镜像地址).txt】 Maven依赖:【spring-ai-autoconfigure-vector-store-qdrant-1.0.0-M7.jar Maven依赖信息(可用于项目pom.xml).txt】 Gradle依赖:【spring-ai-autoconfigure-vector-store-qdrant-1.0.0-M7.jar Gradle依赖信息(可用于项目build.gradle).txt】 源代码下载地址:【spring-ai-autoconfigure-vector-store-qdrant-1.0.0-M7-sources.jar下载地址(官方地址+国内镜像地址).txt】 # 本文件关键字: spring-ai-autoconfigure-vector-store-qdrant-1.0.0-M7.jar中文文档.zip,java,spring-ai-autoconfigure-vector-store-qdrant-1.0.0-M7.jar,org.springframework.ai,spring-ai-autoconfigure-vector-store-qdrant,1.0.0-M7,org.springframework.ai.vectorstore.qdr
1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
内容概要:本文详细介绍了平方根容积卡尔曼滤波(SRCKF)在永磁同步电机(PMSM)控制系统中的应用及其相对于传统CKF的优势。文章首先指出传统CKF在处理协方差矩阵时存在的数值不稳定性和非正定问题,导致系统性能下降。接着,作者通过引入SRCKF,利用Cholesky分解和QR分解来确保协方差矩阵的正定性,从而提高状态估计的精度和稳定性。文中展示了具体的电机模型和状态方程,并提供了详细的代码实现,包括状态预测、容积点生成以及观测更新等关键步骤。此外,文章还分享了实际调试过程中遇到的问题及解决方案,如选择合适的矩阵分解库和处理电机参数敏感性。最终,通过实验数据对比,证明了SRCKF在突加负载情况下的优越表现。 适合人群:从事永磁同步电机控制研究的技术人员、研究生及以上学历的研究者。 使用场景及目标:适用于需要高精度状态估计的永磁同步电机控制系统的设计与优化,特别是在处理非线性问题和提高数值稳定性方面。 其他说明:文章引用了相关领域的权威文献,如Arasaratnam的TAC论文和Zhong的《PMSM无传感器控制综述》,并强调了实际工程实践中代码调试的重要性。
# 【tokenizers-***.jar***文档.zip】 中包含: ***文档:【tokenizers-***-javadoc-API文档-中文(简体)版.zip】 jar包下载地址:【tokenizers-***.jar下载地址(官方地址+国内镜像地址).txt】 Maven依赖:【tokenizers-***.jar Maven依赖信息(可用于项目pom.xml).txt】 Gradle依赖:【tokenizers-***.jar Gradle依赖信息(可用于项目build.gradle).txt】 源代码下载地址:【tokenizers-***-sources.jar下载地址(官方地址+国内镜像地址).txt】 # 本文件关键字: tokenizers-***.jar***文档.zip,java,tokenizers-***.jar,ai.djl.huggingface,tokenizers,***,ai.djl.engine.rust,jar包,Maven,第三方jar包,组件,开源组件,第三方组件,Gradle,djl,huggingface,中文API文档,手册,开发手册,使用手册,参考手册 # 使用方法: 解压 【tokenizers-***.jar***文档.zip】,再解压其中的 【tokenizers-***-javadoc-API文档-中文(简体)版.zip】,双击 【index.html】 文件,即可用浏览器打开、进行查看。 # 特殊说明: ·本文档为人性化翻译,精心制作,请放心使用。 ·只翻译了该翻译的内容,如:注释、说明、描述、用法讲解 等; ·不该翻译的内容保持原样,如:类名、方法名、包名、类型、关键字、代码 等。 # 温馨提示: (1)为了防止解压后路径太长导致浏览器无法打开,推荐在解压时选择“解压到当前文件夹”(放心,自带文件夹,文件不会散落一地); (2)有时,一套Java组件会有多个jar,所以在下载前,请仔细阅读本篇描述,以确保这就是你需要的文件; # Maven依赖: ``` <dependency> <groupId>ai.djl.huggingface</groupId> <artifactId>tokenizers</artifactId> <version>***</version> </dependency> ``` # Gradle依赖: ``` Gradle: implementation group: 'ai.djl.huggingface', name: 'tokenizers', version: '***' Gradle (Short): implementation 'ai.djl.huggingface:tokenizers:***' Gradle (Kotlin): implementation("ai.djl.huggingface:tokenizers:***") ``` # 含有的 Java package(包): ``` ai.djl.engine.rust ai.djl.engine.rust.zoo ai.djl.huggingface.tokenizers ai.djl.huggingface.tokenizers.jni ai.djl.huggingface.translator ai.djl.huggingface.zoo ``` # 含有的 Java class(类): ``` ai.djl.engine.rust.RsEngine ai.djl.engine.rust.RsEngineProvider ai.djl.engine.rust.RsModel ai.djl.engine.rust.RsNDArray ai.djl.engine.rust.RsNDArrayEx ai.djl.engine.rust.RsNDArrayIndexer ai.djl.engine.rust.RsNDManager ai.djl.engine.rust.RsSymbolBlock ai.djl.engine.rust.RustLibrary ai.djl.engine.rust.zoo.RsModelZoo ai.djl.engine.rust.zoo.RsZooProvider ai.djl.huggingface.tokenizers.Encoding ai.djl.huggingface.tokenizers.HuggingFaceTokenizer ai.djl.huggingface.tokenizers.HuggingFaceTokenizer.Builder ai.djl.hu
3
pchook源码纯源码不是dll
# 【spring-ai-azure-store-1.0.0-M7.jar中文-英文对照文档.zip】 中包含: 中文-英文对照文档:【spring-ai-azure-store-1.0.0-M7-javadoc-API文档-中文(简体)-英语-对照版.zip】 jar包下载地址:【spring-ai-azure-store-1.0.0-M7.jar下载地址(官方地址+国内镜像地址).txt】 Maven依赖:【spring-ai-azure-store-1.0.0-M7.jar Maven依赖信息(可用于项目pom.xml).txt】 Gradle依赖:【spring-ai-azure-store-1.0.0-M7.jar Gradle依赖信息(可用于项目build.gradle).txt】 源代码下载地址:【spring-ai-azure-store-1.0.0-M7-sources.jar下载地址(官方地址+国内镜像地址).txt】 # 本文件关键字: spring-ai-azure-store-1.0.0-M7.jar中文-英文对照文档.zip,java,spring-ai-azure-store-1.0.0-M7.jar,org.springframework.ai,spring-ai-azure-store,1.0.0-M7,org.springframework.ai.vectorstore.azure,jar包,Maven,第三方jar包,组件,开源组件,第三方组件,Gradle,springframework,spring,ai,azure,store,中文-英文对照API文档,手册,开发手册,使用手册,参考手册 # 使用方法: 解压 【spring-ai-azure-store-1.0.0-M7.jar中文-英文对照文档.zip】,再解
内容概要:本文档是关于信捷电气XD、XL系列可编程序控制器的用户手册(硬件篇)。手册详细介绍了该系列PLC的硬件特性,包括产品概述、本体规格参数、系统构成、电源及输入输出规格、运行调试与维护、软元件切换等内容。此外,还提供了丰富的附录信息,如特殊软元件地址及功能、指令一览表、PLC功能配置表和常见问题解答。手册强调了安全操作的重要性,列出了多个安全注意事项,确保用户在正确环境下安装和使用设备,避免潜在风险。 适合人群:具备一定电气知识的专业人士,尤其是从事自动化控制系统设计、安装、调试及维护的技术人员。 使用场景及目标:①帮助用户了解XD、XL系列PLC的硬件特性和规格参数;②指导用户正确安装、接线、调试和维护设备;③提供详细的故障排查指南和技术支持信息,确保设备稳定运行;④为用户提供编程和指令使用的参考资料。 其他说明:手册不仅涵盖了硬件方面的内容,还涉及到了一些基础的软件编程概念,但更深入的编程指导请参考相关软件篇手册。用户在使用过程中遇到问题可以通过提供的联系方式获得技术支持。手册中的内容会定期更新,以适应产品改进和技术发展的需求。