📄 xfrm_policy.c
字号:
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 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; hlist_for_each_entry(pol, entry, &xfrm_policy_inexact[dir], bydst) prune_one_bundle(pol, func, &gc_list); 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); }}static int unused_bundle(struct dst_entry *dst){ return !atomic_read(&dst->__refcnt);}static void __xfrm_garbage_collect(void){ xfrm_prune_bundles(unused_bundle);}static int xfrm_flush_bundles(void){ xfrm_prune_bundles(stale_bundle); return 0;}void xfrm_init_pmtu(struct dst_entry *dst){ do { struct xfrm_dst *xdst = (struct xfrm_dst *)dst; u32 pmtu, route_mtu_cached; pmtu = dst_mtu(dst->child); xdst->child_mtu_cached = pmtu; pmtu = xfrm_state_mtu(dst->xfrm, pmtu); route_mtu_cached = dst_mtu(xdst->route); xdst->route_mtu_cached = route_mtu_cached; if (pmtu > route_mtu_cached) pmtu = route_mtu_cached; dst->metrics[RTAX_MTU-1] = pmtu; } while ((dst = dst->next));}EXPORT_SYMBOL(xfrm_init_pmtu);/* Check that the bundle accepts the flow and its components are * still valid. */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;#ifdef CONFIG_XFRM_SUB_POLICY if (fl) { if (first->origin && !flow_cache_uli_match(first->origin, fl)) return 0; if (first->partner && !xfrm_selector_match(first->partner, fl, family)) return 0; }#endif last = NULL; do { struct xfrm_dst *xdst = (struct xfrm_dst *)dst; if (fl && !xfrm_selector_match(&dst->xfrm->sel, fl, family)) return 0; if (fl && pol && !security_xfrm_state_pol_flow_match(dst->xfrm, pol, fl)) return 0; if (dst->xfrm->km.state != XFRM_STATE_VALID) return 0; if (xdst->genid != dst->xfrm->genid) return 0; if (strict && fl && !(dst->xfrm->outer_mode->flags & XFRM_MODE_FLAG_TUNNEL) && !xfrm_state_addr_flow_check(dst->xfrm, fl, family)) return 0; 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 = dst_mtu(xdst->route); if (xdst->route_mtu_cached != mtu) { last = xdst; xdst->route_mtu_cached = mtu; } dst = dst->child; } while (dst->xfrm); if (likely(!last)) return 1; 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 (last == first) break; last = (struct xfrm_dst *)last->u.dst.next; last->child_mtu_cached = mtu; } return 1;}EXPORT_SYMBOL(xfrm_bundle_ok);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);}static int xfrm_dev_event(struct notifier_block *this, unsigned long event, void *ptr){ struct net_device *dev = ptr; if (dev->nd_net != &init_net) return NOTIFY_DONE; switch (event) { case NETDEV_DOWN: xfrm_flush_bundles(); } return NOTIFY_DONE;}static struct notifier_block xfrm_dev_notifier = { xfrm_dev_event, NULL, 0};static void __init xfrm_policy_init(void){ unsigned int hmask, sz; int dir; xfrm_dst_cache = kmem_cache_create("xfrm_dst_cache", sizeof(struct xfrm_dst), 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL); hmask = 8 - 1; sz = (hmask+1) * sizeof(struct hlist_head); xfrm_policy_byidx = xfrm_hash_alloc(sz); xfrm_idx_hmask = hmask; if (!xfrm_policy_byidx) panic("XFRM: failed to allocate byidx hash\n"); for (dir = 0; dir < XFRM_POLICY_MAX * 2; dir++) { struct xfrm_policy_hash *htab; INIT_HLIST_HEAD(&xfrm_policy_inexact[dir]); htab = &xfrm_policy_bydst[dir]; htab->table = xfrm_hash_alloc(sz); htab->hmask = hmask; if (!htab->table) panic("XFRM: failed to allocate bydst hash\n"); } INIT_WORK(&xfrm_policy_gc_work, xfrm_policy_gc_task); register_netdevice_notifier(&xfrm_dev_notifier);}void __init xfrm_init(void){ xfrm_state_init(); xfrm_policy_init(); xfrm_input_init();}#ifdef CONFIG_AUDITSYSCALLstatic inline void xfrm_audit_common_policyinfo(struct xfrm_policy *xp, struct audit_buffer *audit_buf){ if (xp->security) audit_log_format(audit_buf, " sec_alg=%u sec_doi=%u sec_obj=%s", xp->security->ctx_alg, xp->security->ctx_doi, xp->security->ctx_str); switch(xp->selector.family) { case AF_INET: audit_log_format(audit_buf, " src=%u.%u.%u.%u dst=%u.%u.%u.%u", NIPQUAD(xp->selector.saddr.a4), NIPQUAD(xp->selector.daddr.a4)); break; case AF_INET6: { struct in6_addr saddr6, daddr6; memcpy(&saddr6, xp->selector.saddr.a6, sizeof(struct in6_addr)); memcpy(&daddr6, xp->selector.daddr.a6, sizeof(struct in6_addr)); audit_log_format(audit_buf, " src=" NIP6_FMT " dst=" NIP6_FMT, NIP6(saddr6), NIP6(daddr6)); } break; }}voidxfrm_audit_policy_add(struct xfrm_policy *xp, int result, u32 auid, u32 sid){ struct audit_buffer *audit_buf; extern int audit_enabled; if (audit_enabled == 0) return; audit_buf = xfrm_audit_start(auid, sid); if (audit_buf == NULL) return; audit_log_format(audit_buf, " op=SPD-add res=%u", result); xfrm_audit_common_policyinfo(xp, audit_buf); audit_log_end(audit_buf);}EXPORT_SYMBOL_GPL(xfrm_audit_policy_add);voidxfrm_audit_policy_delete(struct xfrm_policy *xp, int result, u32 auid, u32 sid){ struct audit_buffer *audit_buf; extern int audit_enabled; if (audit_enabled == 0) return; audit_buf = xfrm_audit_start(auid, sid); if (audit_buf == NULL) return; audit_log_format(audit_buf, " op=SPD-delete res=%u", result); xfrm_audit_common_policyinfo(xp, audit_buf); audit_log_end(audit_buf);}EXPORT_SYMBOL_GPL(xfrm_audit_policy_delete);#endif#ifdef CONFIG_XFRM_MIGRATEstatic int xfrm_migrate_selector_match(struct xfrm_selector *sel_cmp, struct xfrm_selector *sel_tgt){ if (sel_cmp->proto == IPSEC_ULPROTO_ANY) { if (sel_tgt->family == sel_cmp->family && xfrm_addr_cmp(&sel_tgt->daddr, &sel_cmp->daddr, sel_cmp->family) == 0 && xfrm_addr_cmp(&sel_tgt->saddr, &sel_cmp->saddr, sel_cmp->family) == 0 && sel_tgt->prefixlen_d == sel_cmp->prefixlen_d && sel_tgt->prefixlen_s == sel_cmp->prefixlen_s) { return 1; } } else { if (memcmp(sel_tgt, sel_cmp, sizeof(*sel_tgt)) == 0) { return 1; } } return 0;}static struct xfrm_policy * xfrm_migrate_policy_find(struct xfrm_selector *sel, u8 dir, u8 type){ struct xfrm_policy *pol, *ret = NULL; struct hlist_node *entry; struct hlist_head *chain; u32 priority = ~0U; read_lock_bh(&xfrm_policy_lock); chain = policy_hash_direct(&sel->daddr, &sel->saddr, sel->family, dir); hlist_for_each_entry(pol, entry, chain, bydst) { if (xfrm_migrate_selector_match(sel, &pol->selector) && pol->type == type) { ret = pol; priority = ret->priority; break; } } chain = &xfrm_policy_inexact[dir]; hlist_for_each_entry(pol, entry, chain, bydst) { if (xfrm_migrate_selector_match(sel, &pol->selector) && pol->type == type && pol->priority < priority) { ret = pol; break; } } if (ret) xfrm_pol_hold(ret); read_unlock_bh(&xfrm_policy_lock); return ret;}static int migrate_tmpl_match(struct xfrm_migrate *m, struct xfrm_tmpl *t){ int match = 0; if (t->mode == m->mode && t->id.proto == m->proto && (m->reqid == 0 || t->reqid == m->reqid)) { switch (t->mode) { case XFRM_MODE_TUNNEL: case XFRM_MODE_BEET: if (xfrm_addr_cmp(&t->id.daddr, &m->old_daddr, m->old_family) == 0 && xfrm_addr_cmp(&t->saddr, &m->old_saddr, m->old_family) == 0) { match = 1; } break; case XFRM_MODE_TRANSPORT: /* in case of transport mode, template does not store any IP addresses, hence we just compare mode and protocol */ match = 1; break; default: break; } } return match;}/* update endpoint address(es) of template(s) */static int xfrm_policy_migrate(struct xfrm_policy *pol, struct xfrm_migrate *m, int num_migrate){ struct xfrm_migrate *mp; struct dst_entry *dst; int i, j, n = 0; write_lock_bh(&pol->lock); if (unlikely(pol->dead)) { /* target policy has been deleted */ write_unlock_bh(&pol->lock); return -ENOENT; } for (i = 0; i < pol->xfrm_nr; i++) { for (j = 0, mp = m; j < num_migrate; j++, mp++) { if (!migrate_tmpl_match(mp, &pol->xfrm_vec[i])) continue; n++; if (pol->xfrm_vec[i].mode != XFRM_MODE_TUNNEL && pol->xfrm_vec[i].mode != XFRM_MODE_BEET) continue; /* update endpoints */ memcpy(&pol->xfrm_vec[i].id.daddr, &mp->new_daddr, sizeof(pol->xfrm_vec[i].id.daddr)); memcpy(&pol->xfrm_vec[i].saddr, &mp->new_saddr, sizeof(pol->xfrm_vec[i].saddr)); pol->xfrm_vec[i].encap_family = mp->new_family; /* flush bundles */ while ((dst = pol->bundles) != NULL) { pol->bundles = dst->next; dst_free(dst); } } } write_unlock_bh(&pol->lock); if (!n) return -ENODATA; return 0;}static int xfrm_migrate_check(struct xfrm_migrate *m, int num_migrate){ int i, j; if (num_migrate < 1 || num_migrate > XFRM_MAX_DEPTH) return -EINVAL; for (i = 0; i < num_migrate; i++) { if ((xfrm_addr_cmp(&m[i].old_daddr, &m[i].new_daddr, m[i].old_family) == 0) && (xfrm_addr_cmp(&m[i].old_saddr, &m[i].new_saddr, m[i].old_family) == 0)) return -EINVAL; if (xfrm_addr_any(&m[i].new_daddr, m[i].new_family) || xfrm_addr_any(&m[i].new_saddr, m[i].new_family)) return -EINVAL; /* check if there is any duplicated entry */ for (j = i + 1; j < num_migrate; j++) { if (!memcmp(&m[i].old_daddr, &m[j].old_daddr, sizeof(m[i].old_daddr)) && !memcmp(&m[i].old_saddr, &m[j].old_saddr, sizeof(m[i].old_saddr)) && m[i].proto == m[j].proto && m[i].mode == m[j].mode && m[i].reqid == m[j].reqid && m[i].old_family == m[j].old_family) return -EINVAL; } } return 0;}int xfrm_migrate(struct xfrm_selector *sel, u8 dir, u8 type, struct xfrm_migrate *m, int num_migrate){ int i, err, nx_cur = 0, nx_new = 0; struct xfrm_policy *pol = NULL; struct xfrm_state *x, *xc; struct xfrm_state *x_cur[XFRM_MAX_DEPTH]; struct xfrm_state *x_new[XFRM_MAX_DEPTH]; struct xfrm_migrate *mp; if ((err = xfrm_migrate_check(m, num_migrate)) < 0) goto out; /* Stage 1 - find policy */ if ((pol = xfrm_migrate_policy_find(sel, dir, type)) == NULL) { err = -ENOENT; goto out; } /* Stage 2 - find and update state(s) */ for (i = 0, mp = m; i < num_migrate; i++, mp++) { if ((x = xfrm_migrate_state_find(mp))) { x_cur[nx_cur] = x; nx_cur++; if ((xc = xfrm_state_migrate(x, mp))) { x_new[nx_new] = xc; nx_new++; } else { err = -ENODATA; goto restore_state; } } } /* Stage 3 - update policy */ if ((err = xfrm_policy_migrate(pol, m, num_migrate)) < 0) goto restore_state; /* Stage 4 - delete old state(s) */ if (nx_cur) { xfrm_states_put(x_cur, nx_cur); xfrm_states_delete(x_cur, nx_cur); } /* Stage 5 - announce */ km_migrate(sel, dir, type, m, num_migrate); xfrm_pol_put(pol); return 0;out: return err;restore_state: if (pol) xfrm_pol_put(pol); if (nx_cur) xfrm_states_put(x_cur, nx_cur); if (nx_new) xfrm_states_delete(x_new, nx_new); return err;}EXPORT_SYMBOL(xfrm_migrate);#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -