msn: [email protected]
来源: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); ...... 待续 ......