diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 62486f8..3cbe4f8 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -48,6 +48,11 @@ #include #include "xfrm_hash.h" +#ifdef IPSEC_FLOW_CACHE +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) +#include "ipsec_flow.h" +#endif +#endif #define XFRM_QUEUE_TMO_MIN ((unsigned)(HZ/10)) #define XFRM_QUEUE_TMO_MAX ((unsigned)(60*HZ)) @@ -179,6 +184,15 @@ static struct xfrm_policy_afinfo const __rcu *xfrm_policy_afinfo[AF_INET6 + 1] static struct kmem_cache *xfrm_dst_cache __ro_after_init; +#ifdef IPSEC_FLOW_CACHE +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) +extern int ipsec_nlkey_flow(u16 xfrm_nr, u16 *xfrm_handle, + const struct flowi *fl, u16 family, u16 dir, u16 ignore_neigh); +int ipsec_flow_init(struct net *net); +void ipsec_flow_fini(struct net *net); +#endif +#endif + static struct rhashtable xfrm_policy_inexact_table; static const struct rhashtable_params xfrm_pol_inexact_params; @@ -2599,6 +2613,17 @@ static dscp_t xfrm_get_dscp(const struct flowi *fl, int family) return 0; } +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) +int xfrm_get_tos(const struct flowi *fl, int family) +{ + if (family == AF_INET) + return inet_dscp_to_dsfield(fl->u.ip4.flowi4_dscp) & INET_DSCP_MASK; + + return 0; +} +EXPORT_SYMBOL(xfrm_get_tos); +#endif + static inline struct xfrm_dst *xfrm_alloc_dst(struct net *net, int family) { const struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family); @@ -3295,6 +3320,37 @@ no_transform: dst = dst_orig; } +#ifdef IPSEC_FLOW_CACHE +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) + { + struct dst_entry *dst1 = dst; + struct xfrm_state *x; + u16 xfrm_handle[XFRM_POLICY_TYPE_MAX]; + u16 ignore_neigh = 0; + + num_xfrms = 0; + memset(xfrm_handle, 0, XFRM_POLICY_TYPE_MAX * sizeof(u16)); + while (((x = dst1->xfrm) != NULL) && + (num_xfrms < XFRM_POLICY_TYPE_MAX)) { + xfrm_handle[num_xfrms++] = x->handle; + if (x->props.mode == XFRM_MODE_TUNNEL) + ignore_neigh = 1; + dst1 = xfrm_dst_child(dst1); + + if (dst1 == NULL) { + err = -EHOSTUNREACH; + goto error; + } + } + if (ipsec_flow_add(net, fl, family, dir, xfrm_handle)) { + /* sent flow notification to cmm with sa_handle */ + ipsec_nlkey_flow(num_xfrms, xfrm_handle, fl, family, + (unsigned short)dir, ignore_neigh); + } + } +#endif +#endif + ok: xfrm_pols_put(pols, drop_pols); if (dst->xfrm && @@ -3853,6 +3909,34 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, goto reject; } +#ifdef IPSEC_FLOW_CACHE +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) + { + struct xfrm_state *x; + u16 xfrm_handle[XFRM_POLICY_TYPE_MAX]; + + xfrm_nr = 0; + memset(xfrm_handle, 0, XFRM_POLICY_TYPE_MAX * sizeof(u16)); + for (i = sp->len - 1; + (i >= 0) && (xfrm_nr < XFRM_POLICY_TYPE_MAX); i--) { + x = sp->xvec[i]; + xfrm_handle[xfrm_nr++] = x->handle; + } + if (ipsec_flow_add(net, (const struct flowi *)&fl, family, dir, + xfrm_handle)) { + /* sent flow notification to cmm with sa_handle */ + ipsec_nlkey_flow(xfrm_nr, xfrm_handle, + (const struct flowi *)&fl, family, dir, 0); + } + } + + /* Hub and spoke changes: Setting the POLICY_IN direction in the packet */ + skb->ipsec_xfrm_dir |= (1 << XFRM_POLICY_IN); + +std_path: +#endif +#endif + xfrm_pols_put(pols, npols); sp->verified_cnt = k; @@ -4328,6 +4412,14 @@ static int __net_init xfrm_net_init(struct net *net) if (rv < 0) goto out_sysctl; +#ifdef IPSEC_FLOW_CACHE +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) + rv = ipsec_flow_init(net); + if (rv < 0) + goto out_ipsec_flow; +#endif +#endif + rv = xfrm_nat_keepalive_net_init(net); if (rv < 0) goto out_nat_keepalive; @@ -4335,6 +4427,12 @@ static int __net_init xfrm_net_init(struct net *net) return 0; out_nat_keepalive: +#ifdef IPSEC_FLOW_CACHE +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) + ipsec_flow_fini(net); +out_ipsec_flow: +#endif +#endif xfrm_sysctl_fini(net); out_sysctl: xfrm_policy_fini(net); @@ -4349,6 +4447,11 @@ out_statistics: static void __net_exit xfrm_net_exit(struct net *net) { xfrm_nat_keepalive_net_fini(net); +#ifdef IPSEC_FLOW_CACHE +#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD) + ipsec_flow_fini(net); +#endif +#endif xfrm_sysctl_fini(net); xfrm_policy_fini(net); xfrm_state_fini(net);