📄 xfrm_state.c
字号:
x->replay_maxdiff = 0; spin_lock_init(&x->lock); } return x;}EXPORT_SYMBOL(xfrm_state_alloc);void __xfrm_state_destroy(struct xfrm_state *x){ BUG_TRAP(x->km.state == XFRM_STATE_DEAD); spin_lock_bh(&xfrm_state_gc_lock); hlist_add_head(&x->bydst, &xfrm_state_gc_list); spin_unlock_bh(&xfrm_state_gc_lock); schedule_work(&xfrm_state_gc_work);}EXPORT_SYMBOL(__xfrm_state_destroy);int __xfrm_state_delete(struct xfrm_state *x){ int err = -ESRCH; if (x->km.state != XFRM_STATE_DEAD) { x->km.state = XFRM_STATE_DEAD; spin_lock(&xfrm_state_lock); hlist_del(&x->bydst); hlist_del(&x->bysrc); if (x->id.spi) hlist_del(&x->byspi); xfrm_state_num--; spin_unlock(&xfrm_state_lock); /* All xfrm_state objects are created by xfrm_state_alloc. * The xfrm_state_alloc call gives a reference, and that * is what we are dropping here. */ xfrm_state_put(x); err = 0; } return err;}EXPORT_SYMBOL(__xfrm_state_delete);int xfrm_state_delete(struct xfrm_state *x){ int err; spin_lock_bh(&x->lock); err = __xfrm_state_delete(x); spin_unlock_bh(&x->lock); return err;}EXPORT_SYMBOL(xfrm_state_delete);#ifdef CONFIG_SECURITY_NETWORK_XFRMstatic inline intxfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info){ int i, err = 0; for (i = 0; i <= xfrm_state_hmask; i++) { struct hlist_node *entry; struct xfrm_state *x; hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) { if (xfrm_id_proto_match(x->id.proto, proto) && (err = security_xfrm_state_delete(x)) != 0) { xfrm_audit_state_delete(x, 0, audit_info->loginuid, audit_info->secid); return err; } } } return err;}#elsestatic inline intxfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info){ return 0;}#endifint xfrm_state_flush(u8 proto, struct xfrm_audit *audit_info){ int i, err = 0; spin_lock_bh(&xfrm_state_lock); err = xfrm_state_flush_secctx_check(proto, audit_info); if (err) goto out; for (i = 0; i <= xfrm_state_hmask; i++) { struct hlist_node *entry; struct xfrm_state *x;restart: hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) { if (!xfrm_state_kern(x) && xfrm_id_proto_match(x->id.proto, proto)) { xfrm_state_hold(x); spin_unlock_bh(&xfrm_state_lock); err = xfrm_state_delete(x); xfrm_audit_state_delete(x, err ? 0 : 1, audit_info->loginuid, audit_info->secid); xfrm_state_put(x); spin_lock_bh(&xfrm_state_lock); goto restart; } } } err = 0;out: spin_unlock_bh(&xfrm_state_lock); wake_up(&km_waitq); return err;}EXPORT_SYMBOL(xfrm_state_flush);void xfrm_sad_getinfo(struct xfrmk_sadinfo *si){ spin_lock_bh(&xfrm_state_lock); si->sadcnt = xfrm_state_num; si->sadhcnt = xfrm_state_hmask; si->sadhmcnt = xfrm_state_hashmax; spin_unlock_bh(&xfrm_state_lock);}EXPORT_SYMBOL(xfrm_sad_getinfo);static intxfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl, struct xfrm_tmpl *tmpl, xfrm_address_t *daddr, xfrm_address_t *saddr, unsigned short family){ struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); if (!afinfo) return -1; afinfo->init_tempsel(x, fl, tmpl, daddr, saddr); xfrm_state_put_afinfo(afinfo); return 0;}static struct xfrm_state *__xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family){ unsigned int h = xfrm_spi_hash(daddr, spi, proto, family); struct xfrm_state *x; struct hlist_node *entry; hlist_for_each_entry(x, entry, xfrm_state_byspi+h, byspi) { if (x->props.family != family || x->id.spi != spi || x->id.proto != proto) continue; switch (family) { case AF_INET: if (x->id.daddr.a4 != daddr->a4) continue; break; case AF_INET6: if (!ipv6_addr_equal((struct in6_addr *)daddr, (struct in6_addr *) x->id.daddr.a6)) continue; break; } xfrm_state_hold(x); return x; } return NULL;}static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family){ unsigned int h = xfrm_src_hash(daddr, saddr, family); struct xfrm_state *x; struct hlist_node *entry; hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) { if (x->props.family != family || x->id.proto != proto) continue; switch (family) { case AF_INET: if (x->id.daddr.a4 != daddr->a4 || x->props.saddr.a4 != saddr->a4) continue; break; case AF_INET6: if (!ipv6_addr_equal((struct in6_addr *)daddr, (struct in6_addr *) x->id.daddr.a6) || !ipv6_addr_equal((struct in6_addr *)saddr, (struct in6_addr *) x->props.saddr.a6)) continue; break; } xfrm_state_hold(x); return x; } return NULL;}static inline struct xfrm_state *__xfrm_state_locate(struct xfrm_state *x, int use_spi, int family){ if (use_spi) return __xfrm_state_lookup(&x->id.daddr, x->id.spi, x->id.proto, family); else return __xfrm_state_lookup_byaddr(&x->id.daddr, &x->props.saddr, x->id.proto, family);}static void xfrm_hash_grow_check(int have_hash_collision){ if (have_hash_collision && (xfrm_state_hmask + 1) < xfrm_state_hashmax && xfrm_state_num > xfrm_state_hmask) schedule_work(&xfrm_hash_work);}struct xfrm_state *xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, struct flowi *fl, struct xfrm_tmpl *tmpl, struct xfrm_policy *pol, int *err, unsigned short family){ unsigned int h = xfrm_dst_hash(daddr, saddr, tmpl->reqid, family); struct hlist_node *entry; struct xfrm_state *x, *x0; int acquire_in_progress = 0; int error = 0; struct xfrm_state *best = NULL; spin_lock_bh(&xfrm_state_lock); hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) { if (x->props.family == family && x->props.reqid == tmpl->reqid && !(x->props.flags & XFRM_STATE_WILDRECV) && xfrm_state_addr_check(x, daddr, saddr, family) && tmpl->mode == x->props.mode && tmpl->id.proto == x->id.proto && (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) { /* Resolution logic: 1. There is a valid state with matching selector. Done. 2. Valid state with inappropriate selector. Skip. Entering area of "sysdeps". 3. If state is not valid, selector is temporary, it selects only session which triggered previous resolution. Key manager will do something to install a state with proper selector. */ if (x->km.state == XFRM_STATE_VALID) { if (!xfrm_selector_match(&x->sel, fl, x->sel.family) || !security_xfrm_state_pol_flow_match(x, pol, fl)) continue; if (!best || best->km.dying > x->km.dying || (best->km.dying == x->km.dying && best->curlft.add_time < x->curlft.add_time)) best = x; } else if (x->km.state == XFRM_STATE_ACQ) { acquire_in_progress = 1; } else if (x->km.state == XFRM_STATE_ERROR || x->km.state == XFRM_STATE_EXPIRED) { if (xfrm_selector_match(&x->sel, fl, x->sel.family) && security_xfrm_state_pol_flow_match(x, pol, fl)) error = -ESRCH; } } } x = best; if (!x && !error && !acquire_in_progress) { if (tmpl->id.spi && (x0 = __xfrm_state_lookup(daddr, tmpl->id.spi, tmpl->id.proto, family)) != NULL) { xfrm_state_put(x0); error = -EEXIST; goto out; } x = xfrm_state_alloc(); if (x == NULL) { error = -ENOMEM; goto out; } /* Initialize temporary selector matching only * to current session. */ xfrm_init_tempsel(x, fl, tmpl, daddr, saddr, family); error = security_xfrm_state_alloc_acquire(x, pol->security, fl->secid); if (error) { x->km.state = XFRM_STATE_DEAD; xfrm_state_put(x); x = NULL; goto out; } if (km_query(x, tmpl, pol) == 0) { x->km.state = XFRM_STATE_ACQ; hlist_add_head(&x->bydst, xfrm_state_bydst+h); h = xfrm_src_hash(daddr, saddr, family); hlist_add_head(&x->bysrc, xfrm_state_bysrc+h); if (x->id.spi) { h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family); hlist_add_head(&x->byspi, xfrm_state_byspi+h); } x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires; x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ; add_timer(&x->timer); xfrm_state_num++; xfrm_hash_grow_check(x->bydst.next != NULL); } else { x->km.state = XFRM_STATE_DEAD; xfrm_state_put(x); x = NULL; error = -ESRCH; } }out: if (x) xfrm_state_hold(x); else *err = acquire_in_progress ? -EAGAIN : error; spin_unlock_bh(&xfrm_state_lock); return x;}struct xfrm_state *xfrm_stateonly_find(xfrm_address_t *daddr, xfrm_address_t *saddr, unsigned short family, u8 mode, u8 proto, u32 reqid){ unsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family); struct xfrm_state *rx = NULL, *x = NULL; struct hlist_node *entry; spin_lock(&xfrm_state_lock); hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) { if (x->props.family == family && x->props.reqid == reqid && !(x->props.flags & XFRM_STATE_WILDRECV) && xfrm_state_addr_check(x, daddr, saddr, family) && mode == x->props.mode && proto == x->id.proto && x->km.state == XFRM_STATE_VALID) { rx = x; break; } } if (rx) xfrm_state_hold(rx); spin_unlock(&xfrm_state_lock); return rx;}EXPORT_SYMBOL(xfrm_stateonly_find);static void __xfrm_state_insert(struct xfrm_state *x){ unsigned int h; x->genid = ++xfrm_state_genid; h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr, x->props.reqid, x->props.family); hlist_add_head(&x->bydst, xfrm_state_bydst+h); h = xfrm_src_hash(&x->id.daddr, &x->props.saddr, x->props.family); hlist_add_head(&x->bysrc, xfrm_state_bysrc+h); if (x->id.spi) { h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family); hlist_add_head(&x->byspi, xfrm_state_byspi+h); } mod_timer(&x->timer, jiffies + HZ); if (x->replay_maxage) mod_timer(&x->rtimer, jiffies + x->replay_maxage); wake_up(&km_waitq); xfrm_state_num++; xfrm_hash_grow_check(x->bydst.next != NULL);}/* xfrm_state_lock is held */static void __xfrm_state_bump_genids(struct xfrm_state *xnew){ unsigned short family = xnew->props.family; u32 reqid = xnew->props.reqid; struct xfrm_state *x; struct hlist_node *entry; unsigned int h; h = xfrm_dst_hash(&xnew->id.daddr, &xnew->props.saddr, reqid, family); hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) { if (x->props.family == family && x->props.reqid == reqid && !xfrm_addr_cmp(&x->id.daddr, &xnew->id.daddr, family) && !xfrm_addr_cmp(&x->props.saddr, &xnew->props.saddr, family)) x->genid = xfrm_state_genid; }}void xfrm_state_insert(struct xfrm_state *x){ spin_lock_bh(&xfrm_state_lock); __xfrm_state_bump_genids(x); __xfrm_state_insert(x); spin_unlock_bh(&xfrm_state_lock);}EXPORT_SYMBOL(xfrm_state_insert);/* xfrm_state_lock is held */static struct xfrm_state *__find_acq_core(unsigned short family, u8 mode, u32 reqid, u8 proto, xfrm_address_t *daddr, xfrm_address_t *saddr, int create){ unsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family); struct hlist_node *entry; struct xfrm_state *x; hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) { if (x->props.reqid != reqid || x->props.mode != mode || x->props.family != family || x->km.state != XFRM_STATE_ACQ || x->id.spi != 0 || x->id.proto != proto) continue; switch (family) { case AF_INET: if (x->id.daddr.a4 != daddr->a4 || x->props.saddr.a4 != saddr->a4) continue; break; case AF_INET6: if (!ipv6_addr_equal((struct in6_addr *)x->id.daddr.a6, (struct in6_addr *)daddr) || !ipv6_addr_equal((struct in6_addr *) x->props.saddr.a6, (struct in6_addr *)saddr)) continue; break; } xfrm_state_hold(x); return x; } if (!create) return NULL; x = xfrm_state_alloc(); if (likely(x)) { switch (family) { case AF_INET: x->sel.daddr.a4 = daddr->a4; x->sel.saddr.a4 = saddr->a4; x->sel.prefixlen_d = 32; x->sel.prefixlen_s = 32; x->props.saddr.a4 = saddr->a4; x->id.daddr.a4 = daddr->a4; break; case AF_INET6: ipv6_addr_copy((struct in6_addr *)x->sel.daddr.a6, (struct in6_addr *)daddr); ipv6_addr_copy((struct in6_addr *)x->sel.saddr.a6, (struct in6_addr *)saddr); x->sel.prefixlen_d = 128; x->sel.prefixlen_s = 128; ipv6_addr_copy((struct in6_addr *)x->props.saddr.a6, (struct in6_addr *)saddr); ipv6_addr_copy((struct in6_addr *)x->id.daddr.a6, (struct in6_addr *)daddr); break; } x->km.state = XFRM_STATE_ACQ; x->id.proto = proto; x->props.family = family; x->props.mode = mode; x->props.reqid = reqid; x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires; xfrm_state_hold(x); x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ; add_timer(&x->timer); hlist_add_head(&x->bydst, xfrm_state_bydst+h); h = xfrm_src_hash(daddr, saddr, family); hlist_add_head(&x->bysrc, xfrm_state_bysrc+h); xfrm_state_num++;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -