2007-02-09 22:25:29 +08:00
|
|
|
/*
|
2005-04-17 06:20:36 +08:00
|
|
|
* xfrm_policy.c
|
|
|
|
*
|
|
|
|
* Changes:
|
|
|
|
* Mitsuru KANDA @USAGI
|
|
|
|
* Kazunori MIYAZAWA @USAGI
|
|
|
|
* Kunihiro Ishiguro <kunihiro@ipinfusion.com>
|
|
|
|
* IPv6 support
|
|
|
|
* Kazunori MIYAZAWA @USAGI
|
|
|
|
* YOSHIFUJI Hideaki
|
|
|
|
* Split up af-specific portion
|
|
|
|
* Derek Atkins <derek@ihtfp.com> Add the post_input processor
|
[LSM-IPSec]: Security association restriction.
This patch series implements per packet access control via the
extension of the Linux Security Modules (LSM) interface by hooks in
the XFRM and pfkey subsystems that leverage IPSec security
associations to label packets. Extensions to the SELinux LSM are
included that leverage the patch for this purpose.
This patch implements the changes necessary to the XFRM subsystem,
pfkey interface, ipv4/ipv6, and xfrm_user interface to restrict a
socket to use only authorized security associations (or no security
association) to send/receive network packets.
Patch purpose:
The patch is designed to enable access control per packets based on
the strongly authenticated IPSec security association. Such access
controls augment the existing ones based on network interface and IP
address. The former are very coarse-grained, and the latter can be
spoofed. By using IPSec, the system can control access to remote
hosts based on cryptographic keys generated using the IPSec mechanism.
This enables access control on a per-machine basis or per-application
if the remote machine is running the same mechanism and trusted to
enforce the access control policy.
Patch design approach:
The overall approach is that policy (xfrm_policy) entries set by
user-level programs (e.g., setkey for ipsec-tools) are extended with a
security context that is used at policy selection time in the XFRM
subsystem to restrict the sockets that can send/receive packets via
security associations (xfrm_states) that are built from those
policies.
A presentation available at
www.selinux-symposium.org/2005/presentations/session2/2-3-jaeger.pdf
from the SELinux symposium describes the overall approach.
Patch implementation details:
On output, the policy retrieved (via xfrm_policy_lookup or
xfrm_sk_policy_lookup) must be authorized for the security context of
the socket and the same security context is required for resultant
security association (retrieved or negotiated via racoon in
ipsec-tools). This is enforced in xfrm_state_find.
On input, the policy retrieved must also be authorized for the socket
(at __xfrm_policy_check), and the security context of the policy must
also match the security association being used.
The patch has virtually no impact on packets that do not use IPSec.
The existing Netfilter (outgoing) and LSM rcv_skb hooks are used as
before.
Also, if IPSec is used without security contexts, the impact is
minimal. The LSM must allow such policies to be selected for the
combination of socket and remote machine, but subsequent IPSec
processing proceeds as in the original case.
Testing:
The pfkey interface is tested using the ipsec-tools. ipsec-tools have
been modified (a separate ipsec-tools patch is available for version
0.5) that supports assignment of xfrm_policy entries and security
associations with security contexts via setkey and the negotiation
using the security contexts via racoon.
The xfrm_user interface is tested via ad hoc programs that set
security contexts. These programs are also available from me, and
contain programs for setting, getting, and deleting policy for testing
this interface. Testing of sa functions was done by tracing kernel
behavior.
Signed-off-by: Trent Jaeger <tjaeger@cse.psu.edu>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2005-12-14 15:12:27 +08:00
|
|
|
*
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
|
2007-11-14 13:37:28 +08:00
|
|
|
#include <linux/err.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/kmod.h>
|
|
|
|
#include <linux/list.h>
|
|
|
|
#include <linux/spinlock.h>
|
|
|
|
#include <linux/workqueue.h>
|
|
|
|
#include <linux/notifier.h>
|
|
|
|
#include <linux/netdevice.h>
|
2006-01-07 15:06:30 +08:00
|
|
|
#include <linux/netfilter.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/module.h>
|
2006-08-24 19:45:07 +08:00
|
|
|
#include <linux/cache.h>
|
2017-07-17 19:57:27 +08:00
|
|
|
#include <linux/cpu.h>
|
2007-12-21 12:49:33 +08:00
|
|
|
#include <linux/audit.h>
|
2007-12-12 01:32:34 +08:00
|
|
|
#include <net/dst.h>
|
2012-02-17 04:08:39 +08:00
|
|
|
#include <net/flow.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <net/xfrm.h>
|
|
|
|
#include <net/ip.h>
|
2007-12-21 12:42:57 +08:00
|
|
|
#ifdef CONFIG_XFRM_STATISTICS
|
|
|
|
#include <net/snmp.h>
|
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-08-24 19:50:50 +08:00
|
|
|
#include "xfrm_hash.h"
|
|
|
|
|
2013-02-05 19:52:55 +08:00
|
|
|
#define XFRM_QUEUE_TMO_MIN ((unsigned)(HZ/10))
|
|
|
|
#define XFRM_QUEUE_TMO_MAX ((unsigned)(60*HZ))
|
|
|
|
#define XFRM_MAX_QUEUE_LEN 100
|
|
|
|
|
2014-09-16 16:08:49 +08:00
|
|
|
struct xfrm_flo {
|
|
|
|
struct dst_entry *dst_orig;
|
|
|
|
u8 flags;
|
|
|
|
};
|
|
|
|
|
2017-07-17 19:57:27 +08:00
|
|
|
static DEFINE_PER_CPU(struct xfrm_dst *, xfrm_last_dst);
|
|
|
|
static struct work_struct *xfrm_pcpu_work __read_mostly;
|
2012-08-13 05:22:29 +08:00
|
|
|
static DEFINE_SPINLOCK(xfrm_policy_afinfo_lock);
|
2017-02-07 22:00:19 +08:00
|
|
|
static struct xfrm_policy_afinfo const __rcu *xfrm_policy_afinfo[AF_INET6 + 1]
|
2012-08-13 05:22:29 +08:00
|
|
|
__read_mostly;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2018-02-25 02:21:38 +08:00
|
|
|
static struct kmem_cache *xfrm_dst_cache __ro_after_init;
|
2016-08-11 21:17:54 +08:00
|
|
|
static __read_mostly seqcount_t xfrm_policy_hash_generation;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-11-29 04:41:01 +08:00
|
|
|
static void xfrm_init_pmtu(struct xfrm_dst **bundle, int nr);
|
2010-04-07 08:30:05 +08:00
|
|
|
static int stale_bundle(struct dst_entry *dst);
|
2011-06-30 07:18:20 +08:00
|
|
|
static int xfrm_bundle_ok(struct xfrm_dst *xdst);
|
2017-10-17 08:28:56 +08:00
|
|
|
static void xfrm_policy_queue_process(struct timer_list *t);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2014-11-13 17:09:50 +08:00
|
|
|
static void __xfrm_policy_link(struct xfrm_policy *pol, int dir);
|
2008-12-03 16:33:09 +08:00
|
|
|
static struct xfrm_policy *__xfrm_policy_unlink(struct xfrm_policy *pol,
|
|
|
|
int dir);
|
|
|
|
|
2016-08-11 21:17:55 +08:00
|
|
|
static inline bool xfrm_pol_hold_rcu(struct xfrm_policy *policy)
|
|
|
|
{
|
2017-07-04 20:53:22 +08:00
|
|
|
return refcount_inc_not_zero(&policy->refcnt);
|
2016-08-11 21:17:55 +08:00
|
|
|
}
|
|
|
|
|
2012-05-16 03:04:57 +08:00
|
|
|
static inline bool
|
2011-02-24 13:12:25 +08:00
|
|
|
__xfrm4_selector_match(const struct xfrm_selector *sel, const struct flowi *fl)
|
2006-11-09 14:46:26 +08:00
|
|
|
{
|
2011-03-12 15:42:11 +08:00
|
|
|
const struct flowi4 *fl4 = &fl->u.ip4;
|
|
|
|
|
2011-11-22 14:46:02 +08:00
|
|
|
return addr4_match(fl4->daddr, sel->daddr.a4, sel->prefixlen_d) &&
|
|
|
|
addr4_match(fl4->saddr, sel->saddr.a4, sel->prefixlen_s) &&
|
2011-03-12 15:42:11 +08:00
|
|
|
!((xfrm_flowi_dport(fl, &fl4->uli) ^ sel->dport) & sel->dport_mask) &&
|
|
|
|
!((xfrm_flowi_sport(fl, &fl4->uli) ^ sel->sport) & sel->sport_mask) &&
|
|
|
|
(fl4->flowi4_proto == sel->proto || !sel->proto) &&
|
|
|
|
(fl4->flowi4_oif == sel->ifindex || !sel->ifindex);
|
2006-11-09 14:46:26 +08:00
|
|
|
}
|
|
|
|
|
2012-05-16 03:04:57 +08:00
|
|
|
static inline bool
|
2011-02-24 13:12:25 +08:00
|
|
|
__xfrm6_selector_match(const struct xfrm_selector *sel, const struct flowi *fl)
|
2006-11-09 14:46:26 +08:00
|
|
|
{
|
2011-03-12 15:42:11 +08:00
|
|
|
const struct flowi6 *fl6 = &fl->u.ip6;
|
|
|
|
|
|
|
|
return addr_match(&fl6->daddr, &sel->daddr, sel->prefixlen_d) &&
|
|
|
|
addr_match(&fl6->saddr, &sel->saddr, sel->prefixlen_s) &&
|
|
|
|
!((xfrm_flowi_dport(fl, &fl6->uli) ^ sel->dport) & sel->dport_mask) &&
|
|
|
|
!((xfrm_flowi_sport(fl, &fl6->uli) ^ sel->sport) & sel->sport_mask) &&
|
|
|
|
(fl6->flowi6_proto == sel->proto || !sel->proto) &&
|
|
|
|
(fl6->flowi6_oif == sel->ifindex || !sel->ifindex);
|
2006-11-09 14:46:26 +08:00
|
|
|
}
|
|
|
|
|
2012-05-16 03:04:57 +08:00
|
|
|
bool xfrm_selector_match(const struct xfrm_selector *sel, const struct flowi *fl,
|
|
|
|
unsigned short family)
|
2006-11-09 14:46:26 +08:00
|
|
|
{
|
|
|
|
switch (family) {
|
|
|
|
case AF_INET:
|
|
|
|
return __xfrm4_selector_match(sel, fl);
|
|
|
|
case AF_INET6:
|
|
|
|
return __xfrm6_selector_match(sel, fl);
|
|
|
|
}
|
2012-05-16 03:04:57 +08:00
|
|
|
return false;
|
2006-11-09 14:46:26 +08:00
|
|
|
}
|
|
|
|
|
2017-02-07 22:00:17 +08:00
|
|
|
static const struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family)
|
2012-08-19 18:31:48 +08:00
|
|
|
{
|
2017-02-07 22:00:17 +08:00
|
|
|
const struct xfrm_policy_afinfo *afinfo;
|
2012-08-19 18:31:48 +08:00
|
|
|
|
2017-02-07 22:00:17 +08:00
|
|
|
if (unlikely(family >= ARRAY_SIZE(xfrm_policy_afinfo)))
|
2012-08-19 18:31:48 +08:00
|
|
|
return NULL;
|
|
|
|
rcu_read_lock();
|
|
|
|
afinfo = rcu_dereference(xfrm_policy_afinfo[family]);
|
|
|
|
if (unlikely(!afinfo))
|
|
|
|
rcu_read_unlock();
|
|
|
|
return afinfo;
|
|
|
|
}
|
|
|
|
|
2017-04-14 16:06:10 +08:00
|
|
|
struct dst_entry *__xfrm_dst_lookup(struct net *net, int tos, int oif,
|
|
|
|
const xfrm_address_t *saddr,
|
|
|
|
const xfrm_address_t *daddr,
|
net: xfrm: support setting an output mark.
On systems that use mark-based routing it may be necessary for
routing lookups to use marks in order for packets to be routed
correctly. An example of such a system is Android, which uses
socket marks to route packets via different networks.
Currently, routing lookups in tunnel mode always use a mark of
zero, making routing incorrect on such systems.
This patch adds a new output_mark element to the xfrm state and
a corresponding XFRMA_OUTPUT_MARK netlink attribute. The output
mark differs from the existing xfrm mark in two ways:
1. The xfrm mark is used to match xfrm policies and states, while
the xfrm output mark is used to set the mark (and influence
the routing) of the packets emitted by those states.
2. The existing mark is constrained to be a subset of the bits of
the originating socket or transformed packet, but the output
mark is arbitrary and depends only on the state.
The use of a separate mark provides additional flexibility. For
example:
- A packet subject to two transforms (e.g., transport mode inside
tunnel mode) can have two different output marks applied to it,
one for the transport mode SA and one for the tunnel mode SA.
- On a system where socket marks determine routing, the packets
emitted by an IPsec tunnel can be routed based on a mark that
is determined by the tunnel, not by the marks of the
unencrypted packets.
- Support for setting the output marks can be introduced without
breaking any existing setups that employ both mark-based
routing and xfrm tunnel mode. Simply changing the code to use
the xfrm mark for routing output packets could xfrm mark could
change behaviour in a way that breaks these setups.
If the output mark is unspecified or set to zero, the mark is not
set or changed.
Tested: make allyesconfig; make -j64
Tested: https://android-review.googlesource.com/452776
Signed-off-by: Lorenzo Colitti <lorenzo@google.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
2017-08-11 01:11:33 +08:00
|
|
|
int family, u32 mark)
|
2008-02-22 13:48:22 +08:00
|
|
|
{
|
2017-02-07 22:00:19 +08:00
|
|
|
const struct xfrm_policy_afinfo *afinfo;
|
2008-02-22 13:48:22 +08:00
|
|
|
struct dst_entry *dst;
|
|
|
|
|
|
|
|
afinfo = xfrm_policy_get_afinfo(family);
|
|
|
|
if (unlikely(afinfo == NULL))
|
|
|
|
return ERR_PTR(-EAFNOSUPPORT);
|
|
|
|
|
net: xfrm: support setting an output mark.
On systems that use mark-based routing it may be necessary for
routing lookups to use marks in order for packets to be routed
correctly. An example of such a system is Android, which uses
socket marks to route packets via different networks.
Currently, routing lookups in tunnel mode always use a mark of
zero, making routing incorrect on such systems.
This patch adds a new output_mark element to the xfrm state and
a corresponding XFRMA_OUTPUT_MARK netlink attribute. The output
mark differs from the existing xfrm mark in two ways:
1. The xfrm mark is used to match xfrm policies and states, while
the xfrm output mark is used to set the mark (and influence
the routing) of the packets emitted by those states.
2. The existing mark is constrained to be a subset of the bits of
the originating socket or transformed packet, but the output
mark is arbitrary and depends only on the state.
The use of a separate mark provides additional flexibility. For
example:
- A packet subject to two transforms (e.g., transport mode inside
tunnel mode) can have two different output marks applied to it,
one for the transport mode SA and one for the tunnel mode SA.
- On a system where socket marks determine routing, the packets
emitted by an IPsec tunnel can be routed based on a mark that
is determined by the tunnel, not by the marks of the
unencrypted packets.
- Support for setting the output marks can be introduced without
breaking any existing setups that employ both mark-based
routing and xfrm tunnel mode. Simply changing the code to use
the xfrm mark for routing output packets could xfrm mark could
change behaviour in a way that breaks these setups.
If the output mark is unspecified or set to zero, the mark is not
set or changed.
Tested: make allyesconfig; make -j64
Tested: https://android-review.googlesource.com/452776
Signed-off-by: Lorenzo Colitti <lorenzo@google.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
2017-08-11 01:11:33 +08:00
|
|
|
dst = afinfo->dst_lookup(net, tos, oif, saddr, daddr, mark);
|
2008-02-22 13:48:22 +08:00
|
|
|
|
2017-02-07 22:00:18 +08:00
|
|
|
rcu_read_unlock();
|
2008-02-22 13:48:22 +08:00
|
|
|
|
|
|
|
return dst;
|
|
|
|
}
|
2017-04-14 16:06:10 +08:00
|
|
|
EXPORT_SYMBOL(__xfrm_dst_lookup);
|
2008-02-22 13:48:22 +08:00
|
|
|
|
2015-08-11 06:58:11 +08:00
|
|
|
static inline struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x,
|
|
|
|
int tos, int oif,
|
2008-02-22 13:48:22 +08:00
|
|
|
xfrm_address_t *prev_saddr,
|
|
|
|
xfrm_address_t *prev_daddr,
|
net: xfrm: support setting an output mark.
On systems that use mark-based routing it may be necessary for
routing lookups to use marks in order for packets to be routed
correctly. An example of such a system is Android, which uses
socket marks to route packets via different networks.
Currently, routing lookups in tunnel mode always use a mark of
zero, making routing incorrect on such systems.
This patch adds a new output_mark element to the xfrm state and
a corresponding XFRMA_OUTPUT_MARK netlink attribute. The output
mark differs from the existing xfrm mark in two ways:
1. The xfrm mark is used to match xfrm policies and states, while
the xfrm output mark is used to set the mark (and influence
the routing) of the packets emitted by those states.
2. The existing mark is constrained to be a subset of the bits of
the originating socket or transformed packet, but the output
mark is arbitrary and depends only on the state.
The use of a separate mark provides additional flexibility. For
example:
- A packet subject to two transforms (e.g., transport mode inside
tunnel mode) can have two different output marks applied to it,
one for the transport mode SA and one for the tunnel mode SA.
- On a system where socket marks determine routing, the packets
emitted by an IPsec tunnel can be routed based on a mark that
is determined by the tunnel, not by the marks of the
unencrypted packets.
- Support for setting the output marks can be introduced without
breaking any existing setups that employ both mark-based
routing and xfrm tunnel mode. Simply changing the code to use
the xfrm mark for routing output packets could xfrm mark could
change behaviour in a way that breaks these setups.
If the output mark is unspecified or set to zero, the mark is not
set or changed.
Tested: make allyesconfig; make -j64
Tested: https://android-review.googlesource.com/452776
Signed-off-by: Lorenzo Colitti <lorenzo@google.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
2017-08-11 01:11:33 +08:00
|
|
|
int family, u32 mark)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-11-26 09:51:25 +08:00
|
|
|
struct net *net = xs_net(x);
|
2007-11-14 13:37:28 +08:00
|
|
|
xfrm_address_t *saddr = &x->props.saddr;
|
|
|
|
xfrm_address_t *daddr = &x->id.daddr;
|
|
|
|
struct dst_entry *dst;
|
|
|
|
|
2008-02-22 13:48:22 +08:00
|
|
|
if (x->type->flags & XFRM_TYPE_LOCAL_COADDR) {
|
2007-11-14 13:37:28 +08:00
|
|
|
saddr = x->coaddr;
|
2008-02-22 13:48:22 +08:00
|
|
|
daddr = prev_daddr;
|
|
|
|
}
|
|
|
|
if (x->type->flags & XFRM_TYPE_REMOTE_COADDR) {
|
|
|
|
saddr = prev_saddr;
|
2007-11-14 13:37:28 +08:00
|
|
|
daddr = x->coaddr;
|
2008-02-22 13:48:22 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
net: xfrm: support setting an output mark.
On systems that use mark-based routing it may be necessary for
routing lookups to use marks in order for packets to be routed
correctly. An example of such a system is Android, which uses
socket marks to route packets via different networks.
Currently, routing lookups in tunnel mode always use a mark of
zero, making routing incorrect on such systems.
This patch adds a new output_mark element to the xfrm state and
a corresponding XFRMA_OUTPUT_MARK netlink attribute. The output
mark differs from the existing xfrm mark in two ways:
1. The xfrm mark is used to match xfrm policies and states, while
the xfrm output mark is used to set the mark (and influence
the routing) of the packets emitted by those states.
2. The existing mark is constrained to be a subset of the bits of
the originating socket or transformed packet, but the output
mark is arbitrary and depends only on the state.
The use of a separate mark provides additional flexibility. For
example:
- A packet subject to two transforms (e.g., transport mode inside
tunnel mode) can have two different output marks applied to it,
one for the transport mode SA and one for the tunnel mode SA.
- On a system where socket marks determine routing, the packets
emitted by an IPsec tunnel can be routed based on a mark that
is determined by the tunnel, not by the marks of the
unencrypted packets.
- Support for setting the output marks can be introduced without
breaking any existing setups that employ both mark-based
routing and xfrm tunnel mode. Simply changing the code to use
the xfrm mark for routing output packets could xfrm mark could
change behaviour in a way that breaks these setups.
If the output mark is unspecified or set to zero, the mark is not
set or changed.
Tested: make allyesconfig; make -j64
Tested: https://android-review.googlesource.com/452776
Signed-off-by: Lorenzo Colitti <lorenzo@google.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
2017-08-11 01:11:33 +08:00
|
|
|
dst = __xfrm_dst_lookup(net, tos, oif, saddr, daddr, family, mark);
|
2008-02-22 13:48:22 +08:00
|
|
|
|
|
|
|
if (!IS_ERR(dst)) {
|
|
|
|
if (prev_saddr != saddr)
|
|
|
|
memcpy(prev_saddr, saddr, sizeof(*prev_saddr));
|
|
|
|
if (prev_daddr != daddr)
|
|
|
|
memcpy(prev_daddr, daddr, sizeof(*prev_daddr));
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-11-14 13:37:28 +08:00
|
|
|
return dst;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline unsigned long make_jiffies(long secs)
|
|
|
|
{
|
|
|
|
if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ)
|
|
|
|
return MAX_SCHEDULE_TIMEOUT-1;
|
|
|
|
else
|
2007-02-09 22:25:29 +08:00
|
|
|
return secs*HZ;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2017-10-17 08:28:56 +08:00
|
|
|
static void xfrm_policy_timer(struct timer_list *t)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2017-10-17 08:28:56 +08:00
|
|
|
struct xfrm_policy *xp = from_timer(xp, t, timer);
|
2007-03-05 08:12:44 +08:00
|
|
|
unsigned long now = get_seconds();
|
2005-04-17 06:20:36 +08:00
|
|
|
long next = LONG_MAX;
|
|
|
|
int warn = 0;
|
|
|
|
int dir;
|
|
|
|
|
|
|
|
read_lock(&xp->lock);
|
|
|
|
|
2010-03-31 08:17:05 +08:00
|
|
|
if (unlikely(xp->walk.dead))
|
2005-04-17 06:20:36 +08:00
|
|
|
goto out;
|
|
|
|
|
2005-10-06 03:15:12 +08:00
|
|
|
dir = xfrm_policy_id2dir(xp->index);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
if (tmo <= 0) {
|
|
|
|
warn = 1;
|
|
|
|
tmo = XFRM_KM_TIMEOUT;
|
|
|
|
}
|
|
|
|
if (tmo < next)
|
|
|
|
next = tmo;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (warn)
|
2006-03-21 11:17:25 +08:00
|
|
|
km_policy_expired(xp, dir, 0, 0);
|
2005-04-17 06:20:36 +08:00
|
|
|
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;
|
|
|
|
|
|
|
|
expired:
|
|
|
|
read_unlock(&xp->lock);
|
2005-06-19 13:43:22 +08:00
|
|
|
if (!xfrm_policy_delete(xp, dir))
|
2006-03-21 11:17:25 +08:00
|
|
|
km_policy_expired(xp, dir, 1, 0);
|
2005-04-17 06:20:36 +08:00
|
|
|
xfrm_pol_put(xp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Allocate xfrm_policy. Not used here, it is supposed to be used by pfkeyv2
|
|
|
|
* SPD calls.
|
|
|
|
*/
|
|
|
|
|
2008-11-26 09:21:45 +08:00
|
|
|
struct xfrm_policy *xfrm_policy_alloc(struct net *net, gfp_t gfp)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct xfrm_policy *policy;
|
|
|
|
|
2006-07-22 05:51:30 +08:00
|
|
|
policy = kzalloc(sizeof(struct xfrm_policy), gfp);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (policy) {
|
2008-11-26 09:21:45 +08:00
|
|
|
write_pnet(&policy->xp_net, net);
|
2008-10-01 22:03:24 +08:00
|
|
|
INIT_LIST_HEAD(&policy->walk.all);
|
2006-08-24 19:45:07 +08:00
|
|
|
INIT_HLIST_NODE(&policy->bydst);
|
|
|
|
INIT_HLIST_NODE(&policy->byidx);
|
2005-04-17 06:20:36 +08:00
|
|
|
rwlock_init(&policy->lock);
|
2017-07-04 20:53:22 +08:00
|
|
|
refcount_set(&policy->refcnt, 1);
|
2013-02-05 19:52:55 +08:00
|
|
|
skb_queue_head_init(&policy->polq.hold_queue);
|
2017-10-17 08:28:56 +08:00
|
|
|
timer_setup(&policy->timer, xfrm_policy_timer, 0);
|
|
|
|
timer_setup(&policy->polq.hold_timer,
|
|
|
|
xfrm_policy_queue_process, 0);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
return policy;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(xfrm_policy_alloc);
|
|
|
|
|
2015-12-08 23:22:01 +08:00
|
|
|
static void xfrm_policy_destroy_rcu(struct rcu_head *head)
|
|
|
|
{
|
|
|
|
struct xfrm_policy *policy = container_of(head, struct xfrm_policy, rcu);
|
|
|
|
|
|
|
|
security_xfrm_policy_free(policy->security);
|
|
|
|
kfree(policy);
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Destroy xfrm_policy: descendant resources must be released to this moment. */
|
|
|
|
|
2008-01-08 14:34:29 +08:00
|
|
|
void xfrm_policy_destroy(struct xfrm_policy *policy)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-10-01 22:03:24 +08:00
|
|
|
BUG_ON(!policy->walk.dead);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-08-01 18:08:36 +08:00
|
|
|
if (del_timer(&policy->timer) || del_timer(&policy->polq.hold_timer))
|
2005-04-17 06:20:36 +08:00
|
|
|
BUG();
|
|
|
|
|
2015-12-08 23:22:01 +08:00
|
|
|
call_rcu(&policy->rcu, xfrm_policy_destroy_rcu);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2008-01-08 14:34:29 +08:00
|
|
|
EXPORT_SYMBOL(xfrm_policy_destroy);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-01-04 00:13:20 +08:00
|
|
|
/* Rule must be locked. Release descendant resources, announce
|
2005-04-17 06:20:36 +08:00
|
|
|
* entry dead. The rule must be unlinked from lists to the moment.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void xfrm_policy_kill(struct xfrm_policy *policy)
|
|
|
|
{
|
2008-10-01 22:03:24 +08:00
|
|
|
policy->walk.dead = 1;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-04-07 08:30:06 +08:00
|
|
|
atomic_inc(&policy->genid);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-10-08 16:49:45 +08:00
|
|
|
if (del_timer(&policy->polq.hold_timer))
|
|
|
|
xfrm_pol_put(policy);
|
2015-04-22 15:51:16 +08:00
|
|
|
skb_queue_purge(&policy->polq.hold_queue);
|
2013-02-05 19:52:55 +08:00
|
|
|
|
2010-04-07 08:30:06 +08:00
|
|
|
if (del_timer(&policy->timer))
|
|
|
|
xfrm_pol_put(policy);
|
|
|
|
|
|
|
|
xfrm_pol_put(policy);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2006-08-24 19:45:07 +08:00
|
|
|
static unsigned int xfrm_policy_hashmax __read_mostly = 1 * 1024 * 1024;
|
|
|
|
|
2008-11-26 09:32:41 +08:00
|
|
|
static inline unsigned int idx_hash(struct net *net, u32 index)
|
2006-08-24 19:45:07 +08:00
|
|
|
{
|
2008-11-26 09:32:41 +08:00
|
|
|
return __idx_hash(index, net->xfrm.policy_idx_hmask);
|
2006-08-24 19:45:07 +08:00
|
|
|
}
|
|
|
|
|
xfrm: hash prefixed policies based on preflen thresholds
The idea is an extension of the current policy hashing.
Today only non-prefixed policies are stored in a hash table. This
patch relaxes the constraints, and hashes policies whose prefix
lengths are greater or equal to a configurable threshold.
Each hash table (one per direction) maintains its own set of IPv4 and
IPv6 thresholds (dbits4, sbits4, dbits6, sbits6), by default (32, 32,
128, 128).
Example, if the output hash table is configured with values (16, 24,
56, 64):
ip xfrm policy add dir out src 10.22.0.0/20 dst 10.24.1.0/24 ... => hashed
ip xfrm policy add dir out src 10.22.0.0/16 dst 10.24.1.1/32 ... => hashed
ip xfrm policy add dir out src 10.22.0.0/16 dst 10.24.0.0/16 ... => unhashed
ip xfrm policy add dir out \
src 3ffe:304:124:2200::/60 dst 3ffe:304:124:2401::/64 ... => hashed
ip xfrm policy add dir out \
src 3ffe:304:124:2200::/56 dst 3ffe:304:124:2401::2/128 ... => hashed
ip xfrm policy add dir out \
src 3ffe:304:124:2200::/56 dst 3ffe:304:124:2400::/56 ... => unhashed
The high order bits of the addresses (up to the threshold) are used to
compute the hash key.
Signed-off-by: Christophe Gouault <christophe.gouault@6wind.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
2014-08-29 22:16:04 +08:00
|
|
|
/* calculate policy hash thresholds */
|
|
|
|
static void __get_hash_thresh(struct net *net,
|
|
|
|
unsigned short family, int dir,
|
|
|
|
u8 *dbits, u8 *sbits)
|
|
|
|
{
|
|
|
|
switch (family) {
|
|
|
|
case AF_INET:
|
|
|
|
*dbits = net->xfrm.policy_bydst[dir].dbits4;
|
|
|
|
*sbits = net->xfrm.policy_bydst[dir].sbits4;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case AF_INET6:
|
|
|
|
*dbits = net->xfrm.policy_bydst[dir].dbits6;
|
|
|
|
*sbits = net->xfrm.policy_bydst[dir].sbits6;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
*dbits = 0;
|
|
|
|
*sbits = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-24 13:33:19 +08:00
|
|
|
static struct hlist_head *policy_hash_bysel(struct net *net,
|
|
|
|
const struct xfrm_selector *sel,
|
|
|
|
unsigned short family, int dir)
|
2006-08-24 19:45:07 +08:00
|
|
|
{
|
2008-11-26 09:33:06 +08:00
|
|
|
unsigned int hmask = net->xfrm.policy_bydst[dir].hmask;
|
xfrm: hash prefixed policies based on preflen thresholds
The idea is an extension of the current policy hashing.
Today only non-prefixed policies are stored in a hash table. This
patch relaxes the constraints, and hashes policies whose prefix
lengths are greater or equal to a configurable threshold.
Each hash table (one per direction) maintains its own set of IPv4 and
IPv6 thresholds (dbits4, sbits4, dbits6, sbits6), by default (32, 32,
128, 128).
Example, if the output hash table is configured with values (16, 24,
56, 64):
ip xfrm policy add dir out src 10.22.0.0/20 dst 10.24.1.0/24 ... => hashed
ip xfrm policy add dir out src 10.22.0.0/16 dst 10.24.1.1/32 ... => hashed
ip xfrm policy add dir out src 10.22.0.0/16 dst 10.24.0.0/16 ... => unhashed
ip xfrm policy add dir out \
src 3ffe:304:124:2200::/60 dst 3ffe:304:124:2401::/64 ... => hashed
ip xfrm policy add dir out \
src 3ffe:304:124:2200::/56 dst 3ffe:304:124:2401::2/128 ... => hashed
ip xfrm policy add dir out \
src 3ffe:304:124:2200::/56 dst 3ffe:304:124:2400::/56 ... => unhashed
The high order bits of the addresses (up to the threshold) are used to
compute the hash key.
Signed-off-by: Christophe Gouault <christophe.gouault@6wind.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
2014-08-29 22:16:04 +08:00
|
|
|
unsigned int hash;
|
|
|
|
u8 dbits;
|
|
|
|
u8 sbits;
|
|
|
|
|
|
|
|
__get_hash_thresh(net, family, dir, &dbits, &sbits);
|
|
|
|
hash = __sel_hash(sel, family, hmask, dbits, sbits);
|
2006-08-24 19:45:07 +08:00
|
|
|
|
2016-08-11 21:17:53 +08:00
|
|
|
if (hash == hmask + 1)
|
|
|
|
return &net->xfrm.policy_inexact[dir];
|
|
|
|
|
|
|
|
return rcu_dereference_check(net->xfrm.policy_bydst[dir].table,
|
|
|
|
lockdep_is_held(&net->xfrm.xfrm_policy_lock)) + hash;
|
2006-08-24 19:45:07 +08:00
|
|
|
}
|
|
|
|
|
2011-02-24 13:33:19 +08:00
|
|
|
static struct hlist_head *policy_hash_direct(struct net *net,
|
|
|
|
const xfrm_address_t *daddr,
|
|
|
|
const xfrm_address_t *saddr,
|
|
|
|
unsigned short family, int dir)
|
2006-08-24 19:45:07 +08:00
|
|
|
{
|
2008-11-26 09:33:06 +08:00
|
|
|
unsigned int hmask = net->xfrm.policy_bydst[dir].hmask;
|
xfrm: hash prefixed policies based on preflen thresholds
The idea is an extension of the current policy hashing.
Today only non-prefixed policies are stored in a hash table. This
patch relaxes the constraints, and hashes policies whose prefix
lengths are greater or equal to a configurable threshold.
Each hash table (one per direction) maintains its own set of IPv4 and
IPv6 thresholds (dbits4, sbits4, dbits6, sbits6), by default (32, 32,
128, 128).
Example, if the output hash table is configured with values (16, 24,
56, 64):
ip xfrm policy add dir out src 10.22.0.0/20 dst 10.24.1.0/24 ... => hashed
ip xfrm policy add dir out src 10.22.0.0/16 dst 10.24.1.1/32 ... => hashed
ip xfrm policy add dir out src 10.22.0.0/16 dst 10.24.0.0/16 ... => unhashed
ip xfrm policy add dir out \
src 3ffe:304:124:2200::/60 dst 3ffe:304:124:2401::/64 ... => hashed
ip xfrm policy add dir out \
src 3ffe:304:124:2200::/56 dst 3ffe:304:124:2401::2/128 ... => hashed
ip xfrm policy add dir out \
src 3ffe:304:124:2200::/56 dst 3ffe:304:124:2400::/56 ... => unhashed
The high order bits of the addresses (up to the threshold) are used to
compute the hash key.
Signed-off-by: Christophe Gouault <christophe.gouault@6wind.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
2014-08-29 22:16:04 +08:00
|
|
|
unsigned int hash;
|
|
|
|
u8 dbits;
|
|
|
|
u8 sbits;
|
|
|
|
|
|
|
|
__get_hash_thresh(net, family, dir, &dbits, &sbits);
|
|
|
|
hash = __addr_hash(daddr, saddr, family, hmask, dbits, sbits);
|
2006-08-24 19:45:07 +08:00
|
|
|
|
2016-08-11 21:17:53 +08:00
|
|
|
return rcu_dereference_check(net->xfrm.policy_bydst[dir].table,
|
|
|
|
lockdep_is_held(&net->xfrm.xfrm_policy_lock)) + hash;
|
2006-08-24 19:45:07 +08:00
|
|
|
}
|
|
|
|
|
xfrm: hash prefixed policies based on preflen thresholds
The idea is an extension of the current policy hashing.
Today only non-prefixed policies are stored in a hash table. This
patch relaxes the constraints, and hashes policies whose prefix
lengths are greater or equal to a configurable threshold.
Each hash table (one per direction) maintains its own set of IPv4 and
IPv6 thresholds (dbits4, sbits4, dbits6, sbits6), by default (32, 32,
128, 128).
Example, if the output hash table is configured with values (16, 24,
56, 64):
ip xfrm policy add dir out src 10.22.0.0/20 dst 10.24.1.0/24 ... => hashed
ip xfrm policy add dir out src 10.22.0.0/16 dst 10.24.1.1/32 ... => hashed
ip xfrm policy add dir out src 10.22.0.0/16 dst 10.24.0.0/16 ... => unhashed
ip xfrm policy add dir out \
src 3ffe:304:124:2200::/60 dst 3ffe:304:124:2401::/64 ... => hashed
ip xfrm policy add dir out \
src 3ffe:304:124:2200::/56 dst 3ffe:304:124:2401::2/128 ... => hashed
ip xfrm policy add dir out \
src 3ffe:304:124:2200::/56 dst 3ffe:304:124:2400::/56 ... => unhashed
The high order bits of the addresses (up to the threshold) are used to
compute the hash key.
Signed-off-by: Christophe Gouault <christophe.gouault@6wind.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
2014-08-29 22:16:04 +08:00
|
|
|
static void xfrm_dst_hash_transfer(struct net *net,
|
|
|
|
struct hlist_head *list,
|
2006-08-24 19:45:07 +08:00
|
|
|
struct hlist_head *ndsttable,
|
xfrm: hash prefixed policies based on preflen thresholds
The idea is an extension of the current policy hashing.
Today only non-prefixed policies are stored in a hash table. This
patch relaxes the constraints, and hashes policies whose prefix
lengths are greater or equal to a configurable threshold.
Each hash table (one per direction) maintains its own set of IPv4 and
IPv6 thresholds (dbits4, sbits4, dbits6, sbits6), by default (32, 32,
128, 128).
Example, if the output hash table is configured with values (16, 24,
56, 64):
ip xfrm policy add dir out src 10.22.0.0/20 dst 10.24.1.0/24 ... => hashed
ip xfrm policy add dir out src 10.22.0.0/16 dst 10.24.1.1/32 ... => hashed
ip xfrm policy add dir out src 10.22.0.0/16 dst 10.24.0.0/16 ... => unhashed
ip xfrm policy add dir out \
src 3ffe:304:124:2200::/60 dst 3ffe:304:124:2401::/64 ... => hashed
ip xfrm policy add dir out \
src 3ffe:304:124:2200::/56 dst 3ffe:304:124:2401::2/128 ... => hashed
ip xfrm policy add dir out \
src 3ffe:304:124:2200::/56 dst 3ffe:304:124:2400::/56 ... => unhashed
The high order bits of the addresses (up to the threshold) are used to
compute the hash key.
Signed-off-by: Christophe Gouault <christophe.gouault@6wind.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
2014-08-29 22:16:04 +08:00
|
|
|
unsigned int nhashmask,
|
|
|
|
int dir)
|
2006-08-24 19:45:07 +08:00
|
|
|
{
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 09:06:00 +08:00
|
|
|
struct hlist_node *tmp, *entry0 = NULL;
|
2006-08-24 19:45:07 +08:00
|
|
|
struct xfrm_policy *pol;
|
2008-02-18 15:29:30 +08:00
|
|
|
unsigned int h0 = 0;
|
xfrm: hash prefixed policies based on preflen thresholds
The idea is an extension of the current policy hashing.
Today only non-prefixed policies are stored in a hash table. This
patch relaxes the constraints, and hashes policies whose prefix
lengths are greater or equal to a configurable threshold.
Each hash table (one per direction) maintains its own set of IPv4 and
IPv6 thresholds (dbits4, sbits4, dbits6, sbits6), by default (32, 32,
128, 128).
Example, if the output hash table is configured with values (16, 24,
56, 64):
ip xfrm policy add dir out src 10.22.0.0/20 dst 10.24.1.0/24 ... => hashed
ip xfrm policy add dir out src 10.22.0.0/16 dst 10.24.1.1/32 ... => hashed
ip xfrm policy add dir out src 10.22.0.0/16 dst 10.24.0.0/16 ... => unhashed
ip xfrm policy add dir out \
src 3ffe:304:124:2200::/60 dst 3ffe:304:124:2401::/64 ... => hashed
ip xfrm policy add dir out \
src 3ffe:304:124:2200::/56 dst 3ffe:304:124:2401::2/128 ... => hashed
ip xfrm policy add dir out \
src 3ffe:304:124:2200::/56 dst 3ffe:304:124:2400::/56 ... => unhashed
The high order bits of the addresses (up to the threshold) are used to
compute the hash key.
Signed-off-by: Christophe Gouault <christophe.gouault@6wind.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
2014-08-29 22:16:04 +08:00
|
|
|
u8 dbits;
|
|
|
|
u8 sbits;
|
2006-08-24 19:45:07 +08:00
|
|
|
|
2008-02-18 15:29:30 +08:00
|
|
|
redo:
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 09:06:00 +08:00
|
|
|
hlist_for_each_entry_safe(pol, tmp, list, bydst) {
|
2006-08-24 19:45:07 +08:00
|
|
|
unsigned int h;
|
|
|
|
|
xfrm: hash prefixed policies based on preflen thresholds
The idea is an extension of the current policy hashing.
Today only non-prefixed policies are stored in a hash table. This
patch relaxes the constraints, and hashes policies whose prefix
lengths are greater or equal to a configurable threshold.
Each hash table (one per direction) maintains its own set of IPv4 and
IPv6 thresholds (dbits4, sbits4, dbits6, sbits6), by default (32, 32,
128, 128).
Example, if the output hash table is configured with values (16, 24,
56, 64):
ip xfrm policy add dir out src 10.22.0.0/20 dst 10.24.1.0/24 ... => hashed
ip xfrm policy add dir out src 10.22.0.0/16 dst 10.24.1.1/32 ... => hashed
ip xfrm policy add dir out src 10.22.0.0/16 dst 10.24.0.0/16 ... => unhashed
ip xfrm policy add dir out \
src 3ffe:304:124:2200::/60 dst 3ffe:304:124:2401::/64 ... => hashed
ip xfrm policy add dir out \
src 3ffe:304:124:2200::/56 dst 3ffe:304:124:2401::2/128 ... => hashed
ip xfrm policy add dir out \
src 3ffe:304:124:2200::/56 dst 3ffe:304:124:2400::/56 ... => unhashed
The high order bits of the addresses (up to the threshold) are used to
compute the hash key.
Signed-off-by: Christophe Gouault <christophe.gouault@6wind.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
2014-08-29 22:16:04 +08:00
|
|
|
__get_hash_thresh(net, pol->family, dir, &dbits, &sbits);
|
2006-08-24 19:45:07 +08:00
|
|
|
h = __addr_hash(&pol->selector.daddr, &pol->selector.saddr,
|
xfrm: hash prefixed policies based on preflen thresholds
The idea is an extension of the current policy hashing.
Today only non-prefixed policies are stored in a hash table. This
patch relaxes the constraints, and hashes policies whose prefix
lengths are greater or equal to a configurable threshold.
Each hash table (one per direction) maintains its own set of IPv4 and
IPv6 thresholds (dbits4, sbits4, dbits6, sbits6), by default (32, 32,
128, 128).
Example, if the output hash table is configured with values (16, 24,
56, 64):
ip xfrm policy add dir out src 10.22.0.0/20 dst 10.24.1.0/24 ... => hashed
ip xfrm policy add dir out src 10.22.0.0/16 dst 10.24.1.1/32 ... => hashed
ip xfrm policy add dir out src 10.22.0.0/16 dst 10.24.0.0/16 ... => unhashed
ip xfrm policy add dir out \
src 3ffe:304:124:2200::/60 dst 3ffe:304:124:2401::/64 ... => hashed
ip xfrm policy add dir out \
src 3ffe:304:124:2200::/56 dst 3ffe:304:124:2401::2/128 ... => hashed
ip xfrm policy add dir out \
src 3ffe:304:124:2200::/56 dst 3ffe:304:124:2400::/56 ... => unhashed
The high order bits of the addresses (up to the threshold) are used to
compute the hash key.
Signed-off-by: Christophe Gouault <christophe.gouault@6wind.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
2014-08-29 22:16:04 +08:00
|
|
|
pol->family, nhashmask, dbits, sbits);
|
2008-02-18 15:29:30 +08:00
|
|
|
if (!entry0) {
|
2016-08-11 21:17:52 +08:00
|
|
|
hlist_del_rcu(&pol->bydst);
|
|
|
|
hlist_add_head_rcu(&pol->bydst, ndsttable + h);
|
2008-02-18 15:29:30 +08:00
|
|
|
h0 = h;
|
|
|
|
} else {
|
|
|
|
if (h != h0)
|
|
|
|
continue;
|
2016-08-11 21:17:52 +08:00
|
|
|
hlist_del_rcu(&pol->bydst);
|
|
|
|
hlist_add_behind_rcu(&pol->bydst, entry0);
|
2008-02-18 15:29:30 +08:00
|
|
|
}
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 09:06:00 +08:00
|
|
|
entry0 = &pol->bydst;
|
2008-02-18 15:29:30 +08:00
|
|
|
}
|
|
|
|
if (!hlist_empty(list)) {
|
|
|
|
entry0 = NULL;
|
|
|
|
goto redo;
|
2006-08-24 19:45:07 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void xfrm_idx_hash_transfer(struct hlist_head *list,
|
|
|
|
struct hlist_head *nidxtable,
|
|
|
|
unsigned int nhashmask)
|
|
|
|
{
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 09:06:00 +08:00
|
|
|
struct hlist_node *tmp;
|
2006-08-24 19:45:07 +08:00
|
|
|
struct xfrm_policy *pol;
|
|
|
|
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 09:06:00 +08:00
|
|
|
hlist_for_each_entry_safe(pol, tmp, list, byidx) {
|
2006-08-24 19:45:07 +08:00
|
|
|
unsigned int h;
|
|
|
|
|
|
|
|
h = __idx_hash(pol->index, nhashmask);
|
|
|
|
hlist_add_head(&pol->byidx, nidxtable+h);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned long xfrm_new_hash_mask(unsigned int old_hmask)
|
|
|
|
{
|
|
|
|
return ((old_hmask + 1) << 1) - 1;
|
|
|
|
}
|
|
|
|
|
2008-11-26 09:28:57 +08:00
|
|
|
static void xfrm_bydst_resize(struct net *net, int dir)
|
2006-08-24 19:45:07 +08:00
|
|
|
{
|
2008-11-26 09:28:57 +08:00
|
|
|
unsigned int hmask = net->xfrm.policy_bydst[dir].hmask;
|
2006-08-24 19:45:07 +08:00
|
|
|
unsigned int nhashmask = xfrm_new_hash_mask(hmask);
|
|
|
|
unsigned int nsize = (nhashmask + 1) * sizeof(struct hlist_head);
|
2006-08-24 19:50:50 +08:00
|
|
|
struct hlist_head *ndst = xfrm_hash_alloc(nsize);
|
2016-08-11 21:17:53 +08:00
|
|
|
struct hlist_head *odst;
|
2006-08-24 19:45:07 +08:00
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!ndst)
|
|
|
|
return;
|
|
|
|
|
2016-08-11 21:17:59 +08:00
|
|
|
spin_lock_bh(&net->xfrm.xfrm_policy_lock);
|
2016-08-11 21:17:54 +08:00
|
|
|
write_seqcount_begin(&xfrm_policy_hash_generation);
|
|
|
|
|
|
|
|
odst = rcu_dereference_protected(net->xfrm.policy_bydst[dir].table,
|
|
|
|
lockdep_is_held(&net->xfrm.xfrm_policy_lock));
|
2006-08-24 19:45:07 +08:00
|
|
|
|
2016-08-11 21:17:53 +08:00
|
|
|
odst = rcu_dereference_protected(net->xfrm.policy_bydst[dir].table,
|
|
|
|
lockdep_is_held(&net->xfrm.xfrm_policy_lock));
|
|
|
|
|
2006-08-24 19:45:07 +08:00
|
|
|
for (i = hmask; i >= 0; i--)
|
xfrm: hash prefixed policies based on preflen thresholds
The idea is an extension of the current policy hashing.
Today only non-prefixed policies are stored in a hash table. This
patch relaxes the constraints, and hashes policies whose prefix
lengths are greater or equal to a configurable threshold.
Each hash table (one per direction) maintains its own set of IPv4 and
IPv6 thresholds (dbits4, sbits4, dbits6, sbits6), by default (32, 32,
128, 128).
Example, if the output hash table is configured with values (16, 24,
56, 64):
ip xfrm policy add dir out src 10.22.0.0/20 dst 10.24.1.0/24 ... => hashed
ip xfrm policy add dir out src 10.22.0.0/16 dst 10.24.1.1/32 ... => hashed
ip xfrm policy add dir out src 10.22.0.0/16 dst 10.24.0.0/16 ... => unhashed
ip xfrm policy add dir out \
src 3ffe:304:124:2200::/60 dst 3ffe:304:124:2401::/64 ... => hashed
ip xfrm policy add dir out \
src 3ffe:304:124:2200::/56 dst 3ffe:304:124:2401::2/128 ... => hashed
ip xfrm policy add dir out \
src 3ffe:304:124:2200::/56 dst 3ffe:304:124:2400::/56 ... => unhashed
The high order bits of the addresses (up to the threshold) are used to
compute the hash key.
Signed-off-by: Christophe Gouault <christophe.gouault@6wind.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
2014-08-29 22:16:04 +08:00
|
|
|
xfrm_dst_hash_transfer(net, odst + i, ndst, nhashmask, dir);
|
2006-08-24 19:45:07 +08:00
|
|
|
|
2016-08-11 21:17:53 +08:00
|
|
|
rcu_assign_pointer(net->xfrm.policy_bydst[dir].table, ndst);
|
2008-11-26 09:28:57 +08:00
|
|
|
net->xfrm.policy_bydst[dir].hmask = nhashmask;
|
2006-08-24 19:45:07 +08:00
|
|
|
|
2016-08-11 21:17:54 +08:00
|
|
|
write_seqcount_end(&xfrm_policy_hash_generation);
|
2016-08-11 21:17:59 +08:00
|
|
|
spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
|
2006-08-24 19:45:07 +08:00
|
|
|
|
2016-08-11 21:17:53 +08:00
|
|
|
synchronize_rcu();
|
|
|
|
|
2006-08-24 19:50:50 +08:00
|
|
|
xfrm_hash_free(odst, (hmask + 1) * sizeof(struct hlist_head));
|
2006-08-24 19:45:07 +08:00
|
|
|
}
|
|
|
|
|
2008-11-26 09:28:57 +08:00
|
|
|
static void xfrm_byidx_resize(struct net *net, int total)
|
2006-08-24 19:45:07 +08:00
|
|
|
{
|
2008-11-26 09:28:57 +08:00
|
|
|
unsigned int hmask = net->xfrm.policy_idx_hmask;
|
2006-08-24 19:45:07 +08:00
|
|
|
unsigned int nhashmask = xfrm_new_hash_mask(hmask);
|
|
|
|
unsigned int nsize = (nhashmask + 1) * sizeof(struct hlist_head);
|
2008-11-26 09:28:57 +08:00
|
|
|
struct hlist_head *oidx = net->xfrm.policy_byidx;
|
2006-08-24 19:50:50 +08:00
|
|
|
struct hlist_head *nidx = xfrm_hash_alloc(nsize);
|
2006-08-24 19:45:07 +08:00
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!nidx)
|
|
|
|
return;
|
|
|
|
|
2016-08-11 21:17:59 +08:00
|
|
|
spin_lock_bh(&net->xfrm.xfrm_policy_lock);
|
2006-08-24 19:45:07 +08:00
|
|
|
|
|
|
|
for (i = hmask; i >= 0; i--)
|
|
|
|
xfrm_idx_hash_transfer(oidx + i, nidx, nhashmask);
|
|
|
|
|
2008-11-26 09:28:57 +08:00
|
|
|
net->xfrm.policy_byidx = nidx;
|
|
|
|
net->xfrm.policy_idx_hmask = nhashmask;
|
2006-08-24 19:45:07 +08:00
|
|
|
|
2016-08-11 21:17:59 +08:00
|
|
|
spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
|
2006-08-24 19:45:07 +08:00
|
|
|
|
2006-08-24 19:50:50 +08:00
|
|
|
xfrm_hash_free(oidx, (hmask + 1) * sizeof(struct hlist_head));
|
2006-08-24 19:45:07 +08:00
|
|
|
}
|
|
|
|
|
2008-11-26 09:28:57 +08:00
|
|
|
static inline int xfrm_bydst_should_resize(struct net *net, int dir, int *total)
|
2006-08-24 19:45:07 +08:00
|
|
|
{
|
2008-11-26 09:28:57 +08:00
|
|
|
unsigned int cnt = net->xfrm.policy_count[dir];
|
|
|
|
unsigned int hmask = net->xfrm.policy_bydst[dir].hmask;
|
2006-08-24 19:45:07 +08:00
|
|
|
|
|
|
|
if (total)
|
|
|
|
*total += cnt;
|
|
|
|
|
|
|
|
if ((hmask + 1) < xfrm_policy_hashmax &&
|
|
|
|
cnt > hmask)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-11-26 09:28:57 +08:00
|
|
|
static inline int xfrm_byidx_should_resize(struct net *net, int total)
|
2006-08-24 19:45:07 +08:00
|
|
|
{
|
2008-11-26 09:28:57 +08:00
|
|
|
unsigned int hmask = net->xfrm.policy_idx_hmask;
|
2006-08-24 19:45:07 +08:00
|
|
|
|
|
|
|
if ((hmask + 1) < xfrm_policy_hashmax &&
|
|
|
|
total > hmask)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-01-23 21:37:10 +08:00
|
|
|
void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si)
|
2007-04-29 12:20:32 +08:00
|
|
|
{
|
2010-01-23 21:37:10 +08:00
|
|
|
si->incnt = net->xfrm.policy_count[XFRM_POLICY_IN];
|
|
|
|
si->outcnt = net->xfrm.policy_count[XFRM_POLICY_OUT];
|
|
|
|
si->fwdcnt = net->xfrm.policy_count[XFRM_POLICY_FWD];
|
|
|
|
si->inscnt = net->xfrm.policy_count[XFRM_POLICY_IN+XFRM_POLICY_MAX];
|
|
|
|
si->outscnt = net->xfrm.policy_count[XFRM_POLICY_OUT+XFRM_POLICY_MAX];
|
|
|
|
si->fwdscnt = net->xfrm.policy_count[XFRM_POLICY_FWD+XFRM_POLICY_MAX];
|
|
|
|
si->spdhcnt = net->xfrm.policy_idx_hmask;
|
2007-04-29 12:20:32 +08:00
|
|
|
si->spdhmcnt = xfrm_policy_hashmax;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(xfrm_spd_getinfo);
|
2006-08-24 19:45:07 +08:00
|
|
|
|
2007-04-29 12:20:32 +08:00
|
|
|
static DEFINE_MUTEX(hash_resize_mutex);
|
2008-11-26 09:28:57 +08:00
|
|
|
static void xfrm_hash_resize(struct work_struct *work)
|
2006-08-24 19:45:07 +08:00
|
|
|
{
|
2008-11-26 09:28:57 +08:00
|
|
|
struct net *net = container_of(work, struct net, xfrm.policy_hash_work);
|
2006-08-24 19:45:07 +08:00
|
|
|
int dir, total;
|
|
|
|
|
|
|
|
mutex_lock(&hash_resize_mutex);
|
|
|
|
|
|
|
|
total = 0;
|
2014-11-13 17:09:49 +08:00
|
|
|
for (dir = 0; dir < XFRM_POLICY_MAX; dir++) {
|
2008-11-26 09:28:57 +08:00
|
|
|
if (xfrm_bydst_should_resize(net, dir, &total))
|
|
|
|
xfrm_bydst_resize(net, dir);
|
2006-08-24 19:45:07 +08:00
|
|
|
}
|
2008-11-26 09:28:57 +08:00
|
|
|
if (xfrm_byidx_should_resize(net, total))
|
|
|
|
xfrm_byidx_resize(net, total);
|
2006-08-24 19:45:07 +08:00
|
|
|
|
|
|
|
mutex_unlock(&hash_resize_mutex);
|
|
|
|
}
|
|
|
|
|
xfrm: configure policy hash table thresholds by netlink
Enable to specify local and remote prefix length thresholds for the
policy hash table via a netlink XFRM_MSG_NEWSPDINFO message.
prefix length thresholds are specified by XFRMA_SPD_IPV4_HTHRESH and
XFRMA_SPD_IPV6_HTHRESH optional attributes (struct xfrmu_spdhthresh).
example:
struct xfrmu_spdhthresh thresh4 = {
.lbits = 0;
.rbits = 24;
};
struct xfrmu_spdhthresh thresh6 = {
.lbits = 0;
.rbits = 56;
};
struct nlmsghdr *hdr;
struct nl_msg *msg;
msg = nlmsg_alloc();
hdr = nlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, XFRMA_SPD_IPV4_HTHRESH, sizeof(__u32), NLM_F_REQUEST);
nla_put(msg, XFRMA_SPD_IPV4_HTHRESH, sizeof(thresh4), &thresh4);
nla_put(msg, XFRMA_SPD_IPV6_HTHRESH, sizeof(thresh6), &thresh6);
nla_send_auto(sk, msg);
The numbers are the policy selector minimum prefix lengths to put a
policy in the hash table.
- lbits is the local threshold (source address for out policies,
destination address for in and fwd policies).
- rbits is the remote threshold (destination address for out
policies, source address for in and fwd policies).
The default values are:
XFRMA_SPD_IPV4_HTHRESH: 32 32
XFRMA_SPD_IPV6_HTHRESH: 128 128
Dynamic re-building of the SPD is performed when the thresholds values
are changed.
The current thresholds can be read via a XFRM_MSG_GETSPDINFO request:
the kernel replies to XFRM_MSG_GETSPDINFO requests by an
XFRM_MSG_NEWSPDINFO message, with both attributes
XFRMA_SPD_IPV4_HTHRESH and XFRMA_SPD_IPV6_HTHRESH.
Signed-off-by: Christophe Gouault <christophe.gouault@6wind.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
2014-08-29 22:16:05 +08:00
|
|
|
static void xfrm_hash_rebuild(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct net *net = container_of(work, struct net,
|
|
|
|
xfrm.policy_hthresh.work);
|
|
|
|
unsigned int hmask;
|
|
|
|
struct xfrm_policy *pol;
|
|
|
|
struct xfrm_policy *policy;
|
|
|
|
struct hlist_head *chain;
|
|
|
|
struct hlist_head *odst;
|
|
|
|
struct hlist_node *newpos;
|
|
|
|
int i;
|
|
|
|
int dir;
|
|
|
|
unsigned seq;
|
|
|
|
u8 lbits4, rbits4, lbits6, rbits6;
|
|
|
|
|
|
|
|
mutex_lock(&hash_resize_mutex);
|
|
|
|
|
|
|
|
/* read selector prefixlen thresholds */
|
|
|
|
do {
|
|
|
|
seq = read_seqbegin(&net->xfrm.policy_hthresh.lock);
|
|
|
|
|
|
|
|
lbits4 = net->xfrm.policy_hthresh.lbits4;
|
|
|
|
rbits4 = net->xfrm.policy_hthresh.rbits4;
|
|
|
|
lbits6 = net->xfrm.policy_hthresh.lbits6;
|
|
|
|
rbits6 = net->xfrm.policy_hthresh.rbits6;
|
|
|
|
} while (read_seqretry(&net->xfrm.policy_hthresh.lock, seq));
|
|
|
|
|
2016-08-11 21:17:59 +08:00
|
|
|
spin_lock_bh(&net->xfrm.xfrm_policy_lock);
|
xfrm: configure policy hash table thresholds by netlink
Enable to specify local and remote prefix length thresholds for the
policy hash table via a netlink XFRM_MSG_NEWSPDINFO message.
prefix length thresholds are specified by XFRMA_SPD_IPV4_HTHRESH and
XFRMA_SPD_IPV6_HTHRESH optional attributes (struct xfrmu_spdhthresh).
example:
struct xfrmu_spdhthresh thresh4 = {
.lbits = 0;
.rbits = 24;
};
struct xfrmu_spdhthresh thresh6 = {
.lbits = 0;
.rbits = 56;
};
struct nlmsghdr *hdr;
struct nl_msg *msg;
msg = nlmsg_alloc();
hdr = nlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, XFRMA_SPD_IPV4_HTHRESH, sizeof(__u32), NLM_F_REQUEST);
nla_put(msg, XFRMA_SPD_IPV4_HTHRESH, sizeof(thresh4), &thresh4);
nla_put(msg, XFRMA_SPD_IPV6_HTHRESH, sizeof(thresh6), &thresh6);
nla_send_auto(sk, msg);
The numbers are the policy selector minimum prefix lengths to put a
policy in the hash table.
- lbits is the local threshold (source address for out policies,
destination address for in and fwd policies).
- rbits is the remote threshold (destination address for out
policies, source address for in and fwd policies).
The default values are:
XFRMA_SPD_IPV4_HTHRESH: 32 32
XFRMA_SPD_IPV6_HTHRESH: 128 128
Dynamic re-building of the SPD is performed when the thresholds values
are changed.
The current thresholds can be read via a XFRM_MSG_GETSPDINFO request:
the kernel replies to XFRM_MSG_GETSPDINFO requests by an
XFRM_MSG_NEWSPDINFO message, with both attributes
XFRMA_SPD_IPV4_HTHRESH and XFRMA_SPD_IPV6_HTHRESH.
Signed-off-by: Christophe Gouault <christophe.gouault@6wind.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
2014-08-29 22:16:05 +08:00
|
|
|
|
|
|
|
/* reset the bydst and inexact table in all directions */
|
2014-11-13 17:09:49 +08:00
|
|
|
for (dir = 0; dir < XFRM_POLICY_MAX; dir++) {
|
xfrm: configure policy hash table thresholds by netlink
Enable to specify local and remote prefix length thresholds for the
policy hash table via a netlink XFRM_MSG_NEWSPDINFO message.
prefix length thresholds are specified by XFRMA_SPD_IPV4_HTHRESH and
XFRMA_SPD_IPV6_HTHRESH optional attributes (struct xfrmu_spdhthresh).
example:
struct xfrmu_spdhthresh thresh4 = {
.lbits = 0;
.rbits = 24;
};
struct xfrmu_spdhthresh thresh6 = {
.lbits = 0;
.rbits = 56;
};
struct nlmsghdr *hdr;
struct nl_msg *msg;
msg = nlmsg_alloc();
hdr = nlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, XFRMA_SPD_IPV4_HTHRESH, sizeof(__u32), NLM_F_REQUEST);
nla_put(msg, XFRMA_SPD_IPV4_HTHRESH, sizeof(thresh4), &thresh4);
nla_put(msg, XFRMA_SPD_IPV6_HTHRESH, sizeof(thresh6), &thresh6);
nla_send_auto(sk, msg);
The numbers are the policy selector minimum prefix lengths to put a
policy in the hash table.
- lbits is the local threshold (source address for out policies,
destination address for in and fwd policies).
- rbits is the remote threshold (destination address for out
policies, source address for in and fwd policies).
The default values are:
XFRMA_SPD_IPV4_HTHRESH: 32 32
XFRMA_SPD_IPV6_HTHRESH: 128 128
Dynamic re-building of the SPD is performed when the thresholds values
are changed.
The current thresholds can be read via a XFRM_MSG_GETSPDINFO request:
the kernel replies to XFRM_MSG_GETSPDINFO requests by an
XFRM_MSG_NEWSPDINFO message, with both attributes
XFRMA_SPD_IPV4_HTHRESH and XFRMA_SPD_IPV6_HTHRESH.
Signed-off-by: Christophe Gouault <christophe.gouault@6wind.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
2014-08-29 22:16:05 +08:00
|
|
|
INIT_HLIST_HEAD(&net->xfrm.policy_inexact[dir]);
|
|
|
|
hmask = net->xfrm.policy_bydst[dir].hmask;
|
|
|
|
odst = net->xfrm.policy_bydst[dir].table;
|
|
|
|
for (i = hmask; i >= 0; i--)
|
|
|
|
INIT_HLIST_HEAD(odst + i);
|
|
|
|
if ((dir & XFRM_POLICY_MASK) == XFRM_POLICY_OUT) {
|
|
|
|
/* dir out => dst = remote, src = local */
|
|
|
|
net->xfrm.policy_bydst[dir].dbits4 = rbits4;
|
|
|
|
net->xfrm.policy_bydst[dir].sbits4 = lbits4;
|
|
|
|
net->xfrm.policy_bydst[dir].dbits6 = rbits6;
|
|
|
|
net->xfrm.policy_bydst[dir].sbits6 = lbits6;
|
|
|
|
} else {
|
|
|
|
/* dir in/fwd => dst = local, src = remote */
|
|
|
|
net->xfrm.policy_bydst[dir].dbits4 = lbits4;
|
|
|
|
net->xfrm.policy_bydst[dir].sbits4 = rbits4;
|
|
|
|
net->xfrm.policy_bydst[dir].dbits6 = lbits6;
|
|
|
|
net->xfrm.policy_bydst[dir].sbits6 = rbits6;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* re-insert all policies by order of creation */
|
|
|
|
list_for_each_entry_reverse(policy, &net->xfrm.policy_all, walk.all) {
|
2017-12-28 06:25:45 +08:00
|
|
|
if (policy->walk.dead ||
|
|
|
|
xfrm_policy_id2dir(policy->index) >= XFRM_POLICY_MAX) {
|
2016-07-29 15:57:32 +08:00
|
|
|
/* skip socket policies */
|
|
|
|
continue;
|
|
|
|
}
|
xfrm: configure policy hash table thresholds by netlink
Enable to specify local and remote prefix length thresholds for the
policy hash table via a netlink XFRM_MSG_NEWSPDINFO message.
prefix length thresholds are specified by XFRMA_SPD_IPV4_HTHRESH and
XFRMA_SPD_IPV6_HTHRESH optional attributes (struct xfrmu_spdhthresh).
example:
struct xfrmu_spdhthresh thresh4 = {
.lbits = 0;
.rbits = 24;
};
struct xfrmu_spdhthresh thresh6 = {
.lbits = 0;
.rbits = 56;
};
struct nlmsghdr *hdr;
struct nl_msg *msg;
msg = nlmsg_alloc();
hdr = nlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, XFRMA_SPD_IPV4_HTHRESH, sizeof(__u32), NLM_F_REQUEST);
nla_put(msg, XFRMA_SPD_IPV4_HTHRESH, sizeof(thresh4), &thresh4);
nla_put(msg, XFRMA_SPD_IPV6_HTHRESH, sizeof(thresh6), &thresh6);
nla_send_auto(sk, msg);
The numbers are the policy selector minimum prefix lengths to put a
policy in the hash table.
- lbits is the local threshold (source address for out policies,
destination address for in and fwd policies).
- rbits is the remote threshold (destination address for out
policies, source address for in and fwd policies).
The default values are:
XFRMA_SPD_IPV4_HTHRESH: 32 32
XFRMA_SPD_IPV6_HTHRESH: 128 128
Dynamic re-building of the SPD is performed when the thresholds values
are changed.
The current thresholds can be read via a XFRM_MSG_GETSPDINFO request:
the kernel replies to XFRM_MSG_GETSPDINFO requests by an
XFRM_MSG_NEWSPDINFO message, with both attributes
XFRMA_SPD_IPV4_HTHRESH and XFRMA_SPD_IPV6_HTHRESH.
Signed-off-by: Christophe Gouault <christophe.gouault@6wind.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
2014-08-29 22:16:05 +08:00
|
|
|
newpos = NULL;
|
|
|
|
chain = policy_hash_bysel(net, &policy->selector,
|
|
|
|
policy->family,
|
|
|
|
xfrm_policy_id2dir(policy->index));
|
|
|
|
hlist_for_each_entry(pol, chain, bydst) {
|
|
|
|
if (policy->priority >= pol->priority)
|
|
|
|
newpos = &pol->bydst;
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (newpos)
|
|
|
|
hlist_add_behind(&policy->bydst, newpos);
|
|
|
|
else
|
|
|
|
hlist_add_head(&policy->bydst, chain);
|
|
|
|
}
|
|
|
|
|
2016-08-11 21:17:59 +08:00
|
|
|
spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
|
xfrm: configure policy hash table thresholds by netlink
Enable to specify local and remote prefix length thresholds for the
policy hash table via a netlink XFRM_MSG_NEWSPDINFO message.
prefix length thresholds are specified by XFRMA_SPD_IPV4_HTHRESH and
XFRMA_SPD_IPV6_HTHRESH optional attributes (struct xfrmu_spdhthresh).
example:
struct xfrmu_spdhthresh thresh4 = {
.lbits = 0;
.rbits = 24;
};
struct xfrmu_spdhthresh thresh6 = {
.lbits = 0;
.rbits = 56;
};
struct nlmsghdr *hdr;
struct nl_msg *msg;
msg = nlmsg_alloc();
hdr = nlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, XFRMA_SPD_IPV4_HTHRESH, sizeof(__u32), NLM_F_REQUEST);
nla_put(msg, XFRMA_SPD_IPV4_HTHRESH, sizeof(thresh4), &thresh4);
nla_put(msg, XFRMA_SPD_IPV6_HTHRESH, sizeof(thresh6), &thresh6);
nla_send_auto(sk, msg);
The numbers are the policy selector minimum prefix lengths to put a
policy in the hash table.
- lbits is the local threshold (source address for out policies,
destination address for in and fwd policies).
- rbits is the remote threshold (destination address for out
policies, source address for in and fwd policies).
The default values are:
XFRMA_SPD_IPV4_HTHRESH: 32 32
XFRMA_SPD_IPV6_HTHRESH: 128 128
Dynamic re-building of the SPD is performed when the thresholds values
are changed.
The current thresholds can be read via a XFRM_MSG_GETSPDINFO request:
the kernel replies to XFRM_MSG_GETSPDINFO requests by an
XFRM_MSG_NEWSPDINFO message, with both attributes
XFRMA_SPD_IPV4_HTHRESH and XFRMA_SPD_IPV6_HTHRESH.
Signed-off-by: Christophe Gouault <christophe.gouault@6wind.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
2014-08-29 22:16:05 +08:00
|
|
|
|
|
|
|
mutex_unlock(&hash_resize_mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
void xfrm_policy_hash_rebuild(struct net *net)
|
|
|
|
{
|
|
|
|
schedule_work(&net->xfrm.policy_hthresh.work);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(xfrm_policy_hash_rebuild);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Generate new index... KAME seems to generate them ordered by cost
|
|
|
|
* of an absolute inpredictability of ordering of rules. This will not pass. */
|
2013-11-07 17:47:48 +08:00
|
|
|
static u32 xfrm_gen_index(struct net *net, int dir, u32 index)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
static u32 idx_generator;
|
|
|
|
|
|
|
|
for (;;) {
|
2006-08-24 19:45:07 +08:00
|
|
|
struct hlist_head *list;
|
|
|
|
struct xfrm_policy *p;
|
|
|
|
u32 idx;
|
|
|
|
int found;
|
|
|
|
|
2013-11-07 17:47:48 +08:00
|
|
|
if (!index) {
|
|
|
|
idx = (idx_generator | dir);
|
|
|
|
idx_generator += 8;
|
|
|
|
} else {
|
|
|
|
idx = index;
|
|
|
|
index = 0;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (idx == 0)
|
|
|
|
idx = 8;
|
2008-11-26 09:33:06 +08:00
|
|
|
list = net->xfrm.policy_byidx + idx_hash(net, idx);
|
2006-08-24 19:45:07 +08:00
|
|
|
found = 0;
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 09:06:00 +08:00
|
|
|
hlist_for_each_entry(p, list, byidx) {
|
2006-08-24 19:45:07 +08:00
|
|
|
if (p->index == idx) {
|
|
|
|
found = 1;
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
2006-08-24 19:45:07 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2006-08-24 19:45:07 +08:00
|
|
|
if (!found)
|
2005-04-17 06:20:36 +08:00
|
|
|
return idx;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-08-24 19:45:07 +08:00
|
|
|
static inline int selector_cmp(struct xfrm_selector *s1, struct xfrm_selector *s2)
|
|
|
|
{
|
|
|
|
u32 *p1 = (u32 *) s1;
|
|
|
|
u32 *p2 = (u32 *) s2;
|
|
|
|
int len = sizeof(struct xfrm_selector) / sizeof(u32);
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < len; i++) {
|
|
|
|
if (p1[i] != p2[i])
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-02-05 19:52:55 +08:00
|
|
|
static void xfrm_policy_requeue(struct xfrm_policy *old,
|
|
|
|
struct xfrm_policy *new)
|
|
|
|
{
|
|
|
|
struct xfrm_policy_queue *pq = &old->polq;
|
|
|
|
struct sk_buff_head list;
|
|
|
|
|
2015-04-30 17:25:19 +08:00
|
|
|
if (skb_queue_empty(&pq->hold_queue))
|
|
|
|
return;
|
|
|
|
|
2013-02-05 19:52:55 +08:00
|
|
|
__skb_queue_head_init(&list);
|
|
|
|
|
|
|
|
spin_lock_bh(&pq->hold_queue.lock);
|
|
|
|
skb_queue_splice_init(&pq->hold_queue, &list);
|
2013-10-08 16:49:45 +08:00
|
|
|
if (del_timer(&pq->hold_timer))
|
|
|
|
xfrm_pol_put(old);
|
2013-02-05 19:52:55 +08:00
|
|
|
spin_unlock_bh(&pq->hold_queue.lock);
|
|
|
|
|
|
|
|
pq = &new->polq;
|
|
|
|
|
|
|
|
spin_lock_bh(&pq->hold_queue.lock);
|
|
|
|
skb_queue_splice(&list, &pq->hold_queue);
|
|
|
|
pq->timeout = XFRM_QUEUE_TMO_MIN;
|
2013-10-08 16:49:45 +08:00
|
|
|
if (!mod_timer(&pq->hold_timer, jiffies))
|
|
|
|
xfrm_pol_hold(new);
|
2013-02-05 19:52:55 +08:00
|
|
|
spin_unlock_bh(&pq->hold_queue.lock);
|
|
|
|
}
|
|
|
|
|
2013-02-11 14:02:36 +08:00
|
|
|
static bool xfrm_policy_mark_match(struct xfrm_policy *policy,
|
|
|
|
struct xfrm_policy *pol)
|
|
|
|
{
|
|
|
|
u32 mark = policy->mark.v & policy->mark.m;
|
|
|
|
|
|
|
|
if (policy->mark.v == pol->mark.v && policy->mark.m == pol->mark.m)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if ((mark & pol->mark.m) == pol->mark.v &&
|
|
|
|
policy->priority == pol->priority)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
|
|
|
|
{
|
2008-11-26 09:33:06 +08:00
|
|
|
struct net *net = xp_net(policy);
|
2006-08-24 19:45:07 +08:00
|
|
|
struct xfrm_policy *pol;
|
|
|
|
struct xfrm_policy *delpol;
|
|
|
|
struct hlist_head *chain;
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 09:06:00 +08:00
|
|
|
struct hlist_node *newpos;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2016-08-11 21:17:59 +08:00
|
|
|
spin_lock_bh(&net->xfrm.xfrm_policy_lock);
|
2008-11-26 09:33:06 +08:00
|
|
|
chain = policy_hash_bysel(net, &policy->selector, policy->family, dir);
|
2006-08-24 19:45:07 +08:00
|
|
|
delpol = NULL;
|
|
|
|
newpos = NULL;
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 09:06:00 +08:00
|
|
|
hlist_for_each_entry(pol, chain, bydst) {
|
2007-01-17 08:52:02 +08:00
|
|
|
if (pol->type == policy->type &&
|
2006-08-24 19:45:07 +08:00
|
|
|
!selector_cmp(&pol->selector, &policy->selector) &&
|
2013-02-11 14:02:36 +08:00
|
|
|
xfrm_policy_mark_match(policy, pol) &&
|
2007-01-17 08:52:02 +08:00
|
|
|
xfrm_sec_ctx_match(pol->security, policy->security) &&
|
|
|
|
!WARN_ON(delpol)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
if (excl) {
|
2016-08-11 21:17:59 +08:00
|
|
|
spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EEXIST;
|
|
|
|
}
|
|
|
|
delpol = pol;
|
|
|
|
if (policy->priority > pol->priority)
|
|
|
|
continue;
|
|
|
|
} else if (policy->priority >= pol->priority) {
|
2007-01-17 08:52:02 +08:00
|
|
|
newpos = &pol->bydst;
|
2005-04-17 06:20:36 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (delpol)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (newpos)
|
2014-08-07 07:09:16 +08:00
|
|
|
hlist_add_behind(&policy->bydst, newpos);
|
2006-08-24 19:45:07 +08:00
|
|
|
else
|
|
|
|
hlist_add_head(&policy->bydst, chain);
|
2014-11-13 17:09:50 +08:00
|
|
|
__xfrm_policy_link(policy, dir);
|
2013-07-30 08:33:53 +08:00
|
|
|
|
|
|
|
/* After previous checking, family can either be AF_INET or AF_INET6 */
|
|
|
|
if (policy->family == AF_INET)
|
|
|
|
rt_genid_bump_ipv4(net);
|
|
|
|
else
|
|
|
|
rt_genid_bump_ipv6(net);
|
|
|
|
|
2013-02-05 19:52:55 +08:00
|
|
|
if (delpol) {
|
|
|
|
xfrm_policy_requeue(delpol, policy);
|
2008-12-03 16:33:09 +08:00
|
|
|
__xfrm_policy_unlink(delpol, dir);
|
2013-02-05 19:52:55 +08:00
|
|
|
}
|
2013-11-07 17:47:48 +08:00
|
|
|
policy->index = delpol ? delpol->index : xfrm_gen_index(net, dir, policy->index);
|
2008-11-26 09:33:06 +08:00
|
|
|
hlist_add_head(&policy->byidx, net->xfrm.policy_byidx+idx_hash(net, policy->index));
|
2007-03-05 08:12:44 +08:00
|
|
|
policy->curlft.add_time = get_seconds();
|
2005-04-17 06:20:36 +08:00
|
|
|
policy->curlft.use_time = 0;
|
|
|
|
if (!mod_timer(&policy->timer, jiffies + HZ))
|
|
|
|
xfrm_pol_hold(policy);
|
2016-08-11 21:17:59 +08:00
|
|
|
spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2005-12-22 23:39:48 +08:00
|
|
|
if (delpol)
|
2005-04-17 06:20:36 +08:00
|
|
|
xfrm_policy_kill(delpol);
|
2008-11-26 09:33:06 +08:00
|
|
|
else if (xfrm_bydst_should_resize(net, dir, NULL))
|
|
|
|
schedule_work(&net->xfrm.policy_hash_work);
|
2005-12-22 23:39:48 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(xfrm_policy_insert);
|
|
|
|
|
2010-02-22 19:32:57 +08:00
|
|
|
struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark, u8 type,
|
|
|
|
int dir, struct xfrm_selector *sel,
|
2007-03-08 07:37:58 +08:00
|
|
|
struct xfrm_sec_ctx *ctx, int delete,
|
|
|
|
int *err)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2006-08-24 19:45:07 +08:00
|
|
|
struct xfrm_policy *pol, *ret;
|
|
|
|
struct hlist_head *chain;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-03-08 07:37:58 +08:00
|
|
|
*err = 0;
|
2016-08-11 21:17:59 +08:00
|
|
|
spin_lock_bh(&net->xfrm.xfrm_policy_lock);
|
2008-11-26 09:34:20 +08:00
|
|
|
chain = policy_hash_bysel(net, sel, sel->family, dir);
|
2006-08-24 19:45:07 +08:00
|
|
|
ret = NULL;
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 09:06:00 +08:00
|
|
|
hlist_for_each_entry(pol, chain, bydst) {
|
2006-08-24 19:45:07 +08:00
|
|
|
if (pol->type == type &&
|
2010-02-22 19:32:58 +08:00
|
|
|
(mark & pol->mark.m) == pol->mark.v &&
|
2006-08-24 19:45:07 +08:00
|
|
|
!selector_cmp(sel, &pol->selector) &&
|
|
|
|
xfrm_sec_ctx_match(ctx, pol->security)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
xfrm_pol_hold(pol);
|
2006-08-24 19:45:07 +08:00
|
|
|
if (delete) {
|
2008-04-13 10:07:52 +08:00
|
|
|
*err = security_xfrm_policy_delete(
|
|
|
|
pol->security);
|
2007-03-08 07:37:58 +08:00
|
|
|
if (*err) {
|
2016-08-11 21:17:59 +08:00
|
|
|
spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
|
2007-03-08 07:37:58 +08:00
|
|
|
return pol;
|
|
|
|
}
|
2008-12-03 16:33:09 +08:00
|
|
|
__xfrm_policy_unlink(pol, dir);
|
2006-08-24 19:45:07 +08:00
|
|
|
}
|
|
|
|
ret = pol;
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2016-08-11 21:17:59 +08:00
|
|
|
spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-04-07 08:30:04 +08:00
|
|
|
if (ret && delete)
|
2006-08-24 19:45:07 +08:00
|
|
|
xfrm_policy_kill(ret);
|
|
|
|
return ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
[LSM-IPSec]: Security association restriction.
This patch series implements per packet access control via the
extension of the Linux Security Modules (LSM) interface by hooks in
the XFRM and pfkey subsystems that leverage IPSec security
associations to label packets. Extensions to the SELinux LSM are
included that leverage the patch for this purpose.
This patch implements the changes necessary to the XFRM subsystem,
pfkey interface, ipv4/ipv6, and xfrm_user interface to restrict a
socket to use only authorized security associations (or no security
association) to send/receive network packets.
Patch purpose:
The patch is designed to enable access control per packets based on
the strongly authenticated IPSec security association. Such access
controls augment the existing ones based on network interface and IP
address. The former are very coarse-grained, and the latter can be
spoofed. By using IPSec, the system can control access to remote
hosts based on cryptographic keys generated using the IPSec mechanism.
This enables access control on a per-machine basis or per-application
if the remote machine is running the same mechanism and trusted to
enforce the access control policy.
Patch design approach:
The overall approach is that policy (xfrm_policy) entries set by
user-level programs (e.g., setkey for ipsec-tools) are extended with a
security context that is used at policy selection time in the XFRM
subsystem to restrict the sockets that can send/receive packets via
security associations (xfrm_states) that are built from those
policies.
A presentation available at
www.selinux-symposium.org/2005/presentations/session2/2-3-jaeger.pdf
from the SELinux symposium describes the overall approach.
Patch implementation details:
On output, the policy retrieved (via xfrm_policy_lookup or
xfrm_sk_policy_lookup) must be authorized for the security context of
the socket and the same security context is required for resultant
security association (retrieved or negotiated via racoon in
ipsec-tools). This is enforced in xfrm_state_find.
On input, the policy retrieved must also be authorized for the socket
(at __xfrm_policy_check), and the security context of the policy must
also match the security association being used.
The patch has virtually no impact on packets that do not use IPSec.
The existing Netfilter (outgoing) and LSM rcv_skb hooks are used as
before.
Also, if IPSec is used without security contexts, the impact is
minimal. The LSM must allow such policies to be selected for the
combination of socket and remote machine, but subsequent IPSec
processing proceeds as in the original case.
Testing:
The pfkey interface is tested using the ipsec-tools. ipsec-tools have
been modified (a separate ipsec-tools patch is available for version
0.5) that supports assignment of xfrm_policy entries and security
associations with security contexts via setkey and the negotiation
using the security contexts via racoon.
The xfrm_user interface is tested via ad hoc programs that set
security contexts. These programs are also available from me, and
contain programs for setting, getting, and deleting policy for testing
this interface. Testing of sa functions was done by tracing kernel
behavior.
Signed-off-by: Trent Jaeger <tjaeger@cse.psu.edu>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2005-12-14 15:12:27 +08:00
|
|
|
EXPORT_SYMBOL(xfrm_policy_bysel_ctx);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-02-22 19:32:57 +08:00
|
|
|
struct xfrm_policy *xfrm_policy_byid(struct net *net, u32 mark, u8 type,
|
|
|
|
int dir, u32 id, int delete, int *err)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2006-08-24 19:45:07 +08:00
|
|
|
struct xfrm_policy *pol, *ret;
|
|
|
|
struct hlist_head *chain;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-05-14 17:15:47 +08:00
|
|
|
*err = -ENOENT;
|
|
|
|
if (xfrm_policy_id2dir(id) != dir)
|
|
|
|
return NULL;
|
|
|
|
|
2007-03-08 07:37:58 +08:00
|
|
|
*err = 0;
|
2016-08-11 21:17:59 +08:00
|
|
|
spin_lock_bh(&net->xfrm.xfrm_policy_lock);
|
2008-11-26 09:34:20 +08:00
|
|
|
chain = net->xfrm.policy_byidx + idx_hash(net, id);
|
2006-08-24 19:45:07 +08:00
|
|
|
ret = NULL;
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 09:06:00 +08:00
|
|
|
hlist_for_each_entry(pol, chain, byidx) {
|
2010-02-22 19:32:58 +08:00
|
|
|
if (pol->type == type && pol->index == id &&
|
|
|
|
(mark & pol->mark.m) == pol->mark.v) {
|
2005-04-17 06:20:36 +08:00
|
|
|
xfrm_pol_hold(pol);
|
2006-08-24 19:45:07 +08:00
|
|
|
if (delete) {
|
2008-04-13 10:07:52 +08:00
|
|
|
*err = security_xfrm_policy_delete(
|
|
|
|
pol->security);
|
2007-03-08 07:37:58 +08:00
|
|
|
if (*err) {
|
2016-08-11 21:17:59 +08:00
|
|
|
spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
|
2007-03-08 07:37:58 +08:00
|
|
|
return pol;
|
|
|
|
}
|
2008-12-03 16:33:09 +08:00
|
|
|
__xfrm_policy_unlink(pol, dir);
|
2006-08-24 19:45:07 +08:00
|
|
|
}
|
|
|
|
ret = pol;
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2016-08-11 21:17:59 +08:00
|
|
|
spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-04-07 08:30:04 +08:00
|
|
|
if (ret && delete)
|
2006-08-24 19:45:07 +08:00
|
|
|
xfrm_policy_kill(ret);
|
|
|
|
return ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(xfrm_policy_byid);
|
|
|
|
|
2007-06-05 07:05:57 +08:00
|
|
|
#ifdef CONFIG_SECURITY_NETWORK_XFRM
|
|
|
|
static inline int
|
2014-04-22 20:48:30 +08:00
|
|
|
xfrm_policy_flush_secctx_check(struct net *net, u8 type, bool task_valid)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2007-06-05 07:05:57 +08:00
|
|
|
int dir, err = 0;
|
|
|
|
|
|
|
|
for (dir = 0; dir < XFRM_POLICY_MAX; dir++) {
|
|
|
|
struct xfrm_policy *pol;
|
|
|
|
int i;
|
|
|
|
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 09:06:00 +08:00
|
|
|
hlist_for_each_entry(pol,
|
2008-11-26 09:33:32 +08:00
|
|
|
&net->xfrm.policy_inexact[dir], bydst) {
|
2007-06-05 07:05:57 +08:00
|
|
|
if (pol->type != type)
|
|
|
|
continue;
|
2008-04-13 10:07:52 +08:00
|
|
|
err = security_xfrm_policy_delete(pol->security);
|
2007-06-05 07:05:57 +08:00
|
|
|
if (err) {
|
2014-04-22 20:48:30 +08:00
|
|
|
xfrm_audit_policy_delete(pol, 0, task_valid);
|
2007-06-05 07:05:57 +08:00
|
|
|
return err;
|
|
|
|
}
|
2007-07-19 09:45:15 +08:00
|
|
|
}
|
2008-11-26 09:33:32 +08:00
|
|
|
for (i = net->xfrm.policy_bydst[dir].hmask; i >= 0; i--) {
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 09:06:00 +08:00
|
|
|
hlist_for_each_entry(pol,
|
2008-11-26 09:33:32 +08:00
|
|
|
net->xfrm.policy_bydst[dir].table + i,
|
2007-06-05 07:05:57 +08:00
|
|
|
bydst) {
|
|
|
|
if (pol->type != type)
|
|
|
|
continue;
|
2008-04-13 10:07:52 +08:00
|
|
|
err = security_xfrm_policy_delete(
|
|
|
|
pol->security);
|
2007-06-05 07:05:57 +08:00
|
|
|
if (err) {
|
2007-09-18 02:51:22 +08:00
|
|
|
xfrm_audit_policy_delete(pol, 0,
|
2014-04-22 20:48:30 +08:00
|
|
|
task_valid);
|
2007-06-05 07:05:57 +08:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
static inline int
|
2014-04-22 20:48:30 +08:00
|
|
|
xfrm_policy_flush_secctx_check(struct net *net, u8 type, bool task_valid)
|
2007-06-05 07:05:57 +08:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2014-04-22 20:48:30 +08:00
|
|
|
int xfrm_policy_flush(struct net *net, u8 type, bool task_valid)
|
2007-06-05 07:05:57 +08:00
|
|
|
{
|
2010-02-19 10:00:42 +08:00
|
|
|
int dir, err = 0, cnt = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2016-08-11 21:17:59 +08:00
|
|
|
spin_lock_bh(&net->xfrm.xfrm_policy_lock);
|
2007-06-05 07:05:57 +08:00
|
|
|
|
2014-04-22 20:48:30 +08:00
|
|
|
err = xfrm_policy_flush_secctx_check(net, type, task_valid);
|
2007-06-05 07:05:57 +08:00
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
for (dir = 0; dir < XFRM_POLICY_MAX; dir++) {
|
2006-08-24 19:45:07 +08:00
|
|
|
struct xfrm_policy *pol;
|
2008-12-03 16:33:09 +08:00
|
|
|
int i;
|
2006-08-24 19:45:07 +08:00
|
|
|
|
|
|
|
again1:
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 09:06:00 +08:00
|
|
|
hlist_for_each_entry(pol,
|
2008-11-26 09:33:32 +08:00
|
|
|
&net->xfrm.policy_inexact[dir], bydst) {
|
2006-08-24 19:45:07 +08:00
|
|
|
if (pol->type != type)
|
|
|
|
continue;
|
2010-03-31 08:17:05 +08:00
|
|
|
__xfrm_policy_unlink(pol, dir);
|
2016-08-11 21:17:59 +08:00
|
|
|
spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
|
2010-03-31 08:17:05 +08:00
|
|
|
cnt++;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2014-04-22 20:48:30 +08:00
|
|
|
xfrm_audit_policy_delete(pol, 1, task_valid);
|
2006-11-28 03:11:54 +08:00
|
|
|
|
2006-08-24 19:45:07 +08:00
|
|
|
xfrm_policy_kill(pol);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2016-08-24 19:08:40 +08:00
|
|
|
spin_lock_bh(&net->xfrm.xfrm_policy_lock);
|
2006-08-24 19:45:07 +08:00
|
|
|
goto again1;
|
|
|
|
}
|
|
|
|
|
2008-11-26 09:33:32 +08:00
|
|
|
for (i = net->xfrm.policy_bydst[dir].hmask; i >= 0; i--) {
|
2006-08-24 19:45:07 +08:00
|
|
|
again2:
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 09:06:00 +08:00
|
|
|
hlist_for_each_entry(pol,
|
2008-11-26 09:33:32 +08:00
|
|
|
net->xfrm.policy_bydst[dir].table + i,
|
2006-08-24 19:45:07 +08:00
|
|
|
bydst) {
|
|
|
|
if (pol->type != type)
|
|
|
|
continue;
|
2010-03-31 08:17:05 +08:00
|
|
|
__xfrm_policy_unlink(pol, dir);
|
2016-08-11 21:17:59 +08:00
|
|
|
spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
|
2010-03-31 08:17:05 +08:00
|
|
|
cnt++;
|
2006-08-24 19:45:07 +08:00
|
|
|
|
2014-04-22 20:48:30 +08:00
|
|
|
xfrm_audit_policy_delete(pol, 1, task_valid);
|
2006-08-24 19:45:07 +08:00
|
|
|
xfrm_policy_kill(pol);
|
|
|
|
|
2016-08-11 21:17:59 +08:00
|
|
|
spin_lock_bh(&net->xfrm.xfrm_policy_lock);
|
2006-08-24 19:45:07 +08:00
|
|
|
goto again2;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2006-08-24 19:45:07 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2010-02-19 10:00:42 +08:00
|
|
|
if (!cnt)
|
|
|
|
err = -ESRCH;
|
2007-06-05 07:05:57 +08:00
|
|
|
out:
|
2016-08-11 21:17:59 +08:00
|
|
|
spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
|
2007-06-05 07:05:57 +08:00
|
|
|
return err;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(xfrm_policy_flush);
|
|
|
|
|
2008-11-26 09:34:49 +08:00
|
|
|
int xfrm_policy_walk(struct net *net, struct xfrm_policy_walk *walk,
|
2008-02-29 13:31:08 +08:00
|
|
|
int (*func)(struct xfrm_policy *, int, int, void*),
|
2005-04-17 06:20:36 +08:00
|
|
|
void *data)
|
|
|
|
{
|
2008-10-01 22:03:24 +08:00
|
|
|
struct xfrm_policy *pol;
|
|
|
|
struct xfrm_policy_walk_entry *x;
|
2008-02-29 13:31:08 +08:00
|
|
|
int error = 0;
|
|
|
|
|
|
|
|
if (walk->type >= XFRM_POLICY_TYPE_MAX &&
|
|
|
|
walk->type != XFRM_POLICY_TYPE_ANY)
|
|
|
|
return -EINVAL;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-10-01 22:03:24 +08:00
|
|
|
if (list_empty(&walk->walk.all) && walk->seq != 0)
|
2008-02-29 13:31:08 +08:00
|
|
|
return 0;
|
|
|
|
|
2016-08-11 21:17:59 +08:00
|
|
|
spin_lock_bh(&net->xfrm.xfrm_policy_lock);
|
2008-10-01 22:03:24 +08:00
|
|
|
if (list_empty(&walk->walk.all))
|
2008-11-26 09:34:49 +08:00
|
|
|
x = list_first_entry(&net->xfrm.policy_all, struct xfrm_policy_walk_entry, all);
|
2008-10-01 22:03:24 +08:00
|
|
|
else
|
2015-04-22 17:09:54 +08:00
|
|
|
x = list_first_entry(&walk->walk.all,
|
|
|
|
struct xfrm_policy_walk_entry, all);
|
|
|
|
|
2008-11-26 09:34:49 +08:00
|
|
|
list_for_each_entry_from(x, &net->xfrm.policy_all, all) {
|
2008-10-01 22:03:24 +08:00
|
|
|
if (x->dead)
|
2008-02-29 13:31:08 +08:00
|
|
|
continue;
|
2008-10-01 22:03:24 +08:00
|
|
|
pol = container_of(x, struct xfrm_policy, walk);
|
|
|
|
if (walk->type != XFRM_POLICY_TYPE_ANY &&
|
|
|
|
walk->type != pol->type)
|
|
|
|
continue;
|
|
|
|
error = func(pol, xfrm_policy_id2dir(pol->index),
|
|
|
|
walk->seq, data);
|
|
|
|
if (error) {
|
|
|
|
list_move_tail(&walk->walk.all, &x->all);
|
|
|
|
goto out;
|
2006-08-24 19:45:07 +08:00
|
|
|
}
|
2008-10-01 22:03:24 +08:00
|
|
|
walk->seq++;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2008-10-01 22:03:24 +08:00
|
|
|
if (walk->seq == 0) {
|
2006-12-05 12:02:37 +08:00
|
|
|
error = -ENOENT;
|
|
|
|
goto out;
|
|
|
|
}
|
2008-10-01 22:03:24 +08:00
|
|
|
list_del_init(&walk->walk.all);
|
2005-04-17 06:20:36 +08:00
|
|
|
out:
|
2016-08-11 21:17:59 +08:00
|
|
|
spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(xfrm_policy_walk);
|
|
|
|
|
2008-10-01 22:03:24 +08:00
|
|
|
void xfrm_policy_walk_init(struct xfrm_policy_walk *walk, u8 type)
|
|
|
|
{
|
|
|
|
INIT_LIST_HEAD(&walk->walk.all);
|
|
|
|
walk->walk.dead = 1;
|
|
|
|
walk->type = type;
|
|
|
|
walk->seq = 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(xfrm_policy_walk_init);
|
|
|
|
|
2013-11-07 17:47:50 +08:00
|
|
|
void xfrm_policy_walk_done(struct xfrm_policy_walk *walk, struct net *net)
|
2008-10-01 22:03:24 +08:00
|
|
|
{
|
|
|
|
if (list_empty(&walk->walk.all))
|
|
|
|
return;
|
|
|
|
|
2016-08-11 21:17:59 +08:00
|
|
|
spin_lock_bh(&net->xfrm.xfrm_policy_lock); /*FIXME where is net? */
|
2008-10-01 22:03:24 +08:00
|
|
|
list_del(&walk->walk.all);
|
2016-08-11 21:17:59 +08:00
|
|
|
spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
|
2008-10-01 22:03:24 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(xfrm_policy_walk_done);
|
|
|
|
|
IPsec: propagate security module errors up from flow_cache_lookup
When a security module is loaded (in this case, SELinux), the
security_xfrm_policy_lookup() hook can return an access denied permission
(or other error). We were not handling that correctly, and in fact
inverting the return logic and propagating a false "ok" back up to
xfrm_lookup(), which then allowed packets to pass as if they were not
associated with an xfrm policy.
The way I was seeing the problem was when connecting via IPsec to a
confined service on an SELinux box (vsftpd), which did not have the
appropriate SELinux policy permissions to send packets via IPsec.
The first SYNACK would be blocked, because of an uncached lookup via
flow_cache_lookup(), which would fail to resolve an xfrm policy because
the SELinux policy is checked at that point via the resolver.
However, retransmitted SYNACKs would then find a cached flow entry when
calling into flow_cache_lookup() with a null xfrm policy, which is
interpreted by xfrm_lookup() as the packet not having any associated
policy and similarly to the first case, allowing it to pass without
transformation.
The solution presented here is to first ensure that errno values are
correctly propagated all the way back up through the various call chains
from security_xfrm_policy_lookup(), and handled correctly.
Then, flow_cache_lookup() is modified, so that if the policy resolver
fails (typically a permission denied via the security module), the flow
cache entry is killed rather than having a null policy assigned (which
indicates that the packet can pass freely). This also forces any future
lookups for the same flow to consult the security module (e.g. SELinux)
for current security policy (rather than, say, caching the error on the
flow cache entry).
Signed-off-by: James Morris <jmorris@namei.org>
2006-10-06 04:42:27 +08:00
|
|
|
/*
|
|
|
|
* Find policy to apply to this flow.
|
|
|
|
*
|
|
|
|
* Returns 0 if policy found, else an -errno.
|
|
|
|
*/
|
2011-02-24 14:23:30 +08:00
|
|
|
static int xfrm_policy_match(const struct xfrm_policy *pol,
|
|
|
|
const struct flowi *fl,
|
2006-08-24 19:45:07 +08:00
|
|
|
u8 type, u16 family, int dir)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2011-02-24 14:23:30 +08:00
|
|
|
const struct xfrm_selector *sel = &pol->selector;
|
2012-05-16 03:04:57 +08:00
|
|
|
int ret = -ESRCH;
|
|
|
|
bool match;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-08-24 19:45:07 +08:00
|
|
|
if (pol->family != family ||
|
2011-03-12 13:29:39 +08:00
|
|
|
(fl->flowi_mark & pol->mark.m) != pol->mark.v ||
|
2006-08-24 19:45:07 +08:00
|
|
|
pol->type != type)
|
IPsec: propagate security module errors up from flow_cache_lookup
When a security module is loaded (in this case, SELinux), the
security_xfrm_policy_lookup() hook can return an access denied permission
(or other error). We were not handling that correctly, and in fact
inverting the return logic and propagating a false "ok" back up to
xfrm_lookup(), which then allowed packets to pass as if they were not
associated with an xfrm policy.
The way I was seeing the problem was when connecting via IPsec to a
confined service on an SELinux box (vsftpd), which did not have the
appropriate SELinux policy permissions to send packets via IPsec.
The first SYNACK would be blocked, because of an uncached lookup via
flow_cache_lookup(), which would fail to resolve an xfrm policy because
the SELinux policy is checked at that point via the resolver.
However, retransmitted SYNACKs would then find a cached flow entry when
calling into flow_cache_lookup() with a null xfrm policy, which is
interpreted by xfrm_lookup() as the packet not having any associated
policy and similarly to the first case, allowing it to pass without
transformation.
The solution presented here is to first ensure that errno values are
correctly propagated all the way back up through the various call chains
from security_xfrm_policy_lookup(), and handled correctly.
Then, flow_cache_lookup() is modified, so that if the policy resolver
fails (typically a permission denied via the security module), the flow
cache entry is killed rather than having a null policy assigned (which
indicates that the packet can pass freely). This also forces any future
lookups for the same flow to consult the security module (e.g. SELinux)
for current security policy (rather than, say, caching the error on the
flow cache entry).
Signed-off-by: James Morris <jmorris@namei.org>
2006-10-06 04:42:27 +08:00
|
|
|
return ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-08-24 19:45:07 +08:00
|
|
|
match = xfrm_selector_match(sel, fl, family);
|
IPsec: propagate security module errors up from flow_cache_lookup
When a security module is loaded (in this case, SELinux), the
security_xfrm_policy_lookup() hook can return an access denied permission
(or other error). We were not handling that correctly, and in fact
inverting the return logic and propagating a false "ok" back up to
xfrm_lookup(), which then allowed packets to pass as if they were not
associated with an xfrm policy.
The way I was seeing the problem was when connecting via IPsec to a
confined service on an SELinux box (vsftpd), which did not have the
appropriate SELinux policy permissions to send packets via IPsec.
The first SYNACK would be blocked, because of an uncached lookup via
flow_cache_lookup(), which would fail to resolve an xfrm policy because
the SELinux policy is checked at that point via the resolver.
However, retransmitted SYNACKs would then find a cached flow entry when
calling into flow_cache_lookup() with a null xfrm policy, which is
interpreted by xfrm_lookup() as the packet not having any associated
policy and similarly to the first case, allowing it to pass without
transformation.
The solution presented here is to first ensure that errno values are
correctly propagated all the way back up through the various call chains
from security_xfrm_policy_lookup(), and handled correctly.
Then, flow_cache_lookup() is modified, so that if the policy resolver
fails (typically a permission denied via the security module), the flow
cache entry is killed rather than having a null policy assigned (which
indicates that the packet can pass freely). This also forces any future
lookups for the same flow to consult the security module (e.g. SELinux)
for current security policy (rather than, say, caching the error on the
flow cache entry).
Signed-off-by: James Morris <jmorris@namei.org>
2006-10-06 04:42:27 +08:00
|
|
|
if (match)
|
2011-03-12 13:29:39 +08:00
|
|
|
ret = security_xfrm_policy_lookup(pol->security, fl->flowi_secid,
|
2008-04-13 10:07:52 +08:00
|
|
|
dir);
|
2006-08-24 19:45:07 +08:00
|
|
|
|
IPsec: propagate security module errors up from flow_cache_lookup
When a security module is loaded (in this case, SELinux), the
security_xfrm_policy_lookup() hook can return an access denied permission
(or other error). We were not handling that correctly, and in fact
inverting the return logic and propagating a false "ok" back up to
xfrm_lookup(), which then allowed packets to pass as if they were not
associated with an xfrm policy.
The way I was seeing the problem was when connecting via IPsec to a
confined service on an SELinux box (vsftpd), which did not have the
appropriate SELinux policy permissions to send packets via IPsec.
The first SYNACK would be blocked, because of an uncached lookup via
flow_cache_lookup(), which would fail to resolve an xfrm policy because
the SELinux policy is checked at that point via the resolver.
However, retransmitted SYNACKs would then find a cached flow entry when
calling into flow_cache_lookup() with a null xfrm policy, which is
interpreted by xfrm_lookup() as the packet not having any associated
policy and similarly to the first case, allowing it to pass without
transformation.
The solution presented here is to first ensure that errno values are
correctly propagated all the way back up through the various call chains
from security_xfrm_policy_lookup(), and handled correctly.
Then, flow_cache_lookup() is modified, so that if the policy resolver
fails (typically a permission denied via the security module), the flow
cache entry is killed rather than having a null policy assigned (which
indicates that the packet can pass freely). This also forces any future
lookups for the same flow to consult the security module (e.g. SELinux)
for current security policy (rather than, say, caching the error on the
flow cache entry).
Signed-off-by: James Morris <jmorris@namei.org>
2006-10-06 04:42:27 +08:00
|
|
|
return ret;
|
2006-08-24 19:45:07 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-11-26 09:35:18 +08:00
|
|
|
static struct xfrm_policy *xfrm_policy_lookup_bytype(struct net *net, u8 type,
|
2011-02-23 10:31:08 +08:00
|
|
|
const struct flowi *fl,
|
2006-08-24 19:45:07 +08:00
|
|
|
u16 family, u8 dir)
|
|
|
|
{
|
IPsec: propagate security module errors up from flow_cache_lookup
When a security module is loaded (in this case, SELinux), the
security_xfrm_policy_lookup() hook can return an access denied permission
(or other error). We were not handling that correctly, and in fact
inverting the return logic and propagating a false "ok" back up to
xfrm_lookup(), which then allowed packets to pass as if they were not
associated with an xfrm policy.
The way I was seeing the problem was when connecting via IPsec to a
confined service on an SELinux box (vsftpd), which did not have the
appropriate SELinux policy permissions to send packets via IPsec.
The first SYNACK would be blocked, because of an uncached lookup via
flow_cache_lookup(), which would fail to resolve an xfrm policy because
the SELinux policy is checked at that point via the resolver.
However, retransmitted SYNACKs would then find a cached flow entry when
calling into flow_cache_lookup() with a null xfrm policy, which is
interpreted by xfrm_lookup() as the packet not having any associated
policy and similarly to the first case, allowing it to pass without
transformation.
The solution presented here is to first ensure that errno values are
correctly propagated all the way back up through the various call chains
from security_xfrm_policy_lookup(), and handled correctly.
Then, flow_cache_lookup() is modified, so that if the policy resolver
fails (typically a permission denied via the security module), the flow
cache entry is killed rather than having a null policy assigned (which
indicates that the packet can pass freely). This also forces any future
lookups for the same flow to consult the security module (e.g. SELinux)
for current security policy (rather than, say, caching the error on the
flow cache entry).
Signed-off-by: James Morris <jmorris@namei.org>
2006-10-06 04:42:27 +08:00
|
|
|
int err;
|
2006-08-24 19:45:07 +08:00
|
|
|
struct xfrm_policy *pol, *ret;
|
2011-02-24 14:22:48 +08:00
|
|
|
const xfrm_address_t *daddr, *saddr;
|
2006-08-24 19:45:07 +08:00
|
|
|
struct hlist_head *chain;
|
2016-08-11 21:17:54 +08:00
|
|
|
unsigned int sequence;
|
|
|
|
u32 priority;
|
[LSM-IPSec]: Security association restriction.
This patch series implements per packet access control via the
extension of the Linux Security Modules (LSM) interface by hooks in
the XFRM and pfkey subsystems that leverage IPSec security
associations to label packets. Extensions to the SELinux LSM are
included that leverage the patch for this purpose.
This patch implements the changes necessary to the XFRM subsystem,
pfkey interface, ipv4/ipv6, and xfrm_user interface to restrict a
socket to use only authorized security associations (or no security
association) to send/receive network packets.
Patch purpose:
The patch is designed to enable access control per packets based on
the strongly authenticated IPSec security association. Such access
controls augment the existing ones based on network interface and IP
address. The former are very coarse-grained, and the latter can be
spoofed. By using IPSec, the system can control access to remote
hosts based on cryptographic keys generated using the IPSec mechanism.
This enables access control on a per-machine basis or per-application
if the remote machine is running the same mechanism and trusted to
enforce the access control policy.
Patch design approach:
The overall approach is that policy (xfrm_policy) entries set by
user-level programs (e.g., setkey for ipsec-tools) are extended with a
security context that is used at policy selection time in the XFRM
subsystem to restrict the sockets that can send/receive packets via
security associations (xfrm_states) that are built from those
policies.
A presentation available at
www.selinux-symposium.org/2005/presentations/session2/2-3-jaeger.pdf
from the SELinux symposium describes the overall approach.
Patch implementation details:
On output, the policy retrieved (via xfrm_policy_lookup or
xfrm_sk_policy_lookup) must be authorized for the security context of
the socket and the same security context is required for resultant
security association (retrieved or negotiated via racoon in
ipsec-tools). This is enforced in xfrm_state_find.
On input, the policy retrieved must also be authorized for the socket
(at __xfrm_policy_check), and the security context of the policy must
also match the security association being used.
The patch has virtually no impact on packets that do not use IPSec.
The existing Netfilter (outgoing) and LSM rcv_skb hooks are used as
before.
Also, if IPSec is used without security contexts, the impact is
minimal. The LSM must allow such policies to be selected for the
combination of socket and remote machine, but subsequent IPSec
processing proceeds as in the original case.
Testing:
The pfkey interface is tested using the ipsec-tools. ipsec-tools have
been modified (a separate ipsec-tools patch is available for version
0.5) that supports assignment of xfrm_policy entries and security
associations with security contexts via setkey and the negotiation
using the security contexts via racoon.
The xfrm_user interface is tested via ad hoc programs that set
security contexts. These programs are also available from me, and
contain programs for setting, getting, and deleting policy for testing
this interface. Testing of sa functions was done by tracing kernel
behavior.
Signed-off-by: Trent Jaeger <tjaeger@cse.psu.edu>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2005-12-14 15:12:27 +08:00
|
|
|
|
2006-08-24 19:45:07 +08:00
|
|
|
daddr = xfrm_flowi_daddr(fl, family);
|
|
|
|
saddr = xfrm_flowi_saddr(fl, family);
|
|
|
|
if (unlikely(!daddr || !saddr))
|
|
|
|
return NULL;
|
|
|
|
|
2016-08-11 21:17:56 +08:00
|
|
|
rcu_read_lock();
|
2016-08-11 21:17:54 +08:00
|
|
|
retry:
|
|
|
|
do {
|
|
|
|
sequence = read_seqcount_begin(&xfrm_policy_hash_generation);
|
|
|
|
chain = policy_hash_direct(net, daddr, saddr, family, dir);
|
|
|
|
} while (read_seqcount_retry(&xfrm_policy_hash_generation, sequence));
|
|
|
|
|
|
|
|
priority = ~0U;
|
2006-08-24 19:45:07 +08:00
|
|
|
ret = NULL;
|
2016-08-11 21:17:52 +08:00
|
|
|
hlist_for_each_entry_rcu(pol, chain, bydst) {
|
IPsec: propagate security module errors up from flow_cache_lookup
When a security module is loaded (in this case, SELinux), the
security_xfrm_policy_lookup() hook can return an access denied permission
(or other error). We were not handling that correctly, and in fact
inverting the return logic and propagating a false "ok" back up to
xfrm_lookup(), which then allowed packets to pass as if they were not
associated with an xfrm policy.
The way I was seeing the problem was when connecting via IPsec to a
confined service on an SELinux box (vsftpd), which did not have the
appropriate SELinux policy permissions to send packets via IPsec.
The first SYNACK would be blocked, because of an uncached lookup via
flow_cache_lookup(), which would fail to resolve an xfrm policy because
the SELinux policy is checked at that point via the resolver.
However, retransmitted SYNACKs would then find a cached flow entry when
calling into flow_cache_lookup() with a null xfrm policy, which is
interpreted by xfrm_lookup() as the packet not having any associated
policy and similarly to the first case, allowing it to pass without
transformation.
The solution presented here is to first ensure that errno values are
correctly propagated all the way back up through the various call chains
from security_xfrm_policy_lookup(), and handled correctly.
Then, flow_cache_lookup() is modified, so that if the policy resolver
fails (typically a permission denied via the security module), the flow
cache entry is killed rather than having a null policy assigned (which
indicates that the packet can pass freely). This also forces any future
lookups for the same flow to consult the security module (e.g. SELinux)
for current security policy (rather than, say, caching the error on the
flow cache entry).
Signed-off-by: James Morris <jmorris@namei.org>
2006-10-06 04:42:27 +08:00
|
|
|
err = xfrm_policy_match(pol, fl, type, family, dir);
|
|
|
|
if (err) {
|
|
|
|
if (err == -ESRCH)
|
|
|
|
continue;
|
|
|
|
else {
|
|
|
|
ret = ERR_PTR(err);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
} else {
|
2006-08-24 19:45:07 +08:00
|
|
|
ret = pol;
|
2006-08-26 06:46:46 +08:00
|
|
|
priority = ret->priority;
|
2006-08-24 19:45:07 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2008-11-26 09:35:18 +08:00
|
|
|
chain = &net->xfrm.policy_inexact[dir];
|
2016-08-11 21:17:52 +08:00
|
|
|
hlist_for_each_entry_rcu(pol, chain, bydst) {
|
2015-05-14 11:16:59 +08:00
|
|
|
if ((pol->priority >= priority) && ret)
|
|
|
|
break;
|
|
|
|
|
IPsec: propagate security module errors up from flow_cache_lookup
When a security module is loaded (in this case, SELinux), the
security_xfrm_policy_lookup() hook can return an access denied permission
(or other error). We were not handling that correctly, and in fact
inverting the return logic and propagating a false "ok" back up to
xfrm_lookup(), which then allowed packets to pass as if they were not
associated with an xfrm policy.
The way I was seeing the problem was when connecting via IPsec to a
confined service on an SELinux box (vsftpd), which did not have the
appropriate SELinux policy permissions to send packets via IPsec.
The first SYNACK would be blocked, because of an uncached lookup via
flow_cache_lookup(), which would fail to resolve an xfrm policy because
the SELinux policy is checked at that point via the resolver.
However, retransmitted SYNACKs would then find a cached flow entry when
calling into flow_cache_lookup() with a null xfrm policy, which is
interpreted by xfrm_lookup() as the packet not having any associated
policy and similarly to the first case, allowing it to pass without
transformation.
The solution presented here is to first ensure that errno values are
correctly propagated all the way back up through the various call chains
from security_xfrm_policy_lookup(), and handled correctly.
Then, flow_cache_lookup() is modified, so that if the policy resolver
fails (typically a permission denied via the security module), the flow
cache entry is killed rather than having a null policy assigned (which
indicates that the packet can pass freely). This also forces any future
lookups for the same flow to consult the security module (e.g. SELinux)
for current security policy (rather than, say, caching the error on the
flow cache entry).
Signed-off-by: James Morris <jmorris@namei.org>
2006-10-06 04:42:27 +08:00
|
|
|
err = xfrm_policy_match(pol, fl, type, family, dir);
|
|
|
|
if (err) {
|
|
|
|
if (err == -ESRCH)
|
|
|
|
continue;
|
|
|
|
else {
|
|
|
|
ret = ERR_PTR(err);
|
|
|
|
goto fail;
|
|
|
|
}
|
2015-05-14 11:16:59 +08:00
|
|
|
} else {
|
2006-08-26 06:46:46 +08:00
|
|
|
ret = pol;
|
|
|
|
break;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
2015-04-30 17:13:41 +08:00
|
|
|
|
2016-08-11 21:17:54 +08:00
|
|
|
if (read_seqcount_retry(&xfrm_policy_hash_generation, sequence))
|
|
|
|
goto retry;
|
|
|
|
|
2016-08-11 21:17:55 +08:00
|
|
|
if (ret && !xfrm_pol_hold_rcu(ret))
|
|
|
|
goto retry;
|
IPsec: propagate security module errors up from flow_cache_lookup
When a security module is loaded (in this case, SELinux), the
security_xfrm_policy_lookup() hook can return an access denied permission
(or other error). We were not handling that correctly, and in fact
inverting the return logic and propagating a false "ok" back up to
xfrm_lookup(), which then allowed packets to pass as if they were not
associated with an xfrm policy.
The way I was seeing the problem was when connecting via IPsec to a
confined service on an SELinux box (vsftpd), which did not have the
appropriate SELinux policy permissions to send packets via IPsec.
The first SYNACK would be blocked, because of an uncached lookup via
flow_cache_lookup(), which would fail to resolve an xfrm policy because
the SELinux policy is checked at that point via the resolver.
However, retransmitted SYNACKs would then find a cached flow entry when
calling into flow_cache_lookup() with a null xfrm policy, which is
interpreted by xfrm_lookup() as the packet not having any associated
policy and similarly to the first case, allowing it to pass without
transformation.
The solution presented here is to first ensure that errno values are
correctly propagated all the way back up through the various call chains
from security_xfrm_policy_lookup(), and handled correctly.
Then, flow_cache_lookup() is modified, so that if the policy resolver
fails (typically a permission denied via the security module), the flow
cache entry is killed rather than having a null policy assigned (which
indicates that the packet can pass freely). This also forces any future
lookups for the same flow to consult the security module (e.g. SELinux)
for current security policy (rather than, say, caching the error on the
flow cache entry).
Signed-off-by: James Morris <jmorris@namei.org>
2006-10-06 04:42:27 +08:00
|
|
|
fail:
|
2016-08-11 21:17:56 +08:00
|
|
|
rcu_read_unlock();
|
2006-08-24 13:43:30 +08:00
|
|
|
|
2006-08-24 19:45:07 +08:00
|
|
|
return ret;
|
2006-08-24 13:43:30 +08:00
|
|
|
}
|
|
|
|
|
2010-04-07 08:30:05 +08:00
|
|
|
static struct xfrm_policy *
|
2017-07-17 19:57:24 +08:00
|
|
|
xfrm_policy_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir)
|
2010-04-07 08:30:05 +08:00
|
|
|
{
|
|
|
|
#ifdef CONFIG_XFRM_SUB_POLICY
|
|
|
|
struct xfrm_policy *pol;
|
|
|
|
|
|
|
|
pol = xfrm_policy_lookup_bytype(net, XFRM_POLICY_TYPE_SUB, fl, family, dir);
|
|
|
|
if (pol != NULL)
|
|
|
|
return pol;
|
|
|
|
#endif
|
|
|
|
return xfrm_policy_lookup_bytype(net, XFRM_POLICY_TYPE_MAIN, fl, family, dir);
|
|
|
|
}
|
|
|
|
|
2015-09-25 22:39:10 +08:00
|
|
|
static struct xfrm_policy *xfrm_sk_policy_lookup(const struct sock *sk, int dir,
|
2017-02-14 14:43:56 +08:00
|
|
|
const struct flowi *fl, u16 family)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct xfrm_policy *pol;
|
|
|
|
|
2015-12-08 23:22:02 +08:00
|
|
|
rcu_read_lock();
|
2016-08-11 21:17:57 +08:00
|
|
|
again:
|
2015-12-08 23:22:02 +08:00
|
|
|
pol = rcu_dereference(sk->sk_policy[dir]);
|
|
|
|
if (pol != NULL) {
|
2017-11-29 13:53:55 +08:00
|
|
|
bool match;
|
2007-02-09 22:25:29 +08:00
|
|
|
int err = 0;
|
[LSM-IPSec]: Security association restriction.
This patch series implements per packet access control via the
extension of the Linux Security Modules (LSM) interface by hooks in
the XFRM and pfkey subsystems that leverage IPSec security
associations to label packets. Extensions to the SELinux LSM are
included that leverage the patch for this purpose.
This patch implements the changes necessary to the XFRM subsystem,
pfkey interface, ipv4/ipv6, and xfrm_user interface to restrict a
socket to use only authorized security associations (or no security
association) to send/receive network packets.
Patch purpose:
The patch is designed to enable access control per packets based on
the strongly authenticated IPSec security association. Such access
controls augment the existing ones based on network interface and IP
address. The former are very coarse-grained, and the latter can be
spoofed. By using IPSec, the system can control access to remote
hosts based on cryptographic keys generated using the IPSec mechanism.
This enables access control on a per-machine basis or per-application
if the remote machine is running the same mechanism and trusted to
enforce the access control policy.
Patch design approach:
The overall approach is that policy (xfrm_policy) entries set by
user-level programs (e.g., setkey for ipsec-tools) are extended with a
security context that is used at policy selection time in the XFRM
subsystem to restrict the sockets that can send/receive packets via
security associations (xfrm_states) that are built from those
policies.
A presentation available at
www.selinux-symposium.org/2005/presentations/session2/2-3-jaeger.pdf
from the SELinux symposium describes the overall approach.
Patch implementation details:
On output, the policy retrieved (via xfrm_policy_lookup or
xfrm_sk_policy_lookup) must be authorized for the security context of
the socket and the same security context is required for resultant
security association (retrieved or negotiated via racoon in
ipsec-tools). This is enforced in xfrm_state_find.
On input, the policy retrieved must also be authorized for the socket
(at __xfrm_policy_check), and the security context of the policy must
also match the security association being used.
The patch has virtually no impact on packets that do not use IPSec.
The existing Netfilter (outgoing) and LSM rcv_skb hooks are used as
before.
Also, if IPSec is used without security contexts, the impact is
minimal. The LSM must allow such policies to be selected for the
combination of socket and remote machine, but subsequent IPSec
processing proceeds as in the original case.
Testing:
The pfkey interface is tested using the ipsec-tools. ipsec-tools have
been modified (a separate ipsec-tools patch is available for version
0.5) that supports assignment of xfrm_policy entries and security
associations with security contexts via setkey and the negotiation
using the security contexts via racoon.
The xfrm_user interface is tested via ad hoc programs that set
security contexts. These programs are also available from me, and
contain programs for setting, getting, and deleting policy for testing
this interface. Testing of sa functions was done by tracing kernel
behavior.
Signed-off-by: Trent Jaeger <tjaeger@cse.psu.edu>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2005-12-14 15:12:27 +08:00
|
|
|
|
2017-11-29 13:53:55 +08:00
|
|
|
if (pol->family != family) {
|
|
|
|
pol = NULL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
match = xfrm_selector_match(&pol->selector, fl, family);
|
2006-10-06 04:42:35 +08:00
|
|
|
if (match) {
|
2010-02-22 19:32:58 +08:00
|
|
|
if ((sk->sk_mark & pol->mark.m) != pol->mark.v) {
|
|
|
|
pol = NULL;
|
|
|
|
goto out;
|
|
|
|
}
|
2008-04-13 10:07:52 +08:00
|
|
|
err = security_xfrm_policy_lookup(pol->security,
|
2011-03-12 13:29:39 +08:00
|
|
|
fl->flowi_secid,
|
2017-07-17 19:57:23 +08:00
|
|
|
dir);
|
2016-11-17 20:21:46 +08:00
|
|
|
if (!err) {
|
|
|
|
if (!xfrm_pol_hold_rcu(pol))
|
|
|
|
goto again;
|
|
|
|
} else if (err == -ESRCH) {
|
2006-10-06 04:42:35 +08:00
|
|
|
pol = NULL;
|
2016-11-17 20:21:46 +08:00
|
|
|
} else {
|
2006-10-06 04:42:35 +08:00
|
|
|
pol = ERR_PTR(err);
|
2016-11-17 20:21:46 +08:00
|
|
|
}
|
2006-10-06 04:42:35 +08:00
|
|
|
} else
|
2005-04-17 06:20:36 +08:00
|
|
|
pol = NULL;
|
|
|
|
}
|
2010-02-22 19:32:58 +08:00
|
|
|
out:
|
2015-12-08 23:22:02 +08:00
|
|
|
rcu_read_unlock();
|
2005-04-17 06:20:36 +08:00
|
|
|
return pol;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __xfrm_policy_link(struct xfrm_policy *pol, int dir)
|
|
|
|
{
|
2008-11-26 09:29:47 +08:00
|
|
|
struct net *net = xp_net(pol);
|
2006-08-24 13:43:30 +08:00
|
|
|
|
2008-11-26 09:29:47 +08:00
|
|
|
list_add(&pol->walk.all, &net->xfrm.policy_all);
|
|
|
|
net->xfrm.policy_count[dir]++;
|
2005-04-17 06:20:36 +08:00
|
|
|
xfrm_pol_hold(pol);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct xfrm_policy *__xfrm_policy_unlink(struct xfrm_policy *pol,
|
|
|
|
int dir)
|
|
|
|
{
|
2008-11-26 09:29:47 +08:00
|
|
|
struct net *net = xp_net(pol);
|
|
|
|
|
2014-11-13 17:09:49 +08:00
|
|
|
if (list_empty(&pol->walk.all))
|
2006-08-24 19:45:07 +08:00
|
|
|
return NULL;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2014-11-13 17:09:49 +08:00
|
|
|
/* Socket policies are not hashed. */
|
|
|
|
if (!hlist_unhashed(&pol->bydst)) {
|
2016-08-11 21:17:52 +08:00
|
|
|
hlist_del_rcu(&pol->bydst);
|
2014-11-13 17:09:49 +08:00
|
|
|
hlist_del(&pol->byidx);
|
|
|
|
}
|
|
|
|
|
|
|
|
list_del_init(&pol->walk.all);
|
2008-11-26 09:29:47 +08:00
|
|
|
net->xfrm.policy_count[dir]--;
|
2006-08-24 19:45:07 +08:00
|
|
|
|
|
|
|
return pol;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2014-11-13 17:09:49 +08:00
|
|
|
static void xfrm_sk_policy_link(struct xfrm_policy *pol, int dir)
|
|
|
|
{
|
|
|
|
__xfrm_policy_link(pol, XFRM_POLICY_MAX + dir);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void xfrm_sk_policy_unlink(struct xfrm_policy *pol, int dir)
|
|
|
|
{
|
|
|
|
__xfrm_policy_unlink(pol, XFRM_POLICY_MAX + dir);
|
|
|
|
}
|
|
|
|
|
2005-06-19 13:43:22 +08:00
|
|
|
int xfrm_policy_delete(struct xfrm_policy *pol, int dir)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2013-11-07 17:47:50 +08:00
|
|
|
struct net *net = xp_net(pol);
|
|
|
|
|
2016-08-11 21:17:59 +08:00
|
|
|
spin_lock_bh(&net->xfrm.xfrm_policy_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
pol = __xfrm_policy_unlink(pol, dir);
|
2016-08-11 21:17:59 +08:00
|
|
|
spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (pol) {
|
|
|
|
xfrm_policy_kill(pol);
|
2005-06-19 13:43:22 +08:00
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2005-06-19 13:43:22 +08:00
|
|
|
return -ENOENT;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2006-03-21 11:18:52 +08:00
|
|
|
EXPORT_SYMBOL(xfrm_policy_delete);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol)
|
|
|
|
{
|
2017-11-20 18:26:02 +08:00
|
|
|
struct net *net = sock_net(sk);
|
2005-04-17 06:20:36 +08:00
|
|
|
struct xfrm_policy *old_pol;
|
|
|
|
|
2006-08-24 13:43:30 +08:00
|
|
|
#ifdef CONFIG_XFRM_SUB_POLICY
|
|
|
|
if (pol && pol->type != XFRM_POLICY_TYPE_MAIN)
|
|
|
|
return -EINVAL;
|
|
|
|
#endif
|
|
|
|
|
2016-08-11 21:17:59 +08:00
|
|
|
spin_lock_bh(&net->xfrm.xfrm_policy_lock);
|
2015-12-08 23:22:02 +08:00
|
|
|
old_pol = rcu_dereference_protected(sk->sk_policy[dir],
|
|
|
|
lockdep_is_held(&net->xfrm.xfrm_policy_lock));
|
2005-04-17 06:20:36 +08:00
|
|
|
if (pol) {
|
2007-03-05 08:12:44 +08:00
|
|
|
pol->curlft.add_time = get_seconds();
|
2013-11-07 17:47:48 +08:00
|
|
|
pol->index = xfrm_gen_index(net, XFRM_POLICY_MAX+dir, 0);
|
2014-11-13 17:09:49 +08:00
|
|
|
xfrm_sk_policy_link(pol, dir);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2015-12-08 23:22:02 +08:00
|
|
|
rcu_assign_pointer(sk->sk_policy[dir], pol);
|
2013-02-05 19:52:55 +08:00
|
|
|
if (old_pol) {
|
|
|
|
if (pol)
|
|
|
|
xfrm_policy_requeue(old_pol, pol);
|
|
|
|
|
2010-03-31 08:17:05 +08:00
|
|
|
/* Unlinking succeeds always. This is the only function
|
|
|
|
* allowed to delete or replace socket policy.
|
|
|
|
*/
|
2014-11-13 17:09:49 +08:00
|
|
|
xfrm_sk_policy_unlink(old_pol, dir);
|
2013-02-05 19:52:55 +08:00
|
|
|
}
|
2016-08-11 21:17:59 +08:00
|
|
|
spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (old_pol) {
|
|
|
|
xfrm_policy_kill(old_pol);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-02-24 14:25:41 +08:00
|
|
|
static struct xfrm_policy *clone_policy(const struct xfrm_policy *old, int dir)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-11-26 09:21:45 +08:00
|
|
|
struct xfrm_policy *newp = xfrm_policy_alloc(xp_net(old), GFP_ATOMIC);
|
2013-11-07 17:47:50 +08:00
|
|
|
struct net *net = xp_net(old);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (newp) {
|
|
|
|
newp->selector = old->selector;
|
2008-04-13 10:07:52 +08:00
|
|
|
if (security_xfrm_policy_clone(old->security,
|
|
|
|
&newp->security)) {
|
[LSM-IPSec]: Security association restriction.
This patch series implements per packet access control via the
extension of the Linux Security Modules (LSM) interface by hooks in
the XFRM and pfkey subsystems that leverage IPSec security
associations to label packets. Extensions to the SELinux LSM are
included that leverage the patch for this purpose.
This patch implements the changes necessary to the XFRM subsystem,
pfkey interface, ipv4/ipv6, and xfrm_user interface to restrict a
socket to use only authorized security associations (or no security
association) to send/receive network packets.
Patch purpose:
The patch is designed to enable access control per packets based on
the strongly authenticated IPSec security association. Such access
controls augment the existing ones based on network interface and IP
address. The former are very coarse-grained, and the latter can be
spoofed. By using IPSec, the system can control access to remote
hosts based on cryptographic keys generated using the IPSec mechanism.
This enables access control on a per-machine basis or per-application
if the remote machine is running the same mechanism and trusted to
enforce the access control policy.
Patch design approach:
The overall approach is that policy (xfrm_policy) entries set by
user-level programs (e.g., setkey for ipsec-tools) are extended with a
security context that is used at policy selection time in the XFRM
subsystem to restrict the sockets that can send/receive packets via
security associations (xfrm_states) that are built from those
policies.
A presentation available at
www.selinux-symposium.org/2005/presentations/session2/2-3-jaeger.pdf
from the SELinux symposium describes the overall approach.
Patch implementation details:
On output, the policy retrieved (via xfrm_policy_lookup or
xfrm_sk_policy_lookup) must be authorized for the security context of
the socket and the same security context is required for resultant
security association (retrieved or negotiated via racoon in
ipsec-tools). This is enforced in xfrm_state_find.
On input, the policy retrieved must also be authorized for the socket
(at __xfrm_policy_check), and the security context of the policy must
also match the security association being used.
The patch has virtually no impact on packets that do not use IPSec.
The existing Netfilter (outgoing) and LSM rcv_skb hooks are used as
before.
Also, if IPSec is used without security contexts, the impact is
minimal. The LSM must allow such policies to be selected for the
combination of socket and remote machine, but subsequent IPSec
processing proceeds as in the original case.
Testing:
The pfkey interface is tested using the ipsec-tools. ipsec-tools have
been modified (a separate ipsec-tools patch is available for version
0.5) that supports assignment of xfrm_policy entries and security
associations with security contexts via setkey and the negotiation
using the security contexts via racoon.
The xfrm_user interface is tested via ad hoc programs that set
security contexts. These programs are also available from me, and
contain programs for setting, getting, and deleting policy for testing
this interface. Testing of sa functions was done by tracing kernel
behavior.
Signed-off-by: Trent Jaeger <tjaeger@cse.psu.edu>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2005-12-14 15:12:27 +08:00
|
|
|
kfree(newp);
|
|
|
|
return NULL; /* ENOMEM */
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
newp->lft = old->lft;
|
|
|
|
newp->curlft = old->curlft;
|
2010-02-24 07:09:53 +08:00
|
|
|
newp->mark = old->mark;
|
2005-04-17 06:20:36 +08:00
|
|
|
newp->action = old->action;
|
|
|
|
newp->flags = old->flags;
|
|
|
|
newp->xfrm_nr = old->xfrm_nr;
|
|
|
|
newp->index = old->index;
|
2006-08-24 13:43:30 +08:00
|
|
|
newp->type = old->type;
|
2017-11-10 11:14:06 +08:00
|
|
|
newp->family = old->family;
|
2005-04-17 06:20:36 +08:00
|
|
|
memcpy(newp->xfrm_vec, old->xfrm_vec,
|
|
|
|
newp->xfrm_nr*sizeof(struct xfrm_tmpl));
|
2016-08-11 21:17:59 +08:00
|
|
|
spin_lock_bh(&net->xfrm.xfrm_policy_lock);
|
2014-11-13 17:09:49 +08:00
|
|
|
xfrm_sk_policy_link(newp, dir);
|
2016-08-11 21:17:59 +08:00
|
|
|
spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
xfrm_pol_put(newp);
|
|
|
|
}
|
|
|
|
return newp;
|
|
|
|
}
|
|
|
|
|
2015-12-08 23:22:02 +08:00
|
|
|
int __xfrm_sk_clone_policy(struct sock *sk, const struct sock *osk)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2015-12-08 23:22:02 +08:00
|
|
|
const struct xfrm_policy *p;
|
|
|
|
struct xfrm_policy *np;
|
|
|
|
int i, ret = 0;
|
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
|
|
p = rcu_dereference(osk->sk_policy[i]);
|
|
|
|
if (p) {
|
|
|
|
np = clone_policy(p, i);
|
|
|
|
if (unlikely(!np)) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
rcu_assign_pointer(sk->sk_policy[i], np);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rcu_read_unlock();
|
|
|
|
return ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2006-09-20 03:57:34 +08:00
|
|
|
static int
|
2015-08-11 06:58:11 +08:00
|
|
|
xfrm_get_saddr(struct net *net, int oif, xfrm_address_t *local,
|
net: xfrm: support setting an output mark.
On systems that use mark-based routing it may be necessary for
routing lookups to use marks in order for packets to be routed
correctly. An example of such a system is Android, which uses
socket marks to route packets via different networks.
Currently, routing lookups in tunnel mode always use a mark of
zero, making routing incorrect on such systems.
This patch adds a new output_mark element to the xfrm state and
a corresponding XFRMA_OUTPUT_MARK netlink attribute. The output
mark differs from the existing xfrm mark in two ways:
1. The xfrm mark is used to match xfrm policies and states, while
the xfrm output mark is used to set the mark (and influence
the routing) of the packets emitted by those states.
2. The existing mark is constrained to be a subset of the bits of
the originating socket or transformed packet, but the output
mark is arbitrary and depends only on the state.
The use of a separate mark provides additional flexibility. For
example:
- A packet subject to two transforms (e.g., transport mode inside
tunnel mode) can have two different output marks applied to it,
one for the transport mode SA and one for the tunnel mode SA.
- On a system where socket marks determine routing, the packets
emitted by an IPsec tunnel can be routed based on a mark that
is determined by the tunnel, not by the marks of the
unencrypted packets.
- Support for setting the output marks can be introduced without
breaking any existing setups that employ both mark-based
routing and xfrm tunnel mode. Simply changing the code to use
the xfrm mark for routing output packets could xfrm mark could
change behaviour in a way that breaks these setups.
If the output mark is unspecified or set to zero, the mark is not
set or changed.
Tested: make allyesconfig; make -j64
Tested: https://android-review.googlesource.com/452776
Signed-off-by: Lorenzo Colitti <lorenzo@google.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
2017-08-11 01:11:33 +08:00
|
|
|
xfrm_address_t *remote, unsigned short family, u32 mark)
|
2006-09-20 03:57:34 +08:00
|
|
|
{
|
|
|
|
int err;
|
2017-02-07 22:00:19 +08:00
|
|
|
const struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
|
2006-09-20 03:57:34 +08:00
|
|
|
|
|
|
|
if (unlikely(afinfo == NULL))
|
|
|
|
return -EINVAL;
|
net: xfrm: support setting an output mark.
On systems that use mark-based routing it may be necessary for
routing lookups to use marks in order for packets to be routed
correctly. An example of such a system is Android, which uses
socket marks to route packets via different networks.
Currently, routing lookups in tunnel mode always use a mark of
zero, making routing incorrect on such systems.
This patch adds a new output_mark element to the xfrm state and
a corresponding XFRMA_OUTPUT_MARK netlink attribute. The output
mark differs from the existing xfrm mark in two ways:
1. The xfrm mark is used to match xfrm policies and states, while
the xfrm output mark is used to set the mark (and influence
the routing) of the packets emitted by those states.
2. The existing mark is constrained to be a subset of the bits of
the originating socket or transformed packet, but the output
mark is arbitrary and depends only on the state.
The use of a separate mark provides additional flexibility. For
example:
- A packet subject to two transforms (e.g., transport mode inside
tunnel mode) can have two different output marks applied to it,
one for the transport mode SA and one for the tunnel mode SA.
- On a system where socket marks determine routing, the packets
emitted by an IPsec tunnel can be routed based on a mark that
is determined by the tunnel, not by the marks of the
unencrypted packets.
- Support for setting the output marks can be introduced without
breaking any existing setups that employ both mark-based
routing and xfrm tunnel mode. Simply changing the code to use
the xfrm mark for routing output packets could xfrm mark could
change behaviour in a way that breaks these setups.
If the output mark is unspecified or set to zero, the mark is not
set or changed.
Tested: make allyesconfig; make -j64
Tested: https://android-review.googlesource.com/452776
Signed-off-by: Lorenzo Colitti <lorenzo@google.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
2017-08-11 01:11:33 +08:00
|
|
|
err = afinfo->get_saddr(net, oif, local, remote, mark);
|
2017-02-07 22:00:18 +08:00
|
|
|
rcu_read_unlock();
|
2006-09-20 03:57:34 +08:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Resolve list of templates for the flow, given policy. */
|
|
|
|
|
|
|
|
static int
|
2011-02-23 10:35:39 +08:00
|
|
|
xfrm_tmpl_resolve_one(struct xfrm_policy *policy, const struct flowi *fl,
|
|
|
|
struct xfrm_state **xfrm, unsigned short family)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-11-26 09:56:49 +08:00
|
|
|
struct net *net = xp_net(policy);
|
2005-04-17 06:20:36 +08:00
|
|
|
int nx;
|
|
|
|
int i, error;
|
2017-11-15 13:40:57 +08:00
|
|
|
xfrm_address_t *daddr = xfrm_flowi_daddr(fl, family);
|
|
|
|
xfrm_address_t *saddr = xfrm_flowi_saddr(fl, family);
|
2006-09-20 03:57:34 +08:00
|
|
|
xfrm_address_t tmp;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-12-24 09:43:46 +08:00
|
|
|
for (nx = 0, i = 0; i < policy->xfrm_nr; i++) {
|
2005-04-17 06:20:36 +08:00
|
|
|
struct xfrm_state *x;
|
2017-11-15 13:40:57 +08:00
|
|
|
xfrm_address_t *remote = daddr;
|
|
|
|
xfrm_address_t *local = saddr;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct xfrm_tmpl *tmpl = &policy->xfrm_vec[i];
|
|
|
|
|
2017-11-15 13:40:57 +08:00
|
|
|
if (tmpl->mode == XFRM_MODE_TUNNEL ||
|
|
|
|
tmpl->mode == XFRM_MODE_BEET) {
|
|
|
|
remote = &tmpl->id.daddr;
|
|
|
|
local = &tmpl->saddr;
|
|
|
|
if (xfrm_addr_any(local, tmpl->encap_family)) {
|
|
|
|
error = xfrm_get_saddr(net, fl->flowi_oif,
|
|
|
|
&tmp, remote,
|
|
|
|
tmpl->encap_family, 0);
|
|
|
|
if (error)
|
|
|
|
goto fail;
|
|
|
|
local = &tmp;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
x = xfrm_state_find(remote, local, fl, tmpl, policy, &error, family);
|
|
|
|
|
|
|
|
if (x && x->km.state == XFRM_STATE_VALID) {
|
|
|
|
xfrm[nx++] = x;
|
2017-11-15 13:40:57 +08:00
|
|
|
daddr = remote;
|
|
|
|
saddr = local;
|
2005-04-17 06:20:36 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (x) {
|
|
|
|
error = (x->km.state == XFRM_STATE_ERROR ?
|
|
|
|
-EINVAL : -EAGAIN);
|
|
|
|
xfrm_state_put(x);
|
2013-12-24 09:43:49 +08:00
|
|
|
} else if (error == -ESRCH) {
|
2008-10-23 12:27:19 +08:00
|
|
|
error = -EAGAIN;
|
2013-12-24 09:43:49 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (!tmpl->optional)
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
return nx;
|
|
|
|
|
|
|
|
fail:
|
2013-12-24 09:43:46 +08:00
|
|
|
for (nx--; nx >= 0; nx--)
|
2005-04-17 06:20:36 +08:00
|
|
|
xfrm_state_put(xfrm[nx]);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2006-08-24 13:43:30 +08:00
|
|
|
static int
|
2011-02-23 10:35:39 +08:00
|
|
|
xfrm_tmpl_resolve(struct xfrm_policy **pols, int npols, const struct flowi *fl,
|
|
|
|
struct xfrm_state **xfrm, unsigned short family)
|
2006-08-24 13:43:30 +08:00
|
|
|
{
|
2006-08-24 13:48:31 +08:00
|
|
|
struct xfrm_state *tp[XFRM_MAX_DEPTH];
|
|
|
|
struct xfrm_state **tpp = (npols > 1) ? tp : xfrm;
|
2006-08-24 13:43:30 +08:00
|
|
|
int cnx = 0;
|
|
|
|
int error;
|
|
|
|
int ret;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < npols; i++) {
|
|
|
|
if (cnx + pols[i]->xfrm_nr >= XFRM_MAX_DEPTH) {
|
|
|
|
error = -ENOBUFS;
|
|
|
|
goto fail;
|
|
|
|
}
|
2006-08-24 13:48:31 +08:00
|
|
|
|
|
|
|
ret = xfrm_tmpl_resolve_one(pols[i], fl, &tpp[cnx], family);
|
2006-08-24 13:43:30 +08:00
|
|
|
if (ret < 0) {
|
|
|
|
error = ret;
|
|
|
|
goto fail;
|
|
|
|
} else
|
|
|
|
cnx += ret;
|
|
|
|
}
|
|
|
|
|
2006-08-24 13:48:31 +08:00
|
|
|
/* found states are sorted for outbound processing */
|
|
|
|
if (npols > 1)
|
|
|
|
xfrm_state_sort(xfrm, tpp, cnx, family);
|
|
|
|
|
2006-08-24 13:43:30 +08:00
|
|
|
return cnx;
|
|
|
|
|
|
|
|
fail:
|
2013-12-24 09:43:46 +08:00
|
|
|
for (cnx--; cnx >= 0; cnx--)
|
2006-08-24 13:48:31 +08:00
|
|
|
xfrm_state_put(tpp[cnx]);
|
2006-08-24 13:43:30 +08:00
|
|
|
return error;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2017-02-07 22:00:14 +08:00
|
|
|
static int xfrm_get_tos(const struct flowi *fl, int family)
|
2007-12-12 01:32:34 +08:00
|
|
|
{
|
2017-02-07 22:00:19 +08:00
|
|
|
const struct xfrm_policy_afinfo *afinfo;
|
2018-02-17 15:16:22 +08:00
|
|
|
int tos;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-02-07 22:00:14 +08:00
|
|
|
afinfo = xfrm_policy_get_afinfo(family);
|
2018-02-17 15:16:22 +08:00
|
|
|
if (!afinfo)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
tos = afinfo->get_tos(fl);
|
2007-12-12 01:32:34 +08:00
|
|
|
|
2017-02-07 22:00:18 +08:00
|
|
|
rcu_read_unlock();
|
2007-12-12 01:32:34 +08:00
|
|
|
|
|
|
|
return tos;
|
|
|
|
}
|
|
|
|
|
2010-01-25 14:47:53 +08:00
|
|
|
static inline struct xfrm_dst *xfrm_alloc_dst(struct net *net, int family)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2017-02-07 22:00:19 +08:00
|
|
|
const struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
|
2010-01-25 14:47:53 +08:00
|
|
|
struct dst_ops *dst_ops;
|
2007-12-12 01:32:34 +08:00
|
|
|
struct xfrm_dst *xdst;
|
|
|
|
|
|
|
|
if (!afinfo)
|
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
|
2010-01-25 14:47:53 +08:00
|
|
|
switch (family) {
|
|
|
|
case AF_INET:
|
|
|
|
dst_ops = &net->xfrm.xfrm4_dst_ops;
|
|
|
|
break;
|
2011-12-10 17:48:31 +08:00
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
2010-01-25 14:47:53 +08:00
|
|
|
case AF_INET6:
|
|
|
|
dst_ops = &net->xfrm.xfrm6_dst_ops;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
default:
|
|
|
|
BUG();
|
|
|
|
}
|
2017-06-18 01:42:41 +08:00
|
|
|
xdst = dst_alloc(dst_ops, NULL, 1, DST_OBSOLETE_NONE, 0);
|
2007-12-12 01:32:34 +08:00
|
|
|
|
2011-09-26 15:04:36 +08:00
|
|
|
if (likely(xdst)) {
|
2012-07-06 07:39:34 +08:00
|
|
|
struct dst_entry *dst = &xdst->u.dst;
|
|
|
|
|
|
|
|
memset(dst + 1, 0, sizeof(*xdst) - sizeof(*dst));
|
2011-09-26 15:04:36 +08:00
|
|
|
} else
|
2011-02-11 15:08:33 +08:00
|
|
|
xdst = ERR_PTR(-ENOBUFS);
|
2010-04-07 08:30:05 +08:00
|
|
|
|
2017-02-07 22:00:18 +08:00
|
|
|
rcu_read_unlock();
|
2011-09-26 15:04:36 +08:00
|
|
|
|
2007-12-12 01:32:34 +08:00
|
|
|
return xdst;
|
|
|
|
}
|
|
|
|
|
2007-12-21 12:41:12 +08:00
|
|
|
static inline int xfrm_init_path(struct xfrm_dst *path, struct dst_entry *dst,
|
|
|
|
int nfheader_len)
|
|
|
|
{
|
2017-02-07 22:00:19 +08:00
|
|
|
const struct xfrm_policy_afinfo *afinfo =
|
2007-12-21 12:41:12 +08:00
|
|
|
xfrm_policy_get_afinfo(dst->ops->family);
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (!afinfo)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
err = afinfo->init_path(path, dst, nfheader_len);
|
|
|
|
|
2017-02-07 22:00:18 +08:00
|
|
|
rcu_read_unlock();
|
2007-12-21 12:41:12 +08:00
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2010-03-02 10:51:56 +08:00
|
|
|
static inline int xfrm_fill_dst(struct xfrm_dst *xdst, struct net_device *dev,
|
2011-02-23 09:48:57 +08:00
|
|
|
const struct flowi *fl)
|
2007-12-12 01:32:34 +08:00
|
|
|
{
|
2017-02-07 22:00:19 +08:00
|
|
|
const struct xfrm_policy_afinfo *afinfo =
|
2007-12-12 01:32:34 +08:00
|
|
|
xfrm_policy_get_afinfo(xdst->u.dst.ops->family);
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (!afinfo)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
2007-12-12 01:32:34 +08:00
|
|
|
|
2010-03-02 10:51:56 +08:00
|
|
|
err = afinfo->fill_dst(xdst, dev, fl);
|
2007-12-12 01:32:34 +08:00
|
|
|
|
2017-02-07 22:00:18 +08:00
|
|
|
rcu_read_unlock();
|
2007-12-12 01:32:34 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2010-04-07 08:30:05 +08:00
|
|
|
|
2007-12-12 01:32:34 +08:00
|
|
|
/* Allocate chain of dst_entry's, attach known xfrm's, calculate
|
|
|
|
* all the metrics... Shortly, bundle a bundle.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy,
|
2017-11-29 04:41:01 +08:00
|
|
|
struct xfrm_state **xfrm,
|
|
|
|
struct xfrm_dst **bundle,
|
|
|
|
int nx,
|
2011-02-23 10:36:50 +08:00
|
|
|
const struct flowi *fl,
|
2007-12-12 01:32:34 +08:00
|
|
|
struct dst_entry *dst)
|
|
|
|
{
|
2010-01-25 14:47:53 +08:00
|
|
|
struct net *net = xp_net(policy);
|
2007-12-12 01:32:34 +08:00
|
|
|
unsigned long now = jiffies;
|
|
|
|
struct net_device *dev;
|
2011-05-10 03:36:38 +08:00
|
|
|
struct xfrm_mode *inner_mode;
|
2017-11-29 04:40:28 +08:00
|
|
|
struct xfrm_dst *xdst_prev = NULL;
|
|
|
|
struct xfrm_dst *xdst0 = NULL;
|
2007-12-12 01:32:34 +08:00
|
|
|
int i = 0;
|
|
|
|
int err;
|
|
|
|
int header_len = 0;
|
2007-12-21 12:41:12 +08:00
|
|
|
int nfheader_len = 0;
|
2007-12-12 01:32:34 +08:00
|
|
|
int trailer_len = 0;
|
|
|
|
int tos;
|
|
|
|
int family = policy->selector.family;
|
2008-02-22 13:48:22 +08:00
|
|
|
xfrm_address_t saddr, daddr;
|
|
|
|
|
|
|
|
xfrm_flowi_addr_get(fl, &saddr, &daddr, family);
|
2007-12-12 01:32:34 +08:00
|
|
|
|
|
|
|
tos = xfrm_get_tos(fl, family);
|
|
|
|
|
|
|
|
dst_hold(dst);
|
|
|
|
|
|
|
|
for (; i < nx; i++) {
|
2010-01-25 14:47:53 +08:00
|
|
|
struct xfrm_dst *xdst = xfrm_alloc_dst(net, family);
|
2007-12-12 01:32:34 +08:00
|
|
|
struct dst_entry *dst1 = &xdst->u.dst;
|
|
|
|
|
|
|
|
err = PTR_ERR(xdst);
|
|
|
|
if (IS_ERR(xdst)) {
|
|
|
|
dst_release(dst);
|
|
|
|
goto put_states;
|
|
|
|
}
|
|
|
|
|
2017-11-29 04:41:01 +08:00
|
|
|
bundle[i] = xdst;
|
2017-11-29 04:40:28 +08:00
|
|
|
if (!xdst_prev)
|
|
|
|
xdst0 = xdst;
|
2017-10-11 11:59:38 +08:00
|
|
|
else
|
|
|
|
/* Ref count is taken during xfrm_alloc_dst()
|
|
|
|
* No need to do dst_clone() on dst1
|
|
|
|
*/
|
2017-11-29 04:40:28 +08:00
|
|
|
xfrm_dst_set_child(xdst_prev, &xdst->u.dst);
|
2017-10-11 11:59:38 +08:00
|
|
|
|
2011-05-10 03:36:38 +08:00
|
|
|
if (xfrm[i]->sel.family == AF_UNSPEC) {
|
|
|
|
inner_mode = xfrm_ip2inner_mode(xfrm[i],
|
|
|
|
xfrm_af2proto(family));
|
|
|
|
if (!inner_mode) {
|
|
|
|
err = -EAFNOSUPPORT;
|
|
|
|
dst_release(dst);
|
|
|
|
goto put_states;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
inner_mode = xfrm[i]->inner_mode;
|
|
|
|
|
2007-12-12 01:32:34 +08:00
|
|
|
xdst->route = dst;
|
2010-12-09 13:16:57 +08:00
|
|
|
dst_copy_metrics(dst1, dst);
|
2007-12-12 01:32:34 +08:00
|
|
|
|
|
|
|
if (xfrm[i]->props.mode != XFRM_MODE_TRANSPORT) {
|
|
|
|
family = xfrm[i]->props.family;
|
2015-08-11 06:58:11 +08:00
|
|
|
dst = xfrm_dst_lookup(xfrm[i], tos, fl->flowi_oif,
|
net: xfrm: support setting an output mark.
On systems that use mark-based routing it may be necessary for
routing lookups to use marks in order for packets to be routed
correctly. An example of such a system is Android, which uses
socket marks to route packets via different networks.
Currently, routing lookups in tunnel mode always use a mark of
zero, making routing incorrect on such systems.
This patch adds a new output_mark element to the xfrm state and
a corresponding XFRMA_OUTPUT_MARK netlink attribute. The output
mark differs from the existing xfrm mark in two ways:
1. The xfrm mark is used to match xfrm policies and states, while
the xfrm output mark is used to set the mark (and influence
the routing) of the packets emitted by those states.
2. The existing mark is constrained to be a subset of the bits of
the originating socket or transformed packet, but the output
mark is arbitrary and depends only on the state.
The use of a separate mark provides additional flexibility. For
example:
- A packet subject to two transforms (e.g., transport mode inside
tunnel mode) can have two different output marks applied to it,
one for the transport mode SA and one for the tunnel mode SA.
- On a system where socket marks determine routing, the packets
emitted by an IPsec tunnel can be routed based on a mark that
is determined by the tunnel, not by the marks of the
unencrypted packets.
- Support for setting the output marks can be introduced without
breaking any existing setups that employ both mark-based
routing and xfrm tunnel mode. Simply changing the code to use
the xfrm mark for routing output packets could xfrm mark could
change behaviour in a way that breaks these setups.
If the output mark is unspecified or set to zero, the mark is not
set or changed.
Tested: make allyesconfig; make -j64
Tested: https://android-review.googlesource.com/452776
Signed-off-by: Lorenzo Colitti <lorenzo@google.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
2017-08-11 01:11:33 +08:00
|
|
|
&saddr, &daddr, family,
|
|
|
|
xfrm[i]->props.output_mark);
|
2007-12-12 01:32:34 +08:00
|
|
|
err = PTR_ERR(dst);
|
|
|
|
if (IS_ERR(dst))
|
|
|
|
goto put_states;
|
|
|
|
} else
|
|
|
|
dst_hold(dst);
|
|
|
|
|
|
|
|
dst1->xfrm = xfrm[i];
|
2010-04-07 08:30:05 +08:00
|
|
|
xdst->xfrm_genid = xfrm[i]->genid;
|
2007-12-12 01:32:34 +08:00
|
|
|
|
2012-07-20 03:31:33 +08:00
|
|
|
dst1->obsolete = DST_OBSOLETE_FORCE_CHK;
|
2007-12-12 01:32:34 +08:00
|
|
|
dst1->flags |= DST_HOST;
|
|
|
|
dst1->lastuse = now;
|
|
|
|
|
|
|
|
dst1->input = dst_discard;
|
2011-05-10 03:36:38 +08:00
|
|
|
dst1->output = inner_mode->afinfo->output;
|
2007-12-12 01:32:34 +08:00
|
|
|
|
2017-11-29 04:40:28 +08:00
|
|
|
xdst_prev = xdst;
|
2007-12-12 01:32:34 +08:00
|
|
|
|
|
|
|
header_len += xfrm[i]->props.header_len;
|
2007-12-21 12:41:12 +08:00
|
|
|
if (xfrm[i]->type->flags & XFRM_TYPE_NON_FRAGMENT)
|
|
|
|
nfheader_len += xfrm[i]->props.header_len;
|
2007-12-12 01:32:34 +08:00
|
|
|
trailer_len += xfrm[i]->props.trailer_len;
|
|
|
|
}
|
|
|
|
|
2017-11-29 04:40:28 +08:00
|
|
|
xfrm_dst_set_child(xdst_prev, dst);
|
2017-11-29 04:40:46 +08:00
|
|
|
xdst0->path = dst;
|
2007-12-12 01:32:34 +08:00
|
|
|
|
|
|
|
err = -ENODEV;
|
|
|
|
dev = dst->dev;
|
|
|
|
if (!dev)
|
|
|
|
goto free_dst;
|
|
|
|
|
2017-11-29 04:40:28 +08:00
|
|
|
xfrm_init_path(xdst0, dst, nfheader_len);
|
2017-11-29 04:41:01 +08:00
|
|
|
xfrm_init_pmtu(bundle, nx);
|
2007-12-12 01:32:34 +08:00
|
|
|
|
2017-11-29 04:40:28 +08:00
|
|
|
for (xdst_prev = xdst0; xdst_prev != (struct xfrm_dst *)dst;
|
|
|
|
xdst_prev = (struct xfrm_dst *) xfrm_dst_child(&xdst_prev->u.dst)) {
|
|
|
|
err = xfrm_fill_dst(xdst_prev, dev, fl);
|
2007-12-12 01:32:34 +08:00
|
|
|
if (err)
|
|
|
|
goto free_dst;
|
|
|
|
|
2017-11-29 04:40:28 +08:00
|
|
|
xdst_prev->u.dst.header_len = header_len;
|
|
|
|
xdst_prev->u.dst.trailer_len = trailer_len;
|
|
|
|
header_len -= xdst_prev->u.dst.xfrm->props.header_len;
|
|
|
|
trailer_len -= xdst_prev->u.dst.xfrm->props.trailer_len;
|
2007-12-12 01:32:34 +08:00
|
|
|
}
|
|
|
|
|
2017-11-29 04:40:28 +08:00
|
|
|
return &xdst0->u.dst;
|
2007-12-12 01:32:34 +08:00
|
|
|
|
|
|
|
put_states:
|
|
|
|
for (; i < nx; i++)
|
|
|
|
xfrm_state_put(xfrm[i]);
|
|
|
|
free_dst:
|
2017-11-29 04:40:28 +08:00
|
|
|
if (xdst0)
|
|
|
|
dst_release_immediate(&xdst0->u.dst);
|
2018-05-31 15:45:18 +08:00
|
|
|
|
|
|
|
return ERR_PTR(err);
|
2007-12-12 01:32:34 +08:00
|
|
|
}
|
|
|
|
|
2011-02-23 10:33:42 +08:00
|
|
|
static int xfrm_expand_policies(const struct flowi *fl, u16 family,
|
2010-04-07 08:30:05 +08:00
|
|
|
struct xfrm_policy **pols,
|
|
|
|
int *num_pols, int *num_xfrms)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (*num_pols == 0 || !pols[0]) {
|
|
|
|
*num_pols = 0;
|
|
|
|
*num_xfrms = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (IS_ERR(pols[0]))
|
|
|
|
return PTR_ERR(pols[0]);
|
|
|
|
|
|
|
|
*num_xfrms = pols[0]->xfrm_nr;
|
|
|
|
|
|
|
|
#ifdef CONFIG_XFRM_SUB_POLICY
|
|
|
|
if (pols[0] && pols[0]->action == XFRM_POLICY_ALLOW &&
|
|
|
|
pols[0]->type != XFRM_POLICY_TYPE_MAIN) {
|
|
|
|
pols[1] = xfrm_policy_lookup_bytype(xp_net(pols[0]),
|
|
|
|
XFRM_POLICY_TYPE_MAIN,
|
|
|
|
fl, family,
|
|
|
|
XFRM_POLICY_OUT);
|
|
|
|
if (pols[1]) {
|
|
|
|
if (IS_ERR(pols[1])) {
|
|
|
|
xfrm_pols_put(pols, *num_pols);
|
|
|
|
return PTR_ERR(pols[1]);
|
|
|
|
}
|
2013-12-24 09:43:48 +08:00
|
|
|
(*num_pols)++;
|
2010-04-07 08:30:05 +08:00
|
|
|
(*num_xfrms) += pols[1]->xfrm_nr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
for (i = 0; i < *num_pols; i++) {
|
|
|
|
if (pols[i]->action != XFRM_POLICY_ALLOW) {
|
|
|
|
*num_xfrms = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2017-07-17 19:57:27 +08:00
|
|
|
static void xfrm_last_dst_update(struct xfrm_dst *xdst, struct xfrm_dst *old)
|
|
|
|
{
|
|
|
|
this_cpu_write(xfrm_last_dst, xdst);
|
|
|
|
if (old)
|
|
|
|
dst_release(&old->u.dst);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __xfrm_pcpu_work_fn(void)
|
|
|
|
{
|
|
|
|
struct xfrm_dst *old;
|
|
|
|
|
|
|
|
old = this_cpu_read(xfrm_last_dst);
|
|
|
|
if (old && !xfrm_bundle_ok(old))
|
|
|
|
xfrm_last_dst_update(NULL, old);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void xfrm_pcpu_work_fn(struct work_struct *work)
|
|
|
|
{
|
|
|
|
local_bh_disable();
|
|
|
|
rcu_read_lock();
|
|
|
|
__xfrm_pcpu_work_fn();
|
|
|
|
rcu_read_unlock();
|
|
|
|
local_bh_enable();
|
|
|
|
}
|
|
|
|
|
|
|
|
void xfrm_policy_cache_flush(void)
|
|
|
|
{
|
|
|
|
struct xfrm_dst *old;
|
2018-03-06 05:49:59 +08:00
|
|
|
bool found = false;
|
2017-07-17 19:57:27 +08:00
|
|
|
int cpu;
|
|
|
|
|
2018-01-06 08:13:08 +08:00
|
|
|
might_sleep();
|
|
|
|
|
2017-07-17 19:57:27 +08:00
|
|
|
local_bh_disable();
|
|
|
|
rcu_read_lock();
|
|
|
|
for_each_possible_cpu(cpu) {
|
|
|
|
old = per_cpu(xfrm_last_dst, cpu);
|
|
|
|
if (old && !xfrm_bundle_ok(old)) {
|
|
|
|
if (smp_processor_id() == cpu) {
|
|
|
|
__xfrm_pcpu_work_fn();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rcu_read_unlock();
|
|
|
|
local_bh_enable();
|
|
|
|
|
|
|
|
if (!found)
|
|
|
|
return;
|
|
|
|
|
|
|
|
get_online_cpus();
|
|
|
|
|
|
|
|
for_each_possible_cpu(cpu) {
|
|
|
|
bool bundle_release;
|
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
old = per_cpu(xfrm_last_dst, cpu);
|
|
|
|
bundle_release = old && !xfrm_bundle_ok(old);
|
|
|
|
rcu_read_unlock();
|
|
|
|
|
|
|
|
if (!bundle_release)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (cpu_online(cpu)) {
|
|
|
|
schedule_work_on(cpu, &xfrm_pcpu_work[cpu]);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
old = per_cpu(xfrm_last_dst, cpu);
|
|
|
|
if (old && !xfrm_bundle_ok(old)) {
|
|
|
|
per_cpu(xfrm_last_dst, cpu) = NULL;
|
|
|
|
dst_release(&old->u.dst);
|
|
|
|
}
|
|
|
|
rcu_read_unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
put_online_cpus();
|
|
|
|
}
|
|
|
|
|
2017-11-02 23:46:01 +08:00
|
|
|
static bool xfrm_xdst_can_reuse(struct xfrm_dst *xdst,
|
|
|
|
struct xfrm_state * const xfrm[],
|
|
|
|
int num)
|
2017-07-17 19:57:27 +08:00
|
|
|
{
|
2017-11-02 23:46:01 +08:00
|
|
|
const struct dst_entry *dst = &xdst->u.dst;
|
|
|
|
int i;
|
2017-07-17 19:57:27 +08:00
|
|
|
|
2017-11-02 23:46:01 +08:00
|
|
|
if (xdst->num_xfrms != num)
|
|
|
|
return false;
|
2017-07-17 19:57:27 +08:00
|
|
|
|
2017-11-02 23:46:01 +08:00
|
|
|
for (i = 0; i < num; i++) {
|
|
|
|
if (!dst || dst->xfrm != xfrm[i])
|
|
|
|
return false;
|
2017-11-29 04:40:22 +08:00
|
|
|
dst = xfrm_dst_child(dst);
|
2017-11-02 23:46:01 +08:00
|
|
|
}
|
2017-07-17 19:57:27 +08:00
|
|
|
|
2017-11-02 23:46:01 +08:00
|
|
|
return xfrm_bundle_ok(xdst);
|
2017-07-17 19:57:27 +08:00
|
|
|
}
|
|
|
|
|
2010-04-07 08:30:05 +08:00
|
|
|
static struct xfrm_dst *
|
|
|
|
xfrm_resolve_and_create_bundle(struct xfrm_policy **pols, int num_pols,
|
2011-02-23 10:38:51 +08:00
|
|
|
const struct flowi *fl, u16 family,
|
2010-04-07 08:30:05 +08:00
|
|
|
struct dst_entry *dst_orig)
|
|
|
|
{
|
|
|
|
struct net *net = xp_net(pols[0]);
|
|
|
|
struct xfrm_state *xfrm[XFRM_MAX_DEPTH];
|
2017-11-29 04:41:01 +08:00
|
|
|
struct xfrm_dst *bundle[XFRM_MAX_DEPTH];
|
2017-07-17 19:57:27 +08:00
|
|
|
struct xfrm_dst *xdst, *old;
|
2010-04-07 08:30:05 +08:00
|
|
|
struct dst_entry *dst;
|
|
|
|
int err;
|
|
|
|
|
2017-11-02 23:46:01 +08:00
|
|
|
/* Try to instantiate a bundle */
|
|
|
|
err = xfrm_tmpl_resolve(pols, num_pols, fl, xfrm, family);
|
|
|
|
if (err <= 0) {
|
|
|
|
if (err != 0 && err != -EAGAIN)
|
|
|
|
XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTPOLERROR);
|
|
|
|
return ERR_PTR(err);
|
|
|
|
}
|
|
|
|
|
2017-07-17 19:57:27 +08:00
|
|
|
xdst = this_cpu_read(xfrm_last_dst);
|
|
|
|
if (xdst &&
|
|
|
|
xdst->u.dst.dev == dst_orig->dev &&
|
|
|
|
xdst->num_pols == num_pols &&
|
|
|
|
memcmp(xdst->pols, pols,
|
2017-08-06 16:19:07 +08:00
|
|
|
sizeof(struct xfrm_policy *) * num_pols) == 0 &&
|
2017-11-02 23:46:01 +08:00
|
|
|
xfrm_xdst_can_reuse(xdst, xfrm, err)) {
|
2017-07-17 19:57:27 +08:00
|
|
|
dst_hold(&xdst->u.dst);
|
2017-12-12 01:23:09 +08:00
|
|
|
xfrm_pols_put(pols, num_pols);
|
2017-11-02 23:46:01 +08:00
|
|
|
while (err > 0)
|
|
|
|
xfrm_state_put(xfrm[--err]);
|
2017-07-17 19:57:27 +08:00
|
|
|
return xdst;
|
|
|
|
}
|
|
|
|
|
|
|
|
old = xdst;
|
2010-04-07 08:30:05 +08:00
|
|
|
|
2017-11-29 04:41:01 +08:00
|
|
|
dst = xfrm_bundle_create(pols[0], xfrm, bundle, err, fl, dst_orig);
|
2010-04-07 08:30:05 +08:00
|
|
|
if (IS_ERR(dst)) {
|
|
|
|
XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTBUNDLEGENERROR);
|
|
|
|
return ERR_CAST(dst);
|
|
|
|
}
|
|
|
|
|
|
|
|
xdst = (struct xfrm_dst *)dst;
|
|
|
|
xdst->num_xfrms = err;
|
|
|
|
xdst->num_pols = num_pols;
|
2013-12-24 09:43:47 +08:00
|
|
|
memcpy(xdst->pols, pols, sizeof(struct xfrm_policy *) * num_pols);
|
2010-04-07 08:30:05 +08:00
|
|
|
xdst->policy_genid = atomic_read(&pols[0]->genid);
|
|
|
|
|
2017-07-17 19:57:27 +08:00
|
|
|
atomic_set(&xdst->u.dst.__refcnt, 2);
|
|
|
|
xfrm_last_dst_update(xdst, old);
|
|
|
|
|
2010-04-07 08:30:05 +08:00
|
|
|
return xdst;
|
|
|
|
}
|
|
|
|
|
2017-10-17 08:28:56 +08:00
|
|
|
static void xfrm_policy_queue_process(struct timer_list *t)
|
2013-02-05 19:52:55 +08:00
|
|
|
{
|
|
|
|
struct sk_buff *skb;
|
|
|
|
struct sock *sk;
|
|
|
|
struct dst_entry *dst;
|
2017-10-17 08:28:56 +08:00
|
|
|
struct xfrm_policy *pol = from_timer(pol, t, polq.hold_timer);
|
2015-10-08 05:48:34 +08:00
|
|
|
struct net *net = xp_net(pol);
|
2013-02-05 19:52:55 +08:00
|
|
|
struct xfrm_policy_queue *pq = &pol->polq;
|
|
|
|
struct flowi fl;
|
|
|
|
struct sk_buff_head list;
|
|
|
|
|
|
|
|
spin_lock(&pq->hold_queue.lock);
|
|
|
|
skb = skb_peek(&pq->hold_queue);
|
2013-10-08 16:49:51 +08:00
|
|
|
if (!skb) {
|
|
|
|
spin_unlock(&pq->hold_queue.lock);
|
|
|
|
goto out;
|
|
|
|
}
|
2013-02-05 19:52:55 +08:00
|
|
|
dst = skb_dst(skb);
|
|
|
|
sk = skb->sk;
|
|
|
|
xfrm_decode_session(skb, &fl, dst->ops->family);
|
|
|
|
spin_unlock(&pq->hold_queue.lock);
|
|
|
|
|
2017-11-29 04:40:46 +08:00
|
|
|
dst_hold(xfrm_dst_path(dst));
|
2018-02-01 18:26:12 +08:00
|
|
|
dst = xfrm_lookup(net, xfrm_dst_path(dst), &fl, sk, XFRM_LOOKUP_QUEUE);
|
2013-02-05 19:52:55 +08:00
|
|
|
if (IS_ERR(dst))
|
|
|
|
goto purge_queue;
|
|
|
|
|
|
|
|
if (dst->flags & DST_XFRM_QUEUE) {
|
|
|
|
dst_release(dst);
|
|
|
|
|
|
|
|
if (pq->timeout >= XFRM_QUEUE_TMO_MAX)
|
|
|
|
goto purge_queue;
|
|
|
|
|
|
|
|
pq->timeout = pq->timeout << 1;
|
2013-10-08 16:49:45 +08:00
|
|
|
if (!mod_timer(&pq->hold_timer, jiffies + pq->timeout))
|
|
|
|
xfrm_pol_hold(pol);
|
|
|
|
goto out;
|
2013-02-05 19:52:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
dst_release(dst);
|
|
|
|
|
|
|
|
__skb_queue_head_init(&list);
|
|
|
|
|
|
|
|
spin_lock(&pq->hold_queue.lock);
|
|
|
|
pq->timeout = 0;
|
|
|
|
skb_queue_splice_init(&pq->hold_queue, &list);
|
|
|
|
spin_unlock(&pq->hold_queue.lock);
|
|
|
|
|
|
|
|
while (!skb_queue_empty(&list)) {
|
|
|
|
skb = __skb_dequeue(&list);
|
|
|
|
|
|
|
|
xfrm_decode_session(skb, &fl, skb_dst(skb)->ops->family);
|
2017-11-29 04:40:46 +08:00
|
|
|
dst_hold(xfrm_dst_path(skb_dst(skb)));
|
|
|
|
dst = xfrm_lookup(net, xfrm_dst_path(skb_dst(skb)), &fl, skb->sk, 0);
|
2013-02-05 19:52:55 +08:00
|
|
|
if (IS_ERR(dst)) {
|
|
|
|
kfree_skb(skb);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
nf_reset(skb);
|
|
|
|
skb_dst_drop(skb);
|
|
|
|
skb_dst_set(skb, dst);
|
|
|
|
|
2015-10-08 05:48:35 +08:00
|
|
|
dst_output(net, skb->sk, skb);
|
2013-02-05 19:52:55 +08:00
|
|
|
}
|
|
|
|
|
2013-10-08 16:49:45 +08:00
|
|
|
out:
|
|
|
|
xfrm_pol_put(pol);
|
2013-02-05 19:52:55 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
purge_queue:
|
|
|
|
pq->timeout = 0;
|
2015-04-22 15:51:16 +08:00
|
|
|
skb_queue_purge(&pq->hold_queue);
|
2013-10-08 16:49:45 +08:00
|
|
|
xfrm_pol_put(pol);
|
2013-02-05 19:52:55 +08:00
|
|
|
}
|
|
|
|
|
2015-10-08 05:48:47 +08:00
|
|
|
static int xdst_queue_output(struct net *net, struct sock *sk, struct sk_buff *skb)
|
2013-02-05 19:52:55 +08:00
|
|
|
{
|
|
|
|
unsigned long sched_next;
|
|
|
|
struct dst_entry *dst = skb_dst(skb);
|
|
|
|
struct xfrm_dst *xdst = (struct xfrm_dst *) dst;
|
2013-10-08 16:49:45 +08:00
|
|
|
struct xfrm_policy *pol = xdst->pols[0];
|
|
|
|
struct xfrm_policy_queue *pq = &pol->polq;
|
2013-10-16 19:42:46 +08:00
|
|
|
|
2014-10-31 01:32:34 +08:00
|
|
|
if (unlikely(skb_fclone_busy(sk, skb))) {
|
2013-10-16 19:42:46 +08:00
|
|
|
kfree_skb(skb);
|
|
|
|
return 0;
|
|
|
|
}
|
2013-02-05 19:52:55 +08:00
|
|
|
|
|
|
|
if (pq->hold_queue.qlen > XFRM_MAX_QUEUE_LEN) {
|
|
|
|
kfree_skb(skb);
|
|
|
|
return -EAGAIN;
|
|
|
|
}
|
|
|
|
|
|
|
|
skb_dst_force(skb);
|
|
|
|
|
|
|
|
spin_lock_bh(&pq->hold_queue.lock);
|
|
|
|
|
|
|
|
if (!pq->timeout)
|
|
|
|
pq->timeout = XFRM_QUEUE_TMO_MIN;
|
|
|
|
|
|
|
|
sched_next = jiffies + pq->timeout;
|
|
|
|
|
|
|
|
if (del_timer(&pq->hold_timer)) {
|
|
|
|
if (time_before(pq->hold_timer.expires, sched_next))
|
|
|
|
sched_next = pq->hold_timer.expires;
|
2013-10-08 16:49:45 +08:00
|
|
|
xfrm_pol_put(pol);
|
2013-02-05 19:52:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
__skb_queue_tail(&pq->hold_queue, skb);
|
2013-10-08 16:49:45 +08:00
|
|
|
if (!mod_timer(&pq->hold_timer, sched_next))
|
|
|
|
xfrm_pol_hold(pol);
|
2013-02-05 19:52:55 +08:00
|
|
|
|
|
|
|
spin_unlock_bh(&pq->hold_queue.lock);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct xfrm_dst *xfrm_create_dummy_bundle(struct net *net,
|
2014-09-16 16:08:49 +08:00
|
|
|
struct xfrm_flo *xflo,
|
2013-02-05 19:52:55 +08:00
|
|
|
const struct flowi *fl,
|
|
|
|
int num_xfrms,
|
|
|
|
u16 family)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
struct net_device *dev;
|
2014-09-16 16:08:49 +08:00
|
|
|
struct dst_entry *dst;
|
2013-02-05 19:52:55 +08:00
|
|
|
struct dst_entry *dst1;
|
|
|
|
struct xfrm_dst *xdst;
|
|
|
|
|
|
|
|
xdst = xfrm_alloc_dst(net, family);
|
|
|
|
if (IS_ERR(xdst))
|
|
|
|
return xdst;
|
|
|
|
|
2014-09-16 16:08:49 +08:00
|
|
|
if (!(xflo->flags & XFRM_LOOKUP_QUEUE) ||
|
|
|
|
net->xfrm.sysctl_larval_drop ||
|
|
|
|
num_xfrms <= 0)
|
2013-02-05 19:52:55 +08:00
|
|
|
return xdst;
|
|
|
|
|
2014-09-16 16:08:49 +08:00
|
|
|
dst = xflo->dst_orig;
|
2013-02-05 19:52:55 +08:00
|
|
|
dst1 = &xdst->u.dst;
|
|
|
|
dst_hold(dst);
|
|
|
|
xdst->route = dst;
|
|
|
|
|
|
|
|
dst_copy_metrics(dst1, dst);
|
|
|
|
|
|
|
|
dst1->obsolete = DST_OBSOLETE_FORCE_CHK;
|
|
|
|
dst1->flags |= DST_HOST | DST_XFRM_QUEUE;
|
|
|
|
dst1->lastuse = jiffies;
|
|
|
|
|
|
|
|
dst1->input = dst_discard;
|
|
|
|
dst1->output = xdst_queue_output;
|
|
|
|
|
|
|
|
dst_hold(dst);
|
2017-11-29 04:40:28 +08:00
|
|
|
xfrm_dst_set_child(xdst, dst);
|
2017-11-29 04:40:46 +08:00
|
|
|
xdst->path = dst;
|
2013-02-05 19:52:55 +08:00
|
|
|
|
|
|
|
xfrm_init_path((struct xfrm_dst *)dst1, dst, 0);
|
|
|
|
|
|
|
|
err = -ENODEV;
|
|
|
|
dev = dst->dev;
|
|
|
|
if (!dev)
|
|
|
|
goto free_dst;
|
|
|
|
|
|
|
|
err = xfrm_fill_dst(xdst, dev, fl);
|
|
|
|
if (err)
|
|
|
|
goto free_dst;
|
|
|
|
|
|
|
|
out:
|
|
|
|
return xdst;
|
|
|
|
|
|
|
|
free_dst:
|
|
|
|
dst_release(dst1);
|
|
|
|
xdst = ERR_PTR(err);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2017-07-17 19:57:25 +08:00
|
|
|
static struct xfrm_dst *
|
2017-07-17 19:57:21 +08:00
|
|
|
xfrm_bundle_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir, struct xfrm_flo *xflo)
|
2010-04-07 08:30:05 +08:00
|
|
|
{
|
|
|
|
struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX];
|
2017-07-17 19:57:22 +08:00
|
|
|
int num_pols = 0, num_xfrms = 0, err;
|
2017-07-17 19:57:25 +08:00
|
|
|
struct xfrm_dst *xdst;
|
2010-04-07 08:30:05 +08:00
|
|
|
|
|
|
|
/* Resolve policies to use if we couldn't get them from
|
|
|
|
* previous cache entry */
|
2017-07-17 19:57:22 +08:00
|
|
|
num_pols = 1;
|
2017-07-17 19:57:24 +08:00
|
|
|
pols[0] = xfrm_policy_lookup(net, fl, family, dir);
|
2017-07-17 19:57:22 +08:00
|
|
|
err = xfrm_expand_policies(fl, family, pols,
|
2010-04-07 08:30:05 +08:00
|
|
|
&num_pols, &num_xfrms);
|
2017-07-17 19:57:22 +08:00
|
|
|
if (err < 0)
|
|
|
|
goto inc_error;
|
|
|
|
if (num_pols == 0)
|
|
|
|
return NULL;
|
|
|
|
if (num_xfrms <= 0)
|
|
|
|
goto make_dummy_bundle;
|
2010-04-07 08:30:05 +08:00
|
|
|
|
2018-01-10 19:14:28 +08:00
|
|
|
local_bh_disable();
|
2017-07-17 19:57:25 +08:00
|
|
|
xdst = xfrm_resolve_and_create_bundle(pols, num_pols, fl, family,
|
2018-01-10 19:14:28 +08:00
|
|
|
xflo->dst_orig);
|
|
|
|
local_bh_enable();
|
|
|
|
|
2017-07-17 19:57:25 +08:00
|
|
|
if (IS_ERR(xdst)) {
|
|
|
|
err = PTR_ERR(xdst);
|
2010-04-07 08:30:05 +08:00
|
|
|
if (err != -EAGAIN)
|
|
|
|
goto error;
|
2017-07-17 19:57:22 +08:00
|
|
|
goto make_dummy_bundle;
|
2017-07-17 19:57:25 +08:00
|
|
|
} else if (xdst == NULL) {
|
2010-07-13 05:29:42 +08:00
|
|
|
num_xfrms = 0;
|
2017-07-17 19:57:22 +08:00
|
|
|
goto make_dummy_bundle;
|
2010-04-07 08:30:05 +08:00
|
|
|
}
|
|
|
|
|
2017-07-17 19:57:25 +08:00
|
|
|
return xdst;
|
2010-04-07 08:30:05 +08:00
|
|
|
|
|
|
|
make_dummy_bundle:
|
|
|
|
/* We found policies, but there's no bundles to instantiate:
|
|
|
|
* either because the policy blocks, has no transformations or
|
|
|
|
* we could not build template (no xfrm_states).*/
|
2014-09-16 16:08:49 +08:00
|
|
|
xdst = xfrm_create_dummy_bundle(net, xflo, fl, num_xfrms, family);
|
2010-04-07 08:30:05 +08:00
|
|
|
if (IS_ERR(xdst)) {
|
|
|
|
xfrm_pols_put(pols, num_pols);
|
|
|
|
return ERR_CAST(xdst);
|
|
|
|
}
|
|
|
|
xdst->num_pols = num_pols;
|
|
|
|
xdst->num_xfrms = num_xfrms;
|
2013-12-24 09:43:47 +08:00
|
|
|
memcpy(xdst->pols, pols, sizeof(struct xfrm_policy *) * num_pols);
|
2010-04-07 08:30:05 +08:00
|
|
|
|
2017-07-17 19:57:25 +08:00
|
|
|
return xdst;
|
2010-04-07 08:30:05 +08:00
|
|
|
|
|
|
|
inc_error:
|
|
|
|
XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTPOLERROR);
|
|
|
|
error:
|
2017-07-17 19:57:22 +08:00
|
|
|
xfrm_pols_put(pols, num_pols);
|
2010-04-07 08:30:05 +08:00
|
|
|
return ERR_PTR(err);
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-03-02 06:59:04 +08:00
|
|
|
static struct dst_entry *make_blackhole(struct net *net, u16 family,
|
|
|
|
struct dst_entry *dst_orig)
|
|
|
|
{
|
2017-02-07 22:00:19 +08:00
|
|
|
const struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
|
2011-03-02 06:59:04 +08:00
|
|
|
struct dst_entry *ret;
|
|
|
|
|
|
|
|
if (!afinfo) {
|
|
|
|
dst_release(dst_orig);
|
2012-09-18 06:40:10 +08:00
|
|
|
return ERR_PTR(-EINVAL);
|
2011-03-02 06:59:04 +08:00
|
|
|
} else {
|
|
|
|
ret = afinfo->blackhole_route(net, dst_orig);
|
|
|
|
}
|
2017-02-07 22:00:18 +08:00
|
|
|
rcu_read_unlock();
|
2011-03-02 06:59:04 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* 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.
|
|
|
|
*/
|
2011-03-03 05:27:41 +08:00
|
|
|
struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig,
|
|
|
|
const struct flowi *fl,
|
2015-09-25 22:39:10 +08:00
|
|
|
const struct sock *sk, int flags)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2006-08-24 13:43:30 +08:00
|
|
|
struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX];
|
2010-04-07 08:30:05 +08:00
|
|
|
struct xfrm_dst *xdst;
|
2011-03-03 05:27:41 +08:00
|
|
|
struct dst_entry *dst, *route;
|
2010-04-07 08:30:05 +08:00
|
|
|
u16 family = dst_orig->ops->family;
|
2017-07-17 19:57:23 +08:00
|
|
|
u8 dir = XFRM_POLICY_OUT;
|
2010-04-28 05:20:22 +08:00
|
|
|
int i, err, num_pols, num_xfrms = 0, drop_pols = 0;
|
2006-07-25 14:29:07 +08:00
|
|
|
|
2010-04-07 08:30:05 +08:00
|
|
|
dst = NULL;
|
|
|
|
xdst = NULL;
|
|
|
|
route = NULL;
|
2006-08-24 13:43:30 +08:00
|
|
|
|
2015-12-08 00:53:17 +08:00
|
|
|
sk = sk_const_to_full_sk(sk);
|
2007-08-26 04:46:55 +08:00
|
|
|
if (sk && sk->sk_policy[XFRM_POLICY_OUT]) {
|
2010-04-07 08:30:05 +08:00
|
|
|
num_pols = 1;
|
2017-02-14 14:43:56 +08:00
|
|
|
pols[0] = xfrm_sk_policy_lookup(sk, XFRM_POLICY_OUT, fl, family);
|
2010-04-07 08:30:05 +08:00
|
|
|
err = xfrm_expand_policies(fl, family, pols,
|
|
|
|
&num_pols, &num_xfrms);
|
|
|
|
if (err < 0)
|
2007-12-11 20:38:08 +08:00
|
|
|
goto dropdst;
|
2010-04-07 08:30:05 +08:00
|
|
|
|
|
|
|
if (num_pols) {
|
|
|
|
if (num_xfrms <= 0) {
|
|
|
|
drop_pols = num_pols;
|
|
|
|
goto no_transform;
|
|
|
|
}
|
|
|
|
|
2018-01-10 19:14:28 +08:00
|
|
|
local_bh_disable();
|
2010-04-07 08:30:05 +08:00
|
|
|
xdst = xfrm_resolve_and_create_bundle(
|
|
|
|
pols, num_pols, fl,
|
|
|
|
family, dst_orig);
|
2018-01-10 19:14:28 +08:00
|
|
|
local_bh_enable();
|
|
|
|
|
2010-04-07 08:30:05 +08:00
|
|
|
if (IS_ERR(xdst)) {
|
|
|
|
xfrm_pols_put(pols, num_pols);
|
|
|
|
err = PTR_ERR(xdst);
|
|
|
|
goto dropdst;
|
2010-07-13 05:29:42 +08:00
|
|
|
} else if (xdst == NULL) {
|
|
|
|
num_xfrms = 0;
|
|
|
|
drop_pols = num_pols;
|
|
|
|
goto no_transform;
|
2010-04-07 08:30:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
route = xdst->route;
|
2007-12-21 12:43:36 +08:00
|
|
|
}
|
2006-10-06 04:42:35 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-04-07 08:30:05 +08:00
|
|
|
if (xdst == NULL) {
|
2014-09-16 16:08:49 +08:00
|
|
|
struct xfrm_flo xflo;
|
|
|
|
|
|
|
|
xflo.dst_orig = dst_orig;
|
|
|
|
xflo.flags = flags;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* To accelerate a bit... */
|
2006-08-24 19:45:07 +08:00
|
|
|
if ((dst_orig->flags & DST_NOXFRM) ||
|
2008-11-26 09:35:18 +08:00
|
|
|
!net->xfrm.policy_count[XFRM_POLICY_OUT])
|
2007-12-13 02:44:43 +08:00
|
|
|
goto nopol;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-07-17 19:57:25 +08:00
|
|
|
xdst = xfrm_bundle_lookup(net, fl, family, dir, &xflo);
|
|
|
|
if (xdst == NULL)
|
2010-04-07 08:30:05 +08:00
|
|
|
goto nopol;
|
2017-07-17 19:57:25 +08:00
|
|
|
if (IS_ERR(xdst)) {
|
|
|
|
err = PTR_ERR(xdst);
|
2007-12-11 20:38:08 +08:00
|
|
|
goto dropdst;
|
2008-01-08 13:46:15 +08:00
|
|
|
}
|
2010-04-07 08:30:05 +08:00
|
|
|
|
|
|
|
num_pols = xdst->num_pols;
|
|
|
|
num_xfrms = xdst->num_xfrms;
|
2013-12-24 09:43:47 +08:00
|
|
|
memcpy(pols, xdst->pols, sizeof(struct xfrm_policy *) * num_pols);
|
2010-04-07 08:30:05 +08:00
|
|
|
route = xdst->route;
|
|
|
|
}
|
|
|
|
|
|
|
|
dst = &xdst->u.dst;
|
|
|
|
if (route == NULL && num_xfrms > 0) {
|
|
|
|
/* The only case when xfrm_bundle_lookup() returns a
|
|
|
|
* bundle with null route, is when the template could
|
|
|
|
* not be resolved. It means policies are there, but
|
|
|
|
* bundle could not be created, since we don't yet
|
|
|
|
* have the xfrm_state's. We need to wait for KM to
|
|
|
|
* negotiate new SA's or bail out with error.*/
|
|
|
|
if (net->xfrm.sysctl_larval_drop) {
|
|
|
|
XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTNOSTATES);
|
2015-02-12 01:10:36 +08:00
|
|
|
err = -EREMOTE;
|
|
|
|
goto error;
|
2010-04-07 08:30:05 +08:00
|
|
|
}
|
|
|
|
|
2013-08-27 19:43:30 +08:00
|
|
|
err = -EAGAIN;
|
2010-04-07 08:30:05 +08:00
|
|
|
|
|
|
|
XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTNOSTATES);
|
|
|
|
goto error;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2010-04-07 08:30:05 +08:00
|
|
|
no_transform:
|
|
|
|
if (num_pols == 0)
|
2007-12-13 02:44:43 +08:00
|
|
|
goto nopol;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-04-07 08:30:05 +08:00
|
|
|
if ((flags & XFRM_LOOKUP_ICMP) &&
|
|
|
|
!(pols[0]->flags & XFRM_POLICY_ICMP)) {
|
|
|
|
err = -ENOENT;
|
2007-12-13 02:44:43 +08:00
|
|
|
goto error;
|
2010-04-07 08:30:05 +08:00
|
|
|
}
|
2007-12-13 02:44:43 +08:00
|
|
|
|
2010-04-07 08:30:05 +08:00
|
|
|
for (i = 0; i < num_pols; i++)
|
|
|
|
pols[i]->curlft.use_time = get_seconds();
|
2007-12-13 02:44:43 +08:00
|
|
|
|
2010-04-07 08:30:05 +08:00
|
|
|
if (num_xfrms < 0) {
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Prohibit the flow */
|
2008-11-26 09:59:52 +08:00
|
|
|
XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTPOLBLOCK);
|
2005-09-09 06:11:55 +08:00
|
|
|
err = -EPERM;
|
|
|
|
goto error;
|
2010-04-07 08:30:05 +08:00
|
|
|
} else if (num_xfrms > 0) {
|
|
|
|
/* Flow transformed */
|
|
|
|
dst_release(dst_orig);
|
|
|
|
} else {
|
|
|
|
/* Flow passes untransformed */
|
|
|
|
dst_release(dst);
|
2011-03-03 05:27:41 +08:00
|
|
|
dst = dst_orig;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2010-04-07 08:30:05 +08:00
|
|
|
ok:
|
|
|
|
xfrm_pols_put(pols, drop_pols);
|
2012-05-26 09:30:53 +08:00
|
|
|
if (dst && dst->xfrm &&
|
|
|
|
dst->xfrm->props.mode == XFRM_MODE_TUNNEL)
|
|
|
|
dst->flags |= DST_XFRM_TUNNEL;
|
2011-03-03 05:27:41 +08:00
|
|
|
return dst;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-04-07 08:30:05 +08:00
|
|
|
nopol:
|
2011-03-03 05:27:41 +08:00
|
|
|
if (!(flags & XFRM_LOOKUP_ICMP)) {
|
|
|
|
dst = dst_orig;
|
2010-04-07 08:30:05 +08:00
|
|
|
goto ok;
|
2011-03-03 05:27:41 +08:00
|
|
|
}
|
2010-04-07 08:30:05 +08:00
|
|
|
err = -ENOENT;
|
2005-04-17 06:20:36 +08:00
|
|
|
error:
|
2010-04-07 08:30:05 +08:00
|
|
|
dst_release(dst);
|
2007-12-11 20:38:08 +08:00
|
|
|
dropdst:
|
2015-02-12 01:10:36 +08:00
|
|
|
if (!(flags & XFRM_LOOKUP_KEEP_DST_REF))
|
|
|
|
dst_release(dst_orig);
|
2010-04-07 08:30:05 +08:00
|
|
|
xfrm_pols_put(pols, drop_pols);
|
2011-03-03 05:27:41 +08:00
|
|
|
return ERR_PTR(err);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(xfrm_lookup);
|
|
|
|
|
2014-09-16 16:08:40 +08:00
|
|
|
/* Callers of xfrm_lookup_route() must ensure a call to dst_output().
|
|
|
|
* Otherwise we may send out blackholed packets.
|
|
|
|
*/
|
|
|
|
struct dst_entry *xfrm_lookup_route(struct net *net, struct dst_entry *dst_orig,
|
|
|
|
const struct flowi *fl,
|
2015-09-25 22:39:10 +08:00
|
|
|
const struct sock *sk, int flags)
|
2014-09-16 16:08:40 +08:00
|
|
|
{
|
2014-09-16 16:08:49 +08:00
|
|
|
struct dst_entry *dst = xfrm_lookup(net, dst_orig, fl, sk,
|
2015-02-12 01:10:36 +08:00
|
|
|
flags | XFRM_LOOKUP_QUEUE |
|
|
|
|
XFRM_LOOKUP_KEEP_DST_REF);
|
2014-09-16 16:08:40 +08:00
|
|
|
|
|
|
|
if (IS_ERR(dst) && PTR_ERR(dst) == -EREMOTE)
|
|
|
|
return make_blackhole(net, dst_orig->ops->family, dst_orig);
|
|
|
|
|
|
|
|
return dst;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(xfrm_lookup_route);
|
|
|
|
|
2006-08-24 11:41:00 +08:00
|
|
|
static inline int
|
2011-02-23 09:59:59 +08:00
|
|
|
xfrm_secpath_reject(int idx, struct sk_buff *skb, const struct flowi *fl)
|
2006-08-24 11:41:00 +08:00
|
|
|
{
|
|
|
|
struct xfrm_state *x;
|
|
|
|
|
|
|
|
if (!skb->sp || idx < 0 || idx >= skb->sp->len)
|
|
|
|
return 0;
|
|
|
|
x = skb->sp->xvec[idx];
|
|
|
|
if (!x->type->reject)
|
|
|
|
return 0;
|
2007-10-10 04:24:07 +08:00
|
|
|
return x->type->reject(x, skb, fl);
|
2006-08-24 11:41:00 +08:00
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* When skb is transformed back to its "native" form, we have to
|
|
|
|
* check policy restrictions. At the moment we make this in maximally
|
|
|
|
* stupid way. Shame on me. :-) Of course, connected sockets must
|
|
|
|
* have policy cached at them.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static inline int
|
2011-02-24 14:43:01 +08:00
|
|
|
xfrm_state_ok(const struct xfrm_tmpl *tmpl, const struct xfrm_state *x,
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned short family)
|
|
|
|
{
|
|
|
|
if (xfrm_state_kern(x))
|
2007-02-14 04:57:16 +08:00
|
|
|
return tmpl->optional && !xfrm_state_addr_cmp(tmpl, x, tmpl->encap_family);
|
2005-04-17 06:20:36 +08:00
|
|
|
return x->id.proto == tmpl->id.proto &&
|
|
|
|
(x->id.spi == tmpl->id.spi || !tmpl->id.spi) &&
|
|
|
|
(x->props.reqid == tmpl->reqid || !tmpl->reqid) &&
|
|
|
|
x->props.mode == tmpl->mode &&
|
2008-04-22 15:46:42 +08:00
|
|
|
(tmpl->allalgs || (tmpl->aalgos & (1<<x->props.aalgo)) ||
|
2006-08-24 09:00:48 +08:00
|
|
|
!(xfrm_id_proto_match(tmpl->id.proto, IPSEC_PROTO_ANY))) &&
|
2006-09-23 06:05:15 +08:00
|
|
|
!(x->props.mode != XFRM_MODE_TRANSPORT &&
|
|
|
|
xfrm_state_addr_cmp(tmpl, x, family));
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2006-08-24 11:41:00 +08:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2005-04-17 06:20:36 +08:00
|
|
|
static inline int
|
2011-02-24 14:43:33 +08:00
|
|
|
xfrm_policy_ok(const struct xfrm_tmpl *tmpl, const struct sec_path *sp, int start,
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned short family)
|
|
|
|
{
|
|
|
|
int idx = start;
|
|
|
|
|
|
|
|
if (tmpl->optional) {
|
2006-09-23 06:05:15 +08:00
|
|
|
if (tmpl->mode == XFRM_MODE_TRANSPORT)
|
2005-04-17 06:20:36 +08:00
|
|
|
return start;
|
|
|
|
} else
|
|
|
|
start = -1;
|
|
|
|
for (; idx < sp->len; idx++) {
|
2006-04-01 16:54:16 +08:00
|
|
|
if (xfrm_state_ok(tmpl, sp->xvec[idx], family))
|
2005-04-17 06:20:36 +08:00
|
|
|
return ++idx;
|
2006-08-24 11:41:00 +08:00
|
|
|
if (sp->xvec[idx]->props.mode != XFRM_MODE_TRANSPORT) {
|
|
|
|
if (start == -1)
|
|
|
|
start = -2-idx;
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
2006-08-24 11:41:00 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
return start;
|
|
|
|
}
|
|
|
|
|
2007-12-13 02:44:16 +08:00
|
|
|
int __xfrm_decode_session(struct sk_buff *skb, struct flowi *fl,
|
|
|
|
unsigned int family, int reverse)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2017-02-07 22:00:19 +08:00
|
|
|
const struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
|
2006-07-25 14:29:07 +08:00
|
|
|
int err;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (unlikely(afinfo == NULL))
|
|
|
|
return -EAFNOSUPPORT;
|
|
|
|
|
2007-12-13 02:44:16 +08:00
|
|
|
afinfo->decode_session(skb, fl, reverse);
|
2011-03-12 13:29:39 +08:00
|
|
|
err = security_xfrm_decode_session(skb, &fl->flowi_secid);
|
2017-02-07 22:00:18 +08:00
|
|
|
rcu_read_unlock();
|
2006-07-25 14:29:07 +08:00
|
|
|
return err;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2007-12-13 02:44:16 +08:00
|
|
|
EXPORT_SYMBOL(__xfrm_decode_session);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-02-24 14:44:12 +08:00
|
|
|
static inline int secpath_has_nontransport(const struct sec_path *sp, int k, int *idxp)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
for (; k < sp->len; k++) {
|
2006-08-24 11:41:00 +08:00
|
|
|
if (sp->xvec[k]->props.mode != XFRM_MODE_TRANSPORT) {
|
2006-09-01 15:32:12 +08:00
|
|
|
*idxp = k;
|
2005-04-17 06:20:36 +08:00
|
|
|
return 1;
|
2006-08-24 11:41:00 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-02-09 22:25:29 +08:00
|
|
|
int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned short family)
|
|
|
|
{
|
2008-11-26 09:35:44 +08:00
|
|
|
struct net *net = dev_net(skb->dev);
|
2005-04-17 06:20:36 +08:00
|
|
|
struct xfrm_policy *pol;
|
2006-08-24 13:43:30 +08:00
|
|
|
struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX];
|
|
|
|
int npols = 0;
|
|
|
|
int xfrm_nr;
|
|
|
|
int pi;
|
2007-12-13 02:44:16 +08:00
|
|
|
int reverse;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct flowi fl;
|
2006-08-24 11:41:00 +08:00
|
|
|
int xerr_idx = -1;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-12-13 02:44:16 +08:00
|
|
|
reverse = dir & ~XFRM_POLICY_MASK;
|
|
|
|
dir &= XFRM_POLICY_MASK;
|
|
|
|
|
2007-12-21 12:43:36 +08:00
|
|
|
if (__xfrm_decode_session(skb, &fl, family, reverse) < 0) {
|
2008-11-26 09:59:52 +08:00
|
|
|
XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
2007-12-21 12:43:36 +08:00
|
|
|
}
|
|
|
|
|
2006-01-07 15:06:30 +08:00
|
|
|
nf_nat_decode_session(skb, &fl, family);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* First, check used SA against their selectors. */
|
|
|
|
if (skb->sp) {
|
|
|
|
int i;
|
|
|
|
|
2013-12-24 09:43:46 +08:00
|
|
|
for (i = skb->sp->len-1; i >= 0; i--) {
|
2006-04-01 16:54:16 +08:00
|
|
|
struct xfrm_state *x = skb->sp->xvec[i];
|
2007-12-21 12:43:36 +08:00
|
|
|
if (!xfrm_selector_match(&x->sel, &fl, family)) {
|
2008-11-26 09:59:52 +08:00
|
|
|
XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMISMATCH);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
2007-12-21 12:43:36 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pol = NULL;
|
2015-12-08 00:53:17 +08:00
|
|
|
sk = sk_to_full_sk(sk);
|
2006-10-06 04:42:35 +08:00
|
|
|
if (sk && sk->sk_policy[dir]) {
|
2017-02-14 14:43:56 +08:00
|
|
|
pol = xfrm_sk_policy_lookup(sk, dir, &fl, family);
|
2007-12-21 12:43:36 +08:00
|
|
|
if (IS_ERR(pol)) {
|
2008-11-26 09:59:52 +08:00
|
|
|
XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLERROR);
|
2006-10-06 04:42:35 +08:00
|
|
|
return 0;
|
2007-12-21 12:43:36 +08:00
|
|
|
}
|
2006-10-06 04:42:35 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-07-17 19:57:24 +08:00
|
|
|
if (!pol)
|
|
|
|
pol = xfrm_policy_lookup(net, &fl, family, dir);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-12-21 12:43:36 +08:00
|
|
|
if (IS_ERR(pol)) {
|
2008-11-26 09:59:52 +08:00
|
|
|
XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLERROR);
|
IPsec: propagate security module errors up from flow_cache_lookup
When a security module is loaded (in this case, SELinux), the
security_xfrm_policy_lookup() hook can return an access denied permission
(or other error). We were not handling that correctly, and in fact
inverting the return logic and propagating a false "ok" back up to
xfrm_lookup(), which then allowed packets to pass as if they were not
associated with an xfrm policy.
The way I was seeing the problem was when connecting via IPsec to a
confined service on an SELinux box (vsftpd), which did not have the
appropriate SELinux policy permissions to send packets via IPsec.
The first SYNACK would be blocked, because of an uncached lookup via
flow_cache_lookup(), which would fail to resolve an xfrm policy because
the SELinux policy is checked at that point via the resolver.
However, retransmitted SYNACKs would then find a cached flow entry when
calling into flow_cache_lookup() with a null xfrm policy, which is
interpreted by xfrm_lookup() as the packet not having any associated
policy and similarly to the first case, allowing it to pass without
transformation.
The solution presented here is to first ensure that errno values are
correctly propagated all the way back up through the various call chains
from security_xfrm_policy_lookup(), and handled correctly.
Then, flow_cache_lookup() is modified, so that if the policy resolver
fails (typically a permission denied via the security module), the flow
cache entry is killed rather than having a null policy assigned (which
indicates that the packet can pass freely). This also forces any future
lookups for the same flow to consult the security module (e.g. SELinux)
for current security policy (rather than, say, caching the error on the
flow cache entry).
Signed-off-by: James Morris <jmorris@namei.org>
2006-10-06 04:42:27 +08:00
|
|
|
return 0;
|
2007-12-21 12:43:36 +08:00
|
|
|
}
|
IPsec: propagate security module errors up from flow_cache_lookup
When a security module is loaded (in this case, SELinux), the
security_xfrm_policy_lookup() hook can return an access denied permission
(or other error). We were not handling that correctly, and in fact
inverting the return logic and propagating a false "ok" back up to
xfrm_lookup(), which then allowed packets to pass as if they were not
associated with an xfrm policy.
The way I was seeing the problem was when connecting via IPsec to a
confined service on an SELinux box (vsftpd), which did not have the
appropriate SELinux policy permissions to send packets via IPsec.
The first SYNACK would be blocked, because of an uncached lookup via
flow_cache_lookup(), which would fail to resolve an xfrm policy because
the SELinux policy is checked at that point via the resolver.
However, retransmitted SYNACKs would then find a cached flow entry when
calling into flow_cache_lookup() with a null xfrm policy, which is
interpreted by xfrm_lookup() as the packet not having any associated
policy and similarly to the first case, allowing it to pass without
transformation.
The solution presented here is to first ensure that errno values are
correctly propagated all the way back up through the various call chains
from security_xfrm_policy_lookup(), and handled correctly.
Then, flow_cache_lookup() is modified, so that if the policy resolver
fails (typically a permission denied via the security module), the flow
cache entry is killed rather than having a null policy assigned (which
indicates that the packet can pass freely). This also forces any future
lookups for the same flow to consult the security module (e.g. SELinux)
for current security policy (rather than, say, caching the error on the
flow cache entry).
Signed-off-by: James Morris <jmorris@namei.org>
2006-10-06 04:42:27 +08:00
|
|
|
|
2006-08-24 11:41:00 +08:00
|
|
|
if (!pol) {
|
2006-09-01 15:32:12 +08:00
|
|
|
if (skb->sp && secpath_has_nontransport(skb->sp, 0, &xerr_idx)) {
|
2006-08-24 11:41:00 +08:00
|
|
|
xfrm_secpath_reject(xerr_idx, skb, &fl);
|
2008-11-26 09:59:52 +08:00
|
|
|
XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOPOLS);
|
2006-08-24 11:41:00 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-03-05 08:12:44 +08:00
|
|
|
pol->curlft.use_time = get_seconds();
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-08-24 13:43:30 +08:00
|
|
|
pols[0] = pol;
|
2013-12-24 09:43:48 +08:00
|
|
|
npols++;
|
2006-08-24 13:43:30 +08:00
|
|
|
#ifdef CONFIG_XFRM_SUB_POLICY
|
|
|
|
if (pols[0]->type != XFRM_POLICY_TYPE_MAIN) {
|
2008-11-26 09:35:44 +08:00
|
|
|
pols[1] = xfrm_policy_lookup_bytype(net, XFRM_POLICY_TYPE_MAIN,
|
2006-08-24 13:43:30 +08:00
|
|
|
&fl, family,
|
|
|
|
XFRM_POLICY_IN);
|
|
|
|
if (pols[1]) {
|
2007-12-21 12:43:36 +08:00
|
|
|
if (IS_ERR(pols[1])) {
|
2008-11-26 09:59:52 +08:00
|
|
|
XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLERROR);
|
IPsec: propagate security module errors up from flow_cache_lookup
When a security module is loaded (in this case, SELinux), the
security_xfrm_policy_lookup() hook can return an access denied permission
(or other error). We were not handling that correctly, and in fact
inverting the return logic and propagating a false "ok" back up to
xfrm_lookup(), which then allowed packets to pass as if they were not
associated with an xfrm policy.
The way I was seeing the problem was when connecting via IPsec to a
confined service on an SELinux box (vsftpd), which did not have the
appropriate SELinux policy permissions to send packets via IPsec.
The first SYNACK would be blocked, because of an uncached lookup via
flow_cache_lookup(), which would fail to resolve an xfrm policy because
the SELinux policy is checked at that point via the resolver.
However, retransmitted SYNACKs would then find a cached flow entry when
calling into flow_cache_lookup() with a null xfrm policy, which is
interpreted by xfrm_lookup() as the packet not having any associated
policy and similarly to the first case, allowing it to pass without
transformation.
The solution presented here is to first ensure that errno values are
correctly propagated all the way back up through the various call chains
from security_xfrm_policy_lookup(), and handled correctly.
Then, flow_cache_lookup() is modified, so that if the policy resolver
fails (typically a permission denied via the security module), the flow
cache entry is killed rather than having a null policy assigned (which
indicates that the packet can pass freely). This also forces any future
lookups for the same flow to consult the security module (e.g. SELinux)
for current security policy (rather than, say, caching the error on the
flow cache entry).
Signed-off-by: James Morris <jmorris@namei.org>
2006-10-06 04:42:27 +08:00
|
|
|
return 0;
|
2007-12-21 12:43:36 +08:00
|
|
|
}
|
2007-03-05 08:12:44 +08:00
|
|
|
pols[1]->curlft.use_time = get_seconds();
|
2013-12-24 09:43:48 +08:00
|
|
|
npols++;
|
2006-08-24 13:43:30 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (pol->action == XFRM_POLICY_ALLOW) {
|
|
|
|
struct sec_path *sp;
|
|
|
|
static struct sec_path dummy;
|
2006-08-24 13:43:30 +08:00
|
|
|
struct xfrm_tmpl *tp[XFRM_MAX_DEPTH];
|
2006-08-24 13:48:31 +08:00
|
|
|
struct xfrm_tmpl *stp[XFRM_MAX_DEPTH];
|
2006-08-24 13:43:30 +08:00
|
|
|
struct xfrm_tmpl **tpp = tp;
|
|
|
|
int ti = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
int i, k;
|
|
|
|
|
|
|
|
if ((sp = skb->sp) == NULL)
|
|
|
|
sp = &dummy;
|
|
|
|
|
2006-08-24 13:43:30 +08:00
|
|
|
for (pi = 0; pi < npols; pi++) {
|
|
|
|
if (pols[pi] != pol &&
|
2007-12-21 12:43:36 +08:00
|
|
|
pols[pi]->action != XFRM_POLICY_ALLOW) {
|
2008-11-26 09:59:52 +08:00
|
|
|
XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLBLOCK);
|
2006-08-24 13:43:30 +08:00
|
|
|
goto reject;
|
2007-12-21 12:43:36 +08:00
|
|
|
}
|
|
|
|
if (ti + pols[pi]->xfrm_nr >= XFRM_MAX_DEPTH) {
|
2008-11-26 09:59:52 +08:00
|
|
|
XFRM_INC_STATS(net, LINUX_MIB_XFRMINBUFFERERROR);
|
2006-08-24 13:43:30 +08:00
|
|
|
goto reject_error;
|
2007-12-21 12:43:36 +08:00
|
|
|
}
|
2006-08-24 13:43:30 +08:00
|
|
|
for (i = 0; i < pols[pi]->xfrm_nr; i++)
|
|
|
|
tpp[ti++] = &pols[pi]->xfrm_vec[i];
|
|
|
|
}
|
|
|
|
xfrm_nr = ti;
|
2006-08-24 13:48:31 +08:00
|
|
|
if (npols > 1) {
|
2013-11-07 17:47:50 +08:00
|
|
|
xfrm_tmpl_sort(stp, tpp, xfrm_nr, family, net);
|
2006-08-24 13:48:31 +08:00
|
|
|
tpp = stp;
|
|
|
|
}
|
2006-08-24 13:43:30 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* 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.
|
|
|
|
*/
|
2006-08-24 13:43:30 +08:00
|
|
|
for (i = xfrm_nr-1, k = 0; i >= 0; i--) {
|
|
|
|
k = xfrm_policy_ok(tpp[i], sp, k, family);
|
2006-08-24 11:41:00 +08:00
|
|
|
if (k < 0) {
|
2006-09-01 15:32:12 +08:00
|
|
|
if (k < -1)
|
|
|
|
/* "-2 - errored_index" returned */
|
|
|
|
xerr_idx = -(2+k);
|
2008-11-26 09:59:52 +08:00
|
|
|
XFRM_INC_STATS(net, LINUX_MIB_XFRMINTMPLMISMATCH);
|
2005-04-17 06:20:36 +08:00
|
|
|
goto reject;
|
2006-08-24 11:41:00 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2007-12-21 12:43:36 +08:00
|
|
|
if (secpath_has_nontransport(sp, k, &xerr_idx)) {
|
2008-11-26 09:59:52 +08:00
|
|
|
XFRM_INC_STATS(net, LINUX_MIB_XFRMINTMPLMISMATCH);
|
2005-04-17 06:20:36 +08:00
|
|
|
goto reject;
|
2007-12-21 12:43:36 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-08-24 13:43:30 +08:00
|
|
|
xfrm_pols_put(pols, npols);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 1;
|
|
|
|
}
|
2008-11-26 09:59:52 +08:00
|
|
|
XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLBLOCK);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
reject:
|
2006-08-24 11:41:00 +08:00
|
|
|
xfrm_secpath_reject(xerr_idx, skb, &fl);
|
2006-08-24 13:43:30 +08:00
|
|
|
reject_error:
|
|
|
|
xfrm_pols_put(pols, npols);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(__xfrm_policy_check);
|
|
|
|
|
|
|
|
int __xfrm_route_forward(struct sk_buff *skb, unsigned short family)
|
|
|
|
{
|
2008-11-26 09:36:13 +08:00
|
|
|
struct net *net = dev_net(skb->dev);
|
2005-04-17 06:20:36 +08:00
|
|
|
struct flowi fl;
|
2009-06-02 13:19:30 +08:00
|
|
|
struct dst_entry *dst;
|
2011-03-16 06:26:43 +08:00
|
|
|
int res = 1;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-12-21 12:43:36 +08:00
|
|
|
if (xfrm_decode_session(skb, &fl, family) < 0) {
|
2010-02-18 11:35:07 +08:00
|
|
|
XFRM_INC_STATS(net, LINUX_MIB_XFRMFWDHDRERROR);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
2007-12-21 12:43:36 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-06-01 18:04:49 +08:00
|
|
|
skb_dst_force(skb);
|
2009-06-02 13:19:30 +08:00
|
|
|
|
2014-09-16 16:08:49 +08:00
|
|
|
dst = xfrm_lookup(net, skb_dst(skb), &fl, NULL, XFRM_LOOKUP_QUEUE);
|
2011-03-03 05:27:41 +08:00
|
|
|
if (IS_ERR(dst)) {
|
2011-03-16 06:26:43 +08:00
|
|
|
res = 0;
|
2011-03-03 05:27:41 +08:00
|
|
|
dst = NULL;
|
|
|
|
}
|
2009-06-02 13:19:30 +08:00
|
|
|
skb_dst_set(skb, dst);
|
|
|
|
return res;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(__xfrm_route_forward);
|
|
|
|
|
2006-08-14 09:55:53 +08:00
|
|
|
/* Optimize later using cookies and generation ids. */
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static struct dst_entry *xfrm_dst_check(struct dst_entry *dst, u32 cookie)
|
|
|
|
{
|
2006-08-14 09:55:53 +08:00
|
|
|
/* Code (such as __xfrm4_bundle_create()) sets dst->obsolete
|
2012-07-20 03:31:33 +08:00
|
|
|
* to DST_OBSOLETE_FORCE_CHK to force all XFRM destinations to
|
|
|
|
* get validated by dst_ops->check on every use. We do this
|
|
|
|
* because when a normal route referenced by an XFRM dst is
|
|
|
|
* obsoleted we do not go looking around for all parent
|
|
|
|
* referencing XFRM dsts so that we can invalidate them. It
|
|
|
|
* is just too much work. Instead we make the checks here on
|
|
|
|
* every use. For example:
|
2006-08-14 09:55:53 +08:00
|
|
|
*
|
|
|
|
* XFRM dst A --> IPv4 dst X
|
|
|
|
*
|
|
|
|
* X is the "xdst->route" of A (X is also the "dst->path" of A
|
|
|
|
* in this example). If X is marked obsolete, "A" will not
|
|
|
|
* notice. That's what we are validating here via the
|
|
|
|
* stale_bundle() check.
|
|
|
|
*
|
2017-06-18 01:42:38 +08:00
|
|
|
* When a dst is removed from the fib tree, DST_OBSOLETE_DEAD will
|
|
|
|
* be marked on it.
|
2017-07-17 19:57:26 +08:00
|
|
|
* This will force stale_bundle() to fail on any xdst bundle with
|
2017-06-18 01:42:38 +08:00
|
|
|
* this dst linked in it.
|
2005-12-20 06:23:23 +08:00
|
|
|
*/
|
2006-08-14 09:55:53 +08:00
|
|
|
if (dst->obsolete < 0 && !stale_bundle(dst))
|
|
|
|
return dst;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int stale_bundle(struct dst_entry *dst)
|
|
|
|
{
|
2011-06-30 07:18:20 +08:00
|
|
|
return !xfrm_bundle_ok((struct xfrm_dst *)dst);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2005-05-04 07:27:10 +08:00
|
|
|
void xfrm_dst_ifdown(struct dst_entry *dst, struct net_device *dev)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2017-11-29 04:40:22 +08:00
|
|
|
while ((dst = xfrm_dst_child(dst)) && dst->xfrm && dst->dev == dev) {
|
2008-03-25 20:47:49 +08:00
|
|
|
dst->dev = dev_net(dev)->loopback_dev;
|
2007-09-26 10:16:28 +08:00
|
|
|
dev_hold(dst->dev);
|
2005-04-17 06:20:36 +08:00
|
|
|
dev_put(dev);
|
|
|
|
}
|
|
|
|
}
|
2005-05-04 07:27:10 +08:00
|
|
|
EXPORT_SYMBOL(xfrm_dst_ifdown);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
static void xfrm_link_failure(struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
/* Impossible. Such dst must be popped before reaches point of failure. */
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct dst_entry *xfrm_negative_advice(struct dst_entry *dst)
|
|
|
|
{
|
|
|
|
if (dst) {
|
|
|
|
if (dst->obsolete) {
|
|
|
|
dst_release(dst);
|
|
|
|
dst = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return dst;
|
|
|
|
}
|
|
|
|
|
2017-11-29 04:41:01 +08:00
|
|
|
static void xfrm_init_pmtu(struct xfrm_dst **bundle, int nr)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2017-11-29 04:41:01 +08:00
|
|
|
while (nr--) {
|
|
|
|
struct xfrm_dst *xdst = bundle[nr];
|
2005-04-17 06:20:36 +08:00
|
|
|
u32 pmtu, route_mtu_cached;
|
2017-11-29 04:41:01 +08:00
|
|
|
struct dst_entry *dst;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-11-29 04:41:01 +08:00
|
|
|
dst = &xdst->u.dst;
|
2017-11-29 04:40:22 +08:00
|
|
|
pmtu = dst_mtu(xfrm_dst_child(dst));
|
2005-04-17 06:20:36 +08:00
|
|
|
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;
|
|
|
|
|
2010-12-09 13:16:57 +08:00
|
|
|
dst_metric_set(dst, RTAX_MTU, pmtu);
|
2017-11-29 04:41:01 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Check that the bundle accepts the flow and its components are
|
|
|
|
* still valid.
|
|
|
|
*/
|
|
|
|
|
2011-06-30 07:18:20 +08:00
|
|
|
static int xfrm_bundle_ok(struct xfrm_dst *first)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2017-11-29 04:41:01 +08:00
|
|
|
struct xfrm_dst *bundle[XFRM_MAX_DEPTH];
|
2005-04-17 06:20:36 +08:00
|
|
|
struct dst_entry *dst = &first->u.dst;
|
2017-11-29 04:41:01 +08:00
|
|
|
struct xfrm_dst *xdst;
|
|
|
|
int start_from, nr;
|
2005-04-17 06:20:36 +08:00
|
|
|
u32 mtu;
|
|
|
|
|
2017-11-29 04:40:46 +08:00
|
|
|
if (!dst_check(xfrm_dst_path(dst), ((struct xfrm_dst *)dst)->path_cookie) ||
|
2005-04-17 06:20:36 +08:00
|
|
|
(dst->dev && !netif_running(dst->dev)))
|
|
|
|
return 0;
|
|
|
|
|
2013-02-05 19:52:55 +08:00
|
|
|
if (dst->flags & DST_XFRM_QUEUE)
|
|
|
|
return 1;
|
|
|
|
|
2017-11-29 04:41:01 +08:00
|
|
|
start_from = nr = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
do {
|
|
|
|
struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
|
|
|
|
|
|
|
|
if (dst->xfrm->km.state != XFRM_STATE_VALID)
|
|
|
|
return 0;
|
2010-04-07 08:30:05 +08:00
|
|
|
if (xdst->xfrm_genid != dst->xfrm->genid)
|
|
|
|
return 0;
|
2010-06-25 05:35:00 +08:00
|
|
|
if (xdst->num_pols > 0 &&
|
|
|
|
xdst->policy_genid != atomic_read(&xdst->pols[0]->genid))
|
2006-08-24 18:18:09 +08:00
|
|
|
return 0;
|
2006-08-24 10:12:01 +08:00
|
|
|
|
2017-11-29 04:41:01 +08:00
|
|
|
bundle[nr++] = xdst;
|
|
|
|
|
2017-11-29 04:40:22 +08:00
|
|
|
mtu = dst_mtu(xfrm_dst_child(dst));
|
2005-04-17 06:20:36 +08:00
|
|
|
if (xdst->child_mtu_cached != mtu) {
|
2017-11-29 04:41:01 +08:00
|
|
|
start_from = nr;
|
2005-04-17 06:20:36 +08:00
|
|
|
xdst->child_mtu_cached = mtu;
|
|
|
|
}
|
|
|
|
|
2005-05-27 03:58:04 +08:00
|
|
|
if (!dst_check(xdst->route, xdst->route_cookie))
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
mtu = dst_mtu(xdst->route);
|
|
|
|
if (xdst->route_mtu_cached != mtu) {
|
2017-11-29 04:41:01 +08:00
|
|
|
start_from = nr;
|
2005-04-17 06:20:36 +08:00
|
|
|
xdst->route_mtu_cached = mtu;
|
|
|
|
}
|
|
|
|
|
2017-11-29 04:40:22 +08:00
|
|
|
dst = xfrm_dst_child(dst);
|
2005-04-17 06:20:36 +08:00
|
|
|
} while (dst->xfrm);
|
|
|
|
|
2017-11-29 04:41:01 +08:00
|
|
|
if (likely(!start_from))
|
2005-04-17 06:20:36 +08:00
|
|
|
return 1;
|
|
|
|
|
2017-11-29 04:41:01 +08:00
|
|
|
xdst = bundle[start_from - 1];
|
|
|
|
mtu = xdst->child_mtu_cached;
|
|
|
|
while (start_from--) {
|
|
|
|
dst = &xdst->u.dst;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
mtu = xfrm_state_mtu(dst->xfrm, mtu);
|
2017-11-29 04:41:01 +08:00
|
|
|
if (mtu > xdst->route_mtu_cached)
|
|
|
|
mtu = xdst->route_mtu_cached;
|
2010-12-09 13:16:57 +08:00
|
|
|
dst_metric_set(dst, RTAX_MTU, mtu);
|
2017-11-29 04:41:01 +08:00
|
|
|
if (!start_from)
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
|
2017-11-29 04:41:01 +08:00
|
|
|
xdst = bundle[start_from - 1];
|
|
|
|
xdst->child_mtu_cached = mtu;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2010-12-14 04:52:14 +08:00
|
|
|
static unsigned int xfrm_default_advmss(const struct dst_entry *dst)
|
|
|
|
{
|
2017-11-29 04:40:46 +08:00
|
|
|
return dst_metric_advmss(xfrm_dst_path(dst));
|
2010-12-14 04:52:14 +08:00
|
|
|
}
|
|
|
|
|
2011-11-23 10:12:51 +08:00
|
|
|
static unsigned int xfrm_mtu(const struct dst_entry *dst)
|
2010-12-15 05:01:14 +08:00
|
|
|
{
|
2011-11-23 10:13:31 +08:00
|
|
|
unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
|
|
|
|
|
2017-11-29 04:40:46 +08:00
|
|
|
return mtu ? : dst_mtu(xfrm_dst_path(dst));
|
2010-12-15 05:01:14 +08:00
|
|
|
}
|
|
|
|
|
2017-02-25 23:57:43 +08:00
|
|
|
static const void *xfrm_get_dst_nexthop(const struct dst_entry *dst,
|
|
|
|
const void *daddr)
|
2017-02-07 05:14:15 +08:00
|
|
|
{
|
2017-11-29 04:40:46 +08:00
|
|
|
while (dst->xfrm) {
|
2017-02-07 05:14:15 +08:00
|
|
|
const struct xfrm_state *xfrm = dst->xfrm;
|
|
|
|
|
2018-02-19 14:44:07 +08:00
|
|
|
dst = xfrm_dst_child(dst);
|
|
|
|
|
2017-02-07 05:14:15 +08:00
|
|
|
if (xfrm->props.mode == XFRM_MODE_TRANSPORT)
|
|
|
|
continue;
|
|
|
|
if (xfrm->type->flags & XFRM_TYPE_REMOTE_COADDR)
|
|
|
|
daddr = xfrm->coaddr;
|
|
|
|
else if (!(xfrm->type->flags & XFRM_TYPE_LOCAL_COADDR))
|
|
|
|
daddr = &xfrm->id.daddr;
|
|
|
|
}
|
2017-02-25 23:57:43 +08:00
|
|
|
return daddr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct neighbour *xfrm_neigh_lookup(const struct dst_entry *dst,
|
|
|
|
struct sk_buff *skb,
|
|
|
|
const void *daddr)
|
|
|
|
{
|
2017-11-29 04:40:46 +08:00
|
|
|
const struct dst_entry *path = xfrm_dst_path(dst);
|
2017-02-25 23:57:43 +08:00
|
|
|
|
|
|
|
if (!skb)
|
|
|
|
daddr = xfrm_get_dst_nexthop(dst, daddr);
|
|
|
|
return path->ops->neigh_lookup(path, skb, daddr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void xfrm_confirm_neigh(const struct dst_entry *dst, const void *daddr)
|
|
|
|
{
|
2017-11-29 04:40:46 +08:00
|
|
|
const struct dst_entry *path = xfrm_dst_path(dst);
|
2017-02-25 23:57:43 +08:00
|
|
|
|
|
|
|
daddr = xfrm_get_dst_nexthop(dst, daddr);
|
2017-02-07 05:14:15 +08:00
|
|
|
path->ops->confirm_neigh(path, daddr);
|
|
|
|
}
|
|
|
|
|
2017-02-07 22:00:17 +08:00
|
|
|
int xfrm_policy_register_afinfo(const struct xfrm_policy_afinfo *afinfo, int family)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int err = 0;
|
2017-02-07 22:00:17 +08:00
|
|
|
|
|
|
|
if (WARN_ON(family >= ARRAY_SIZE(xfrm_policy_afinfo)))
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EAFNOSUPPORT;
|
2017-02-07 22:00:17 +08:00
|
|
|
|
2012-08-19 18:31:48 +08:00
|
|
|
spin_lock(&xfrm_policy_afinfo_lock);
|
2017-02-07 22:00:17 +08:00
|
|
|
if (unlikely(xfrm_policy_afinfo[family] != NULL))
|
2015-04-23 11:06:53 +08:00
|
|
|
err = -EEXIST;
|
2005-04-17 06:20:36 +08:00
|
|
|
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;
|
2010-12-14 04:52:14 +08:00
|
|
|
if (likely(dst_ops->default_advmss == NULL))
|
|
|
|
dst_ops->default_advmss = xfrm_default_advmss;
|
2011-11-23 10:12:51 +08:00
|
|
|
if (likely(dst_ops->mtu == NULL))
|
|
|
|
dst_ops->mtu = xfrm_mtu;
|
2005-04-17 06:20:36 +08:00
|
|
|
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;
|
2011-07-18 15:40:17 +08:00
|
|
|
if (likely(dst_ops->neigh_lookup == NULL))
|
|
|
|
dst_ops->neigh_lookup = xfrm_neigh_lookup;
|
2017-02-07 05:14:15 +08:00
|
|
|
if (likely(!dst_ops->confirm_neigh))
|
|
|
|
dst_ops->confirm_neigh = xfrm_confirm_neigh;
|
2017-02-07 22:00:17 +08:00
|
|
|
rcu_assign_pointer(xfrm_policy_afinfo[family], afinfo);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2012-08-19 18:31:48 +08:00
|
|
|
spin_unlock(&xfrm_policy_afinfo_lock);
|
2010-01-25 14:47:53 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(xfrm_policy_register_afinfo);
|
|
|
|
|
2017-02-07 22:00:17 +08:00
|
|
|
void xfrm_policy_unregister_afinfo(const struct xfrm_policy_afinfo *afinfo)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2017-02-07 22:00:15 +08:00
|
|
|
struct dst_ops *dst_ops = afinfo->dst_ops;
|
2017-02-07 22:00:17 +08:00
|
|
|
int i;
|
2017-02-07 22:00:15 +08:00
|
|
|
|
2017-02-07 22:00:17 +08:00
|
|
|
for (i = 0; i < ARRAY_SIZE(xfrm_policy_afinfo); i++) {
|
|
|
|
if (xfrm_policy_afinfo[i] != afinfo)
|
|
|
|
continue;
|
|
|
|
RCU_INIT_POINTER(xfrm_policy_afinfo[i], NULL);
|
|
|
|
break;
|
2012-08-19 18:31:48 +08:00
|
|
|
}
|
|
|
|
|
2017-02-07 22:00:15 +08:00
|
|
|
synchronize_rcu();
|
2012-08-19 18:31:48 +08:00
|
|
|
|
2017-02-07 22:00:15 +08:00
|
|
|
dst_ops->kmem_cachep = NULL;
|
|
|
|
dst_ops->check = NULL;
|
|
|
|
dst_ops->negative_advice = NULL;
|
|
|
|
dst_ops->link_failure = NULL;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(xfrm_policy_unregister_afinfo);
|
|
|
|
|
2007-12-21 12:42:57 +08:00
|
|
|
#ifdef CONFIG_XFRM_STATISTICS
|
2008-11-26 09:59:52 +08:00
|
|
|
static int __net_init xfrm_statistics_init(struct net *net)
|
2007-12-21 12:42:57 +08:00
|
|
|
{
|
2008-11-26 10:00:14 +08:00
|
|
|
int rv;
|
2014-05-06 06:55:55 +08:00
|
|
|
net->mib.xfrm_statistics = alloc_percpu(struct linux_xfrm_mib);
|
|
|
|
if (!net->mib.xfrm_statistics)
|
2007-12-21 12:42:57 +08:00
|
|
|
return -ENOMEM;
|
2008-11-26 10:00:14 +08:00
|
|
|
rv = xfrm_proc_init(net);
|
|
|
|
if (rv < 0)
|
2014-05-06 06:55:55 +08:00
|
|
|
free_percpu(net->mib.xfrm_statistics);
|
2008-11-26 10:00:14 +08:00
|
|
|
return rv;
|
2007-12-21 12:42:57 +08:00
|
|
|
}
|
2008-11-26 09:59:52 +08:00
|
|
|
|
|
|
|
static void xfrm_statistics_fini(struct net *net)
|
|
|
|
{
|
2008-11-26 10:00:14 +08:00
|
|
|
xfrm_proc_fini(net);
|
2014-05-06 06:55:55 +08:00
|
|
|
free_percpu(net->mib.xfrm_statistics);
|
2008-11-26 09:59:52 +08:00
|
|
|
}
|
|
|
|
#else
|
|
|
|
static int __net_init xfrm_statistics_init(struct net *net)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void xfrm_statistics_fini(struct net *net)
|
|
|
|
{
|
|
|
|
}
|
2007-12-21 12:42:57 +08:00
|
|
|
#endif
|
|
|
|
|
2008-11-26 09:14:31 +08:00
|
|
|
static int __net_init xfrm_policy_init(struct net *net)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2006-08-24 19:45:07 +08:00
|
|
|
unsigned int hmask, sz;
|
|
|
|
int dir;
|
|
|
|
|
2008-11-26 09:14:31 +08:00
|
|
|
if (net_eq(net, &init_net))
|
|
|
|
xfrm_dst_cache = kmem_cache_create("xfrm_dst_cache",
|
2005-04-17 06:20:36 +08:00
|
|
|
sizeof(struct xfrm_dst),
|
2006-08-27 10:25:52 +08:00
|
|
|
0, SLAB_HWCACHE_ALIGN|SLAB_PANIC,
|
2007-07-20 09:11:58 +08:00
|
|
|
NULL);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-08-24 19:45:07 +08:00
|
|
|
hmask = 8 - 1;
|
|
|
|
sz = (hmask+1) * sizeof(struct hlist_head);
|
|
|
|
|
2008-11-26 09:22:35 +08:00
|
|
|
net->xfrm.policy_byidx = xfrm_hash_alloc(sz);
|
|
|
|
if (!net->xfrm.policy_byidx)
|
|
|
|
goto out_byidx;
|
2008-11-26 09:22:58 +08:00
|
|
|
net->xfrm.policy_idx_hmask = hmask;
|
2006-08-24 19:45:07 +08:00
|
|
|
|
2014-11-13 17:09:49 +08:00
|
|
|
for (dir = 0; dir < XFRM_POLICY_MAX; dir++) {
|
2006-08-24 19:45:07 +08:00
|
|
|
struct xfrm_policy_hash *htab;
|
|
|
|
|
2008-11-26 09:24:15 +08:00
|
|
|
net->xfrm.policy_count[dir] = 0;
|
2014-11-13 17:09:49 +08:00
|
|
|
net->xfrm.policy_count[XFRM_POLICY_MAX + dir] = 0;
|
2008-11-26 09:23:26 +08:00
|
|
|
INIT_HLIST_HEAD(&net->xfrm.policy_inexact[dir]);
|
2006-08-24 19:45:07 +08:00
|
|
|
|
2008-11-26 09:23:48 +08:00
|
|
|
htab = &net->xfrm.policy_bydst[dir];
|
2006-08-24 19:50:50 +08:00
|
|
|
htab->table = xfrm_hash_alloc(sz);
|
2006-08-24 19:45:07 +08:00
|
|
|
if (!htab->table)
|
2008-11-26 09:23:48 +08:00
|
|
|
goto out_bydst;
|
|
|
|
htab->hmask = hmask;
|
xfrm: hash prefixed policies based on preflen thresholds
The idea is an extension of the current policy hashing.
Today only non-prefixed policies are stored in a hash table. This
patch relaxes the constraints, and hashes policies whose prefix
lengths are greater or equal to a configurable threshold.
Each hash table (one per direction) maintains its own set of IPv4 and
IPv6 thresholds (dbits4, sbits4, dbits6, sbits6), by default (32, 32,
128, 128).
Example, if the output hash table is configured with values (16, 24,
56, 64):
ip xfrm policy add dir out src 10.22.0.0/20 dst 10.24.1.0/24 ... => hashed
ip xfrm policy add dir out src 10.22.0.0/16 dst 10.24.1.1/32 ... => hashed
ip xfrm policy add dir out src 10.22.0.0/16 dst 10.24.0.0/16 ... => unhashed
ip xfrm policy add dir out \
src 3ffe:304:124:2200::/60 dst 3ffe:304:124:2401::/64 ... => hashed
ip xfrm policy add dir out \
src 3ffe:304:124:2200::/56 dst 3ffe:304:124:2401::2/128 ... => hashed
ip xfrm policy add dir out \
src 3ffe:304:124:2200::/56 dst 3ffe:304:124:2400::/56 ... => unhashed
The high order bits of the addresses (up to the threshold) are used to
compute the hash key.
Signed-off-by: Christophe Gouault <christophe.gouault@6wind.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
2014-08-29 22:16:04 +08:00
|
|
|
htab->dbits4 = 32;
|
|
|
|
htab->sbits4 = 32;
|
|
|
|
htab->dbits6 = 128;
|
|
|
|
htab->sbits6 = 128;
|
2006-08-24 19:45:07 +08:00
|
|
|
}
|
xfrm: configure policy hash table thresholds by netlink
Enable to specify local and remote prefix length thresholds for the
policy hash table via a netlink XFRM_MSG_NEWSPDINFO message.
prefix length thresholds are specified by XFRMA_SPD_IPV4_HTHRESH and
XFRMA_SPD_IPV6_HTHRESH optional attributes (struct xfrmu_spdhthresh).
example:
struct xfrmu_spdhthresh thresh4 = {
.lbits = 0;
.rbits = 24;
};
struct xfrmu_spdhthresh thresh6 = {
.lbits = 0;
.rbits = 56;
};
struct nlmsghdr *hdr;
struct nl_msg *msg;
msg = nlmsg_alloc();
hdr = nlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, XFRMA_SPD_IPV4_HTHRESH, sizeof(__u32), NLM_F_REQUEST);
nla_put(msg, XFRMA_SPD_IPV4_HTHRESH, sizeof(thresh4), &thresh4);
nla_put(msg, XFRMA_SPD_IPV6_HTHRESH, sizeof(thresh6), &thresh6);
nla_send_auto(sk, msg);
The numbers are the policy selector minimum prefix lengths to put a
policy in the hash table.
- lbits is the local threshold (source address for out policies,
destination address for in and fwd policies).
- rbits is the remote threshold (destination address for out
policies, source address for in and fwd policies).
The default values are:
XFRMA_SPD_IPV4_HTHRESH: 32 32
XFRMA_SPD_IPV6_HTHRESH: 128 128
Dynamic re-building of the SPD is performed when the thresholds values
are changed.
The current thresholds can be read via a XFRM_MSG_GETSPDINFO request:
the kernel replies to XFRM_MSG_GETSPDINFO requests by an
XFRM_MSG_NEWSPDINFO message, with both attributes
XFRMA_SPD_IPV4_HTHRESH and XFRMA_SPD_IPV6_HTHRESH.
Signed-off-by: Christophe Gouault <christophe.gouault@6wind.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
2014-08-29 22:16:05 +08:00
|
|
|
net->xfrm.policy_hthresh.lbits4 = 32;
|
|
|
|
net->xfrm.policy_hthresh.rbits4 = 32;
|
|
|
|
net->xfrm.policy_hthresh.lbits6 = 128;
|
|
|
|
net->xfrm.policy_hthresh.rbits6 = 128;
|
|
|
|
|
|
|
|
seqlock_init(&net->xfrm.policy_hthresh.lock);
|
2006-08-24 19:45:07 +08:00
|
|
|
|
2008-11-26 09:22:11 +08:00
|
|
|
INIT_LIST_HEAD(&net->xfrm.policy_all);
|
2008-11-26 09:28:57 +08:00
|
|
|
INIT_WORK(&net->xfrm.policy_hash_work, xfrm_hash_resize);
|
xfrm: configure policy hash table thresholds by netlink
Enable to specify local and remote prefix length thresholds for the
policy hash table via a netlink XFRM_MSG_NEWSPDINFO message.
prefix length thresholds are specified by XFRMA_SPD_IPV4_HTHRESH and
XFRMA_SPD_IPV6_HTHRESH optional attributes (struct xfrmu_spdhthresh).
example:
struct xfrmu_spdhthresh thresh4 = {
.lbits = 0;
.rbits = 24;
};
struct xfrmu_spdhthresh thresh6 = {
.lbits = 0;
.rbits = 56;
};
struct nlmsghdr *hdr;
struct nl_msg *msg;
msg = nlmsg_alloc();
hdr = nlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, XFRMA_SPD_IPV4_HTHRESH, sizeof(__u32), NLM_F_REQUEST);
nla_put(msg, XFRMA_SPD_IPV4_HTHRESH, sizeof(thresh4), &thresh4);
nla_put(msg, XFRMA_SPD_IPV6_HTHRESH, sizeof(thresh6), &thresh6);
nla_send_auto(sk, msg);
The numbers are the policy selector minimum prefix lengths to put a
policy in the hash table.
- lbits is the local threshold (source address for out policies,
destination address for in and fwd policies).
- rbits is the remote threshold (destination address for out
policies, source address for in and fwd policies).
The default values are:
XFRMA_SPD_IPV4_HTHRESH: 32 32
XFRMA_SPD_IPV6_HTHRESH: 128 128
Dynamic re-building of the SPD is performed when the thresholds values
are changed.
The current thresholds can be read via a XFRM_MSG_GETSPDINFO request:
the kernel replies to XFRM_MSG_GETSPDINFO requests by an
XFRM_MSG_NEWSPDINFO message, with both attributes
XFRMA_SPD_IPV4_HTHRESH and XFRMA_SPD_IPV6_HTHRESH.
Signed-off-by: Christophe Gouault <christophe.gouault@6wind.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
2014-08-29 22:16:05 +08:00
|
|
|
INIT_WORK(&net->xfrm.policy_hthresh.work, xfrm_hash_rebuild);
|
2008-11-26 09:14:31 +08:00
|
|
|
return 0;
|
2008-11-26 09:22:35 +08:00
|
|
|
|
2008-11-26 09:23:48 +08:00
|
|
|
out_bydst:
|
|
|
|
for (dir--; dir >= 0; dir--) {
|
|
|
|
struct xfrm_policy_hash *htab;
|
|
|
|
|
|
|
|
htab = &net->xfrm.policy_bydst[dir];
|
|
|
|
xfrm_hash_free(htab->table, sz);
|
|
|
|
}
|
|
|
|
xfrm_hash_free(net->xfrm.policy_byidx, sz);
|
2008-11-26 09:22:35 +08:00
|
|
|
out_byidx:
|
|
|
|
return -ENOMEM;
|
2008-11-26 09:14:31 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void xfrm_policy_fini(struct net *net)
|
|
|
|
{
|
2008-11-26 09:22:35 +08:00
|
|
|
unsigned int sz;
|
2008-11-26 09:23:26 +08:00
|
|
|
int dir;
|
2008-11-26 09:22:35 +08:00
|
|
|
|
2008-11-26 09:57:44 +08:00
|
|
|
flush_work(&net->xfrm.policy_hash_work);
|
|
|
|
#ifdef CONFIG_XFRM_SUB_POLICY
|
2014-04-22 20:48:30 +08:00
|
|
|
xfrm_policy_flush(net, XFRM_POLICY_TYPE_SUB, false);
|
2008-11-26 09:57:44 +08:00
|
|
|
#endif
|
2014-04-22 20:48:30 +08:00
|
|
|
xfrm_policy_flush(net, XFRM_POLICY_TYPE_MAIN, false);
|
2008-11-26 09:57:44 +08:00
|
|
|
|
2008-11-26 09:22:11 +08:00
|
|
|
WARN_ON(!list_empty(&net->xfrm.policy_all));
|
2008-11-26 09:22:35 +08:00
|
|
|
|
2014-11-13 17:09:49 +08:00
|
|
|
for (dir = 0; dir < XFRM_POLICY_MAX; dir++) {
|
2008-11-26 09:23:48 +08:00
|
|
|
struct xfrm_policy_hash *htab;
|
|
|
|
|
2008-11-26 09:23:26 +08:00
|
|
|
WARN_ON(!hlist_empty(&net->xfrm.policy_inexact[dir]));
|
2008-11-26 09:23:48 +08:00
|
|
|
|
|
|
|
htab = &net->xfrm.policy_bydst[dir];
|
2013-01-18 23:03:48 +08:00
|
|
|
sz = (htab->hmask + 1) * sizeof(struct hlist_head);
|
2008-11-26 09:23:48 +08:00
|
|
|
WARN_ON(!hlist_empty(htab->table));
|
|
|
|
xfrm_hash_free(htab->table, sz);
|
2008-11-26 09:23:26 +08:00
|
|
|
}
|
|
|
|
|
2008-11-26 09:22:58 +08:00
|
|
|
sz = (net->xfrm.policy_idx_hmask + 1) * sizeof(struct hlist_head);
|
2008-11-26 09:22:35 +08:00
|
|
|
WARN_ON(!hlist_empty(net->xfrm.policy_byidx));
|
|
|
|
xfrm_hash_free(net->xfrm.policy_byidx, sz);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2008-11-26 09:14:31 +08:00
|
|
|
static int __net_init xfrm_net_init(struct net *net)
|
|
|
|
{
|
|
|
|
int rv;
|
|
|
|
|
2017-02-08 18:52:29 +08:00
|
|
|
/* Initialize the per-net locks here */
|
|
|
|
spin_lock_init(&net->xfrm.xfrm_state_lock);
|
|
|
|
spin_lock_init(&net->xfrm.xfrm_policy_lock);
|
|
|
|
mutex_init(&net->xfrm.xfrm_cfg_mutex);
|
|
|
|
|
2008-11-26 09:59:52 +08:00
|
|
|
rv = xfrm_statistics_init(net);
|
|
|
|
if (rv < 0)
|
|
|
|
goto out_statistics;
|
2008-11-26 09:14:31 +08:00
|
|
|
rv = xfrm_state_init(net);
|
|
|
|
if (rv < 0)
|
|
|
|
goto out_state;
|
|
|
|
rv = xfrm_policy_init(net);
|
|
|
|
if (rv < 0)
|
|
|
|
goto out_policy;
|
2008-11-26 10:00:48 +08:00
|
|
|
rv = xfrm_sysctl_init(net);
|
|
|
|
if (rv < 0)
|
|
|
|
goto out_sysctl;
|
2013-11-07 17:47:50 +08:00
|
|
|
|
2008-11-26 09:14:31 +08:00
|
|
|
return 0;
|
|
|
|
|
2008-11-26 10:00:48 +08:00
|
|
|
out_sysctl:
|
|
|
|
xfrm_policy_fini(net);
|
2008-11-26 09:14:31 +08:00
|
|
|
out_policy:
|
|
|
|
xfrm_state_fini(net);
|
|
|
|
out_state:
|
2008-11-26 09:59:52 +08:00
|
|
|
xfrm_statistics_fini(net);
|
|
|
|
out_statistics:
|
2008-11-26 09:14:31 +08:00
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __net_exit xfrm_net_exit(struct net *net)
|
|
|
|
{
|
2008-11-26 10:00:48 +08:00
|
|
|
xfrm_sysctl_fini(net);
|
2008-11-26 09:14:31 +08:00
|
|
|
xfrm_policy_fini(net);
|
|
|
|
xfrm_state_fini(net);
|
2008-11-26 09:59:52 +08:00
|
|
|
xfrm_statistics_fini(net);
|
2008-11-26 09:14:31 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct pernet_operations __net_initdata xfrm_net_ops = {
|
|
|
|
.init = xfrm_net_init,
|
|
|
|
.exit = xfrm_net_exit,
|
|
|
|
};
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
void __init xfrm_init(void)
|
|
|
|
{
|
2017-07-17 19:57:27 +08:00
|
|
|
int i;
|
|
|
|
|
|
|
|
xfrm_pcpu_work = kmalloc_array(NR_CPUS, sizeof(*xfrm_pcpu_work),
|
|
|
|
GFP_KERNEL);
|
|
|
|
BUG_ON(!xfrm_pcpu_work);
|
|
|
|
|
|
|
|
for (i = 0; i < NR_CPUS; i++)
|
|
|
|
INIT_WORK(&xfrm_pcpu_work[i], xfrm_pcpu_work_fn);
|
|
|
|
|
2008-11-26 09:14:31 +08:00
|
|
|
register_pernet_subsys(&xfrm_net_ops);
|
2018-03-29 22:03:25 +08:00
|
|
|
xfrm_dev_init();
|
2016-08-11 21:17:54 +08:00
|
|
|
seqcount_init(&xfrm_policy_hash_generation);
|
2005-04-17 06:20:36 +08:00
|
|
|
xfrm_input_init();
|
|
|
|
}
|
|
|
|
|
2007-09-18 02:51:22 +08:00
|
|
|
#ifdef CONFIG_AUDITSYSCALL
|
2008-01-12 19:20:03 +08:00
|
|
|
static void xfrm_audit_common_policyinfo(struct xfrm_policy *xp,
|
|
|
|
struct audit_buffer *audit_buf)
|
2007-09-18 02:51:22 +08:00
|
|
|
{
|
2007-12-01 20:27:18 +08:00
|
|
|
struct xfrm_sec_ctx *ctx = xp->security;
|
|
|
|
struct xfrm_selector *sel = &xp->selector;
|
|
|
|
|
|
|
|
if (ctx)
|
2007-09-18 02:51:22 +08:00
|
|
|
audit_log_format(audit_buf, " sec_alg=%u sec_doi=%u sec_obj=%s",
|
2007-12-01 20:27:18 +08:00
|
|
|
ctx->ctx_alg, ctx->ctx_doi, ctx->ctx_str);
|
2007-09-18 02:51:22 +08:00
|
|
|
|
2013-12-24 09:43:46 +08:00
|
|
|
switch (sel->family) {
|
2007-09-18 02:51:22 +08:00
|
|
|
case AF_INET:
|
2008-10-31 15:54:56 +08:00
|
|
|
audit_log_format(audit_buf, " src=%pI4", &sel->saddr.a4);
|
2007-12-01 20:27:18 +08:00
|
|
|
if (sel->prefixlen_s != 32)
|
|
|
|
audit_log_format(audit_buf, " src_prefixlen=%d",
|
|
|
|
sel->prefixlen_s);
|
2008-10-31 15:54:56 +08:00
|
|
|
audit_log_format(audit_buf, " dst=%pI4", &sel->daddr.a4);
|
2007-12-01 20:27:18 +08:00
|
|
|
if (sel->prefixlen_d != 32)
|
|
|
|
audit_log_format(audit_buf, " dst_prefixlen=%d",
|
|
|
|
sel->prefixlen_d);
|
2007-09-18 02:51:22 +08:00
|
|
|
break;
|
|
|
|
case AF_INET6:
|
2008-10-30 03:52:50 +08:00
|
|
|
audit_log_format(audit_buf, " src=%pI6", sel->saddr.a6);
|
2007-12-01 20:27:18 +08:00
|
|
|
if (sel->prefixlen_s != 128)
|
|
|
|
audit_log_format(audit_buf, " src_prefixlen=%d",
|
|
|
|
sel->prefixlen_s);
|
2008-10-30 03:52:50 +08:00
|
|
|
audit_log_format(audit_buf, " dst=%pI6", sel->daddr.a6);
|
2007-12-01 20:27:18 +08:00
|
|
|
if (sel->prefixlen_d != 128)
|
|
|
|
audit_log_format(audit_buf, " dst_prefixlen=%d",
|
|
|
|
sel->prefixlen_d);
|
2007-09-18 02:51:22 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-22 20:48:30 +08:00
|
|
|
void xfrm_audit_policy_add(struct xfrm_policy *xp, int result, bool task_valid)
|
2007-09-18 02:51:22 +08:00
|
|
|
{
|
|
|
|
struct audit_buffer *audit_buf;
|
|
|
|
|
2007-12-22 06:58:11 +08:00
|
|
|
audit_buf = xfrm_audit_start("SPD-add");
|
2007-09-18 02:51:22 +08:00
|
|
|
if (audit_buf == NULL)
|
|
|
|
return;
|
2014-04-22 20:48:30 +08:00
|
|
|
xfrm_audit_helper_usrinfo(task_valid, audit_buf);
|
2007-12-22 06:58:11 +08:00
|
|
|
audit_log_format(audit_buf, " res=%u", result);
|
2007-09-18 02:51:22 +08:00
|
|
|
xfrm_audit_common_policyinfo(xp, audit_buf);
|
|
|
|
audit_log_end(audit_buf);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(xfrm_audit_policy_add);
|
|
|
|
|
2007-12-21 12:49:33 +08:00
|
|
|
void xfrm_audit_policy_delete(struct xfrm_policy *xp, int result,
|
2014-04-22 20:48:30 +08:00
|
|
|
bool task_valid)
|
2007-09-18 02:51:22 +08:00
|
|
|
{
|
|
|
|
struct audit_buffer *audit_buf;
|
|
|
|
|
2007-12-22 06:58:11 +08:00
|
|
|
audit_buf = xfrm_audit_start("SPD-delete");
|
2007-09-18 02:51:22 +08:00
|
|
|
if (audit_buf == NULL)
|
|
|
|
return;
|
2014-04-22 20:48:30 +08:00
|
|
|
xfrm_audit_helper_usrinfo(task_valid, audit_buf);
|
2007-12-22 06:58:11 +08:00
|
|
|
audit_log_format(audit_buf, " res=%u", result);
|
2007-09-18 02:51:22 +08:00
|
|
|
xfrm_audit_common_policyinfo(xp, audit_buf);
|
|
|
|
audit_log_end(audit_buf);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(xfrm_audit_policy_delete);
|
|
|
|
#endif
|
|
|
|
|
2007-02-09 05:11:42 +08:00
|
|
|
#ifdef CONFIG_XFRM_MIGRATE
|
2012-05-16 03:04:57 +08:00
|
|
|
static bool xfrm_migrate_selector_match(const struct xfrm_selector *sel_cmp,
|
|
|
|
const struct xfrm_selector *sel_tgt)
|
2007-02-09 05:11:42 +08:00
|
|
|
{
|
|
|
|
if (sel_cmp->proto == IPSEC_ULPROTO_ANY) {
|
|
|
|
if (sel_tgt->family == sel_cmp->family &&
|
2013-01-29 20:48:50 +08:00
|
|
|
xfrm_addr_equal(&sel_tgt->daddr, &sel_cmp->daddr,
|
|
|
|
sel_cmp->family) &&
|
|
|
|
xfrm_addr_equal(&sel_tgt->saddr, &sel_cmp->saddr,
|
|
|
|
sel_cmp->family) &&
|
2007-02-09 05:11:42 +08:00
|
|
|
sel_tgt->prefixlen_d == sel_cmp->prefixlen_d &&
|
|
|
|
sel_tgt->prefixlen_s == sel_cmp->prefixlen_s) {
|
2012-05-16 03:04:57 +08:00
|
|
|
return true;
|
2007-02-09 05:11:42 +08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (memcmp(sel_tgt, sel_cmp, sizeof(*sel_tgt)) == 0) {
|
2012-05-16 03:04:57 +08:00
|
|
|
return true;
|
2007-02-09 05:11:42 +08:00
|
|
|
}
|
|
|
|
}
|
2012-05-16 03:04:57 +08:00
|
|
|
return false;
|
2007-02-09 05:11:42 +08:00
|
|
|
}
|
|
|
|
|
2013-12-24 09:43:47 +08:00
|
|
|
static struct xfrm_policy *xfrm_migrate_policy_find(const struct xfrm_selector *sel,
|
|
|
|
u8 dir, u8 type, struct net *net)
|
2007-02-09 05:11:42 +08:00
|
|
|
{
|
|
|
|
struct xfrm_policy *pol, *ret = NULL;
|
|
|
|
struct hlist_head *chain;
|
|
|
|
u32 priority = ~0U;
|
|
|
|
|
2016-08-11 21:17:59 +08:00
|
|
|
spin_lock_bh(&net->xfrm.xfrm_policy_lock);
|
2013-11-07 17:47:49 +08:00
|
|
|
chain = policy_hash_direct(net, &sel->daddr, &sel->saddr, sel->family, dir);
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 09:06:00 +08:00
|
|
|
hlist_for_each_entry(pol, chain, bydst) {
|
2007-02-09 05:11:42 +08:00
|
|
|
if (xfrm_migrate_selector_match(sel, &pol->selector) &&
|
|
|
|
pol->type == type) {
|
|
|
|
ret = pol;
|
|
|
|
priority = ret->priority;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2013-11-07 17:47:49 +08:00
|
|
|
chain = &net->xfrm.policy_inexact[dir];
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 09:06:00 +08:00
|
|
|
hlist_for_each_entry(pol, chain, bydst) {
|
2015-05-14 11:16:59 +08:00
|
|
|
if ((pol->priority >= priority) && ret)
|
|
|
|
break;
|
|
|
|
|
2007-02-09 05:11:42 +08:00
|
|
|
if (xfrm_migrate_selector_match(sel, &pol->selector) &&
|
2015-05-14 11:16:59 +08:00
|
|
|
pol->type == type) {
|
2007-02-09 05:11:42 +08:00
|
|
|
ret = pol;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-30 17:13:41 +08:00
|
|
|
xfrm_pol_hold(ret);
|
2007-02-09 05:11:42 +08:00
|
|
|
|
2016-08-11 21:17:59 +08:00
|
|
|
spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
|
2007-02-09 05:11:42 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-02-24 13:21:08 +08:00
|
|
|
static int migrate_tmpl_match(const struct xfrm_migrate *m, const struct xfrm_tmpl *t)
|
2007-02-09 05:11:42 +08:00
|
|
|
{
|
|
|
|
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:
|
2013-01-29 20:48:50 +08:00
|
|
|
if (xfrm_addr_equal(&t->id.daddr, &m->old_daddr,
|
|
|
|
m->old_family) &&
|
|
|
|
xfrm_addr_equal(&t->saddr, &m->old_saddr,
|
|
|
|
m->old_family)) {
|
2007-02-09 05:11:42 +08:00
|
|
|
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;
|
|
|
|
int i, j, n = 0;
|
|
|
|
|
|
|
|
write_lock_bh(&pol->lock);
|
2008-10-01 22:03:24 +08:00
|
|
|
if (unlikely(pol->walk.dead)) {
|
2007-02-09 05:11:42 +08:00
|
|
|
/* 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++;
|
2007-10-18 12:31:50 +08:00
|
|
|
if (pol->xfrm_vec[i].mode != XFRM_MODE_TUNNEL &&
|
|
|
|
pol->xfrm_vec[i].mode != XFRM_MODE_BEET)
|
2007-02-09 05:11:42 +08:00
|
|
|
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 */
|
2010-04-07 08:30:05 +08:00
|
|
|
atomic_inc(&pol->genid);
|
2007-02-09 05:11:42 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
write_unlock_bh(&pol->lock);
|
|
|
|
|
|
|
|
if (!n)
|
|
|
|
return -ENODATA;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-02-24 13:21:08 +08:00
|
|
|
static int xfrm_migrate_check(const struct xfrm_migrate *m, int num_migrate)
|
2007-02-09 05:11:42 +08:00
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
if (num_migrate < 1 || num_migrate > XFRM_MAX_DEPTH)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
for (i = 0; i < num_migrate; i++) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2011-02-24 13:35:06 +08:00
|
|
|
int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
|
2008-10-06 04:33:42 +08:00
|
|
|
struct xfrm_migrate *m, int num_migrate,
|
2017-06-06 18:12:13 +08:00
|
|
|
struct xfrm_kmaddress *k, struct net *net,
|
|
|
|
struct xfrm_encap_tmpl *encap)
|
2007-02-09 05:11:42 +08:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
2017-08-03 01:50:14 +08:00
|
|
|
/* Stage 0 - sanity checks */
|
2007-02-09 05:11:42 +08:00
|
|
|
if ((err = xfrm_migrate_check(m, num_migrate)) < 0)
|
|
|
|
goto out;
|
|
|
|
|
2017-08-03 01:50:14 +08:00
|
|
|
if (dir >= XFRM_POLICY_MAX) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2007-02-09 05:11:42 +08:00
|
|
|
/* Stage 1 - find policy */
|
2013-11-07 17:47:49 +08:00
|
|
|
if ((pol = xfrm_migrate_policy_find(sel, dir, type, net)) == NULL) {
|
2007-02-09 05:11:42 +08:00
|
|
|
err = -ENOENT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Stage 2 - find and update state(s) */
|
|
|
|
for (i = 0, mp = m; i < num_migrate; i++, mp++) {
|
2013-11-07 17:47:50 +08:00
|
|
|
if ((x = xfrm_migrate_state_find(mp, net))) {
|
2007-02-09 05:11:42 +08:00
|
|
|
x_cur[nx_cur] = x;
|
|
|
|
nx_cur++;
|
2017-06-06 18:12:13 +08:00
|
|
|
xc = xfrm_state_migrate(x, mp, encap);
|
|
|
|
if (xc) {
|
2007-02-09 05:11:42 +08:00
|
|
|
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 */
|
2017-06-06 18:12:14 +08:00
|
|
|
km_migrate(sel, dir, type, m, num_migrate, k, encap);
|
2007-02-09 05:11:42 +08:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2007-02-09 05:29:15 +08:00
|
|
|
EXPORT_SYMBOL(xfrm_migrate);
|
2007-02-09 05:11:42 +08:00
|
|
|
#endif
|