Commit aabc9761 authored by Herbert Xu's avatar Herbert Xu Committed by David S. Miller

[IPSEC]: Store idev entries

I found a bug that stopped IPsec/IPv6 from working.  About
a month ago IPv6 started using rt6i_idev->dev on the cached socket dst
entries.  If the cached socket dst entry is IPsec, then rt6i_idev will
be NULL.

Since we want to look at the rt6i_idev of the original route in this
case, the easiest fix is to store rt6i_idev in the IPsec dst entry just
as we do for a number of other IPv6 route attributes.  Unfortunately
this means that we need some new code to handle the references to
rt6i_idev.  That's why this patch is bigger than it would otherwise be.

I've also done the same thing for IPv4 since it is conceivable that
once these idev attributes start getting used for accounting, we
probably need to dereference them for IPv4 IPsec entries too.
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent d5d75cd6
#ifndef _NET_XFRM_H #ifndef _NET_XFRM_H
#define _NET_XFRM_H #define _NET_XFRM_H
#include <linux/compiler.h>
#include <linux/xfrm.h> #include <linux/xfrm.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/list.h> #include <linux/list.h>
...@@ -516,6 +517,15 @@ struct xfrm_dst ...@@ -516,6 +517,15 @@ struct xfrm_dst
u32 child_mtu_cached; u32 child_mtu_cached;
}; };
static inline void xfrm_dst_destroy(struct xfrm_dst *xdst)
{
dst_release(xdst->route);
if (likely(xdst->u.dst.xfrm))
xfrm_state_put(xdst->u.dst.xfrm);
}
extern void xfrm_dst_ifdown(struct dst_entry *dst, struct net_device *dev);
/* Decapsulation state, used by the input to store data during /* Decapsulation state, used by the input to store data during
* decapsulation procedure, to be used later (during the policy * decapsulation procedure, to be used later (during the policy
* check * check
......
...@@ -8,7 +8,10 @@ ...@@ -8,7 +8,10 @@
* *
*/ */
#include <asm/bug.h>
#include <linux/compiler.h>
#include <linux/config.h> #include <linux/config.h>
#include <linux/inetdevice.h>
#include <net/xfrm.h> #include <net/xfrm.h>
#include <net/ip.h> #include <net/ip.h>
...@@ -152,6 +155,8 @@ __xfrm4_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int ...@@ -152,6 +155,8 @@ __xfrm4_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int
x->u.rt.rt_dst = rt0->rt_dst; x->u.rt.rt_dst = rt0->rt_dst;
x->u.rt.rt_gateway = rt->rt_gateway; x->u.rt.rt_gateway = rt->rt_gateway;
x->u.rt.rt_spec_dst = rt0->rt_spec_dst; x->u.rt.rt_spec_dst = rt0->rt_spec_dst;
x->u.rt.idev = rt0->idev;
in_dev_hold(rt0->idev);
header_len -= x->u.dst.xfrm->props.header_len; header_len -= x->u.dst.xfrm->props.header_len;
trailer_len -= x->u.dst.xfrm->props.trailer_len; trailer_len -= x->u.dst.xfrm->props.trailer_len;
} }
...@@ -243,11 +248,48 @@ static void xfrm4_update_pmtu(struct dst_entry *dst, u32 mtu) ...@@ -243,11 +248,48 @@ static void xfrm4_update_pmtu(struct dst_entry *dst, u32 mtu)
path->ops->update_pmtu(path, mtu); path->ops->update_pmtu(path, mtu);
} }
static void xfrm4_dst_destroy(struct dst_entry *dst)
{
struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
if (likely(xdst->u.rt.idev))
in_dev_put(xdst->u.rt.idev);
xfrm_dst_destroy(xdst);
}
static void xfrm4_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
int unregister)
{
struct xfrm_dst *xdst;
if (!unregister)
return;
xdst = (struct xfrm_dst *)dst;
if (xdst->u.rt.idev->dev == dev) {
struct in_device *loopback_idev = in_dev_get(&loopback_dev);
BUG_ON(!loopback_idev);
do {
in_dev_put(xdst->u.rt.idev);
xdst->u.rt.idev = loopback_idev;
in_dev_hold(loopback_idev);
xdst = (struct xfrm_dst *)xdst->u.dst.child;
} while (xdst->u.dst.xfrm);
__in_dev_put(loopback_idev);
}
xfrm_dst_ifdown(dst, dev);
}
static struct dst_ops xfrm4_dst_ops = { static struct dst_ops xfrm4_dst_ops = {
.family = AF_INET, .family = AF_INET,
.protocol = __constant_htons(ETH_P_IP), .protocol = __constant_htons(ETH_P_IP),
.gc = xfrm4_garbage_collect, .gc = xfrm4_garbage_collect,
.update_pmtu = xfrm4_update_pmtu, .update_pmtu = xfrm4_update_pmtu,
.destroy = xfrm4_dst_destroy,
.ifdown = xfrm4_dst_ifdown,
.gc_thresh = 1024, .gc_thresh = 1024,
.entry_size = sizeof(struct xfrm_dst), .entry_size = sizeof(struct xfrm_dst),
}; };
......
...@@ -11,7 +11,11 @@ ...@@ -11,7 +11,11 @@
* *
*/ */
#include <asm/bug.h>
#include <linux/compiler.h>
#include <linux/config.h> #include <linux/config.h>
#include <linux/netdevice.h>
#include <net/addrconf.h>
#include <net/xfrm.h> #include <net/xfrm.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/ipv6.h> #include <net/ipv6.h>
...@@ -166,6 +170,8 @@ __xfrm6_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int ...@@ -166,6 +170,8 @@ __xfrm6_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int
memcpy(&x->u.rt6.rt6i_gateway, &rt0->rt6i_gateway, sizeof(x->u.rt6.rt6i_gateway)); memcpy(&x->u.rt6.rt6i_gateway, &rt0->rt6i_gateway, sizeof(x->u.rt6.rt6i_gateway));
x->u.rt6.rt6i_dst = rt0->rt6i_dst; x->u.rt6.rt6i_dst = rt0->rt6i_dst;
x->u.rt6.rt6i_src = rt0->rt6i_src; x->u.rt6.rt6i_src = rt0->rt6i_src;
x->u.rt6.rt6i_idev = rt0->rt6i_idev;
in6_dev_hold(rt0->rt6i_idev);
header_len -= x->u.dst.xfrm->props.header_len; header_len -= x->u.dst.xfrm->props.header_len;
trailer_len -= x->u.dst.xfrm->props.trailer_len; trailer_len -= x->u.dst.xfrm->props.trailer_len;
} }
...@@ -251,11 +257,48 @@ static void xfrm6_update_pmtu(struct dst_entry *dst, u32 mtu) ...@@ -251,11 +257,48 @@ static void xfrm6_update_pmtu(struct dst_entry *dst, u32 mtu)
path->ops->update_pmtu(path, mtu); path->ops->update_pmtu(path, mtu);
} }
static void xfrm6_dst_destroy(struct dst_entry *dst)
{
struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
if (likely(xdst->u.rt6.rt6i_idev))
in6_dev_put(xdst->u.rt6.rt6i_idev);
xfrm_dst_destroy(xdst);
}
static void xfrm6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
int unregister)
{
struct xfrm_dst *xdst;
if (!unregister)
return;
xdst = (struct xfrm_dst *)dst;
if (xdst->u.rt6.rt6i_idev->dev == dev) {
struct inet6_dev *loopback_idev = in6_dev_get(&loopback_dev);
BUG_ON(!loopback_idev);
do {
in6_dev_put(xdst->u.rt6.rt6i_idev);
xdst->u.rt6.rt6i_idev = loopback_idev;
in6_dev_hold(loopback_idev);
xdst = (struct xfrm_dst *)xdst->u.dst.child;
} while (xdst->u.dst.xfrm);
__in6_dev_put(loopback_idev);
}
xfrm_dst_ifdown(dst, dev);
}
static struct dst_ops xfrm6_dst_ops = { static struct dst_ops xfrm6_dst_ops = {
.family = AF_INET6, .family = AF_INET6,
.protocol = __constant_htons(ETH_P_IPV6), .protocol = __constant_htons(ETH_P_IPV6),
.gc = xfrm6_garbage_collect, .gc = xfrm6_garbage_collect,
.update_pmtu = xfrm6_update_pmtu, .update_pmtu = xfrm6_update_pmtu,
.destroy = xfrm6_dst_destroy,
.ifdown = xfrm6_dst_ifdown,
.gc_thresh = 1024, .gc_thresh = 1024,
.entry_size = sizeof(struct xfrm_dst), .entry_size = sizeof(struct xfrm_dst),
}; };
......
...@@ -1028,30 +1028,15 @@ static int stale_bundle(struct dst_entry *dst) ...@@ -1028,30 +1028,15 @@ static int stale_bundle(struct dst_entry *dst)
return !xfrm_bundle_ok((struct xfrm_dst *)dst, NULL, AF_UNSPEC); return !xfrm_bundle_ok((struct xfrm_dst *)dst, NULL, AF_UNSPEC);
} }
static void xfrm_dst_destroy(struct dst_entry *dst) void xfrm_dst_ifdown(struct dst_entry *dst, struct net_device *dev)
{ {
struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
dst_release(xdst->route);
if (!dst->xfrm)
return;
xfrm_state_put(dst->xfrm);
dst->xfrm = NULL;
}
static void xfrm_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
int unregister)
{
if (!unregister)
return;
while ((dst = dst->child) && dst->xfrm && dst->dev == dev) { while ((dst = dst->child) && dst->xfrm && dst->dev == dev) {
dst->dev = &loopback_dev; dst->dev = &loopback_dev;
dev_hold(&loopback_dev); dev_hold(&loopback_dev);
dev_put(dev); dev_put(dev);
} }
} }
EXPORT_SYMBOL(xfrm_dst_ifdown);
static void xfrm_link_failure(struct sk_buff *skb) static void xfrm_link_failure(struct sk_buff *skb)
{ {
...@@ -1262,10 +1247,6 @@ int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo) ...@@ -1262,10 +1247,6 @@ int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo)
dst_ops->kmem_cachep = xfrm_dst_cache; dst_ops->kmem_cachep = xfrm_dst_cache;
if (likely(dst_ops->check == NULL)) if (likely(dst_ops->check == NULL))
dst_ops->check = xfrm_dst_check; dst_ops->check = xfrm_dst_check;
if (likely(dst_ops->destroy == NULL))
dst_ops->destroy = xfrm_dst_destroy;
if (likely(dst_ops->ifdown == NULL))
dst_ops->ifdown = xfrm_dst_ifdown;
if (likely(dst_ops->negative_advice == NULL)) if (likely(dst_ops->negative_advice == NULL))
dst_ops->negative_advice = xfrm_negative_advice; dst_ops->negative_advice = xfrm_negative_advice;
if (likely(dst_ops->link_failure == NULL)) if (likely(dst_ops->link_failure == NULL))
...@@ -1297,8 +1278,6 @@ int xfrm_policy_unregister_afinfo(struct xfrm_policy_afinfo *afinfo) ...@@ -1297,8 +1278,6 @@ int xfrm_policy_unregister_afinfo(struct xfrm_policy_afinfo *afinfo)
xfrm_policy_afinfo[afinfo->family] = NULL; xfrm_policy_afinfo[afinfo->family] = NULL;
dst_ops->kmem_cachep = NULL; dst_ops->kmem_cachep = NULL;
dst_ops->check = NULL; dst_ops->check = NULL;
dst_ops->destroy = NULL;
dst_ops->ifdown = NULL;
dst_ops->negative_advice = NULL; dst_ops->negative_advice = NULL;
dst_ops->link_failure = NULL; dst_ops->link_failure = NULL;
dst_ops->get_mss = NULL; dst_ops->get_mss = NULL;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment