Skip to content

Commit 607f725

Browse files
Paolo Abenidavem330
authored andcommitted
net: replace dst_cache ip6_tunnel implementation with the generic one
This also fix a potential race into the existing tunnel code, which could lead to the wrong dst to be permanenty cached: CPU1: CPU2: <xmit on ip6_tunnel> <cache lookup fails> dst = ip6_route_output(...) <tunnel params are changed via nl> dst_cache_reset() // no effect, // the cache is empty dst_cache_set() // the wrong dst // is permanenty stored // into the cache With the new dst implementation the above race is not possible since the first cache lookup after dst_cache_reset will fail due to the timestamp check Signed-off-by: Paolo Abeni <pabeni@redhat.com> Suggested-and-acked-by: Hannes Frederic Sowa <hannes@stressinduktion.org> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 911362c commit 607f725

File tree

5 files changed

+16
-116
lines changed

5 files changed

+16
-116
lines changed

include/net/ip6_tunnel.h

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <linux/if_tunnel.h>
77
#include <linux/ip6_tunnel.h>
88
#include <net/ip_tunnels.h>
9+
#include <net/dst_cache.h>
910

1011
#define IP6TUNNEL_ERR_TIMEO (30*HZ)
1112

@@ -33,20 +34,14 @@ struct __ip6_tnl_parm {
3334
__be32 o_key;
3435
};
3536

36-
struct ip6_tnl_dst {
37-
seqlock_t lock;
38-
struct dst_entry __rcu *dst;
39-
u32 cookie;
40-
};
41-
4237
/* IPv6 tunnel */
4338
struct ip6_tnl {
4439
struct ip6_tnl __rcu *next; /* next tunnel in list */
4540
struct net_device *dev; /* virtual device associated with tunnel */
4641
struct net *net; /* netns for packet i/o */
4742
struct __ip6_tnl_parm parms; /* tunnel configuration parameters */
4843
struct flowi fl; /* flowi template for xmit */
49-
struct ip6_tnl_dst __percpu *dst_cache; /* cached dst */
44+
struct dst_cache dst_cache; /* cached dst */
5045

5146
int err_count;
5247
unsigned long err_time;
@@ -66,11 +61,6 @@ struct ipv6_tlv_tnl_enc_lim {
6661
__u8 encap_limit; /* tunnel encapsulation limit */
6762
} __packed;
6863

69-
struct dst_entry *ip6_tnl_dst_get(struct ip6_tnl *t);
70-
int ip6_tnl_dst_init(struct ip6_tnl *t);
71-
void ip6_tnl_dst_destroy(struct ip6_tnl *t);
72-
void ip6_tnl_dst_reset(struct ip6_tnl *t);
73-
void ip6_tnl_dst_set(struct ip6_tnl *t, struct dst_entry *dst);
7464
int ip6_tnl_rcv_ctl(struct ip6_tnl *t, const struct in6_addr *laddr,
7565
const struct in6_addr *raddr);
7666
int ip6_tnl_xmit_ctl(struct ip6_tnl *t, const struct in6_addr *laddr,

net/ipv6/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ config IPV6_NDISC_NODETYPE
207207
config IPV6_TUNNEL
208208
tristate "IPv6: IP-in-IPv6 tunnel (RFC2473)"
209209
select INET6_TUNNEL
210+
select DST_CACHE
210211
---help---
211212
Support for IPv6-in-IPv6 and IPv4-in-IPv6 tunnels described in
212213
RFC 2473.

net/ipv6/ip6_gre.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ static void ip6gre_tunnel_uninit(struct net_device *dev)
360360
struct ip6gre_net *ign = net_generic(t->net, ip6gre_net_id);
361361

362362
ip6gre_tunnel_unlink(ign, t);
363-
ip6_tnl_dst_reset(t);
363+
dst_cache_reset(&t->dst_cache);
364364
dev_put(dev);
365365
}
366366

@@ -633,7 +633,7 @@ static netdev_tx_t ip6gre_xmit2(struct sk_buff *skb,
633633
}
634634

635635
if (!fl6->flowi6_mark)
636-
dst = ip6_tnl_dst_get(tunnel);
636+
dst = dst_cache_get(&tunnel->dst_cache);
637637

638638
if (!dst) {
639639
dst = ip6_route_output(net, NULL, fl6);
@@ -702,7 +702,7 @@ static netdev_tx_t ip6gre_xmit2(struct sk_buff *skb,
702702
}
703703

704704
if (!fl6->flowi6_mark && ndst)
705-
ip6_tnl_dst_set(tunnel, ndst);
705+
dst_cache_set_ip6(&tunnel->dst_cache, ndst, &fl6->saddr);
706706
skb_dst_set(skb, dst);
707707

708708
proto = NEXTHDR_GRE;
@@ -1009,7 +1009,7 @@ static int ip6gre_tnl_change(struct ip6_tnl *t,
10091009
t->parms.o_key = p->o_key;
10101010
t->parms.i_flags = p->i_flags;
10111011
t->parms.o_flags = p->o_flags;
1012-
ip6_tnl_dst_reset(t);
1012+
dst_cache_reset(&t->dst_cache);
10131013
ip6gre_tnl_link_config(t, set_mtu);
10141014
return 0;
10151015
}
@@ -1219,7 +1219,7 @@ static void ip6gre_dev_free(struct net_device *dev)
12191219
{
12201220
struct ip6_tnl *t = netdev_priv(dev);
12211221

1222-
ip6_tnl_dst_destroy(t);
1222+
dst_cache_destroy(&t->dst_cache);
12231223
free_percpu(dev->tstats);
12241224
free_netdev(dev);
12251225
}
@@ -1257,7 +1257,7 @@ static int ip6gre_tunnel_init_common(struct net_device *dev)
12571257
if (!dev->tstats)
12581258
return -ENOMEM;
12591259

1260-
ret = ip6_tnl_dst_init(tunnel);
1260+
ret = dst_cache_init(&tunnel->dst_cache, GFP_KERNEL);
12611261
if (ret) {
12621262
free_percpu(dev->tstats);
12631263
dev->tstats = NULL;

net/ipv6/ip6_tunnel.c

Lines changed: 6 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -122,97 +122,6 @@ static struct net_device_stats *ip6_get_stats(struct net_device *dev)
122122
return &dev->stats;
123123
}
124124

125-
/*
126-
* Locking : hash tables are protected by RCU and RTNL
127-
*/
128-
129-
static void ip6_tnl_per_cpu_dst_set(struct ip6_tnl_dst *idst,
130-
struct dst_entry *dst)
131-
{
132-
write_seqlock_bh(&idst->lock);
133-
dst_release(rcu_dereference_protected(
134-
idst->dst,
135-
lockdep_is_held(&idst->lock.lock)));
136-
if (dst) {
137-
dst_hold(dst);
138-
idst->cookie = rt6_get_cookie((struct rt6_info *)dst);
139-
} else {
140-
idst->cookie = 0;
141-
}
142-
rcu_assign_pointer(idst->dst, dst);
143-
write_sequnlock_bh(&idst->lock);
144-
}
145-
146-
struct dst_entry *ip6_tnl_dst_get(struct ip6_tnl *t)
147-
{
148-
struct ip6_tnl_dst *idst;
149-
struct dst_entry *dst;
150-
unsigned int seq;
151-
u32 cookie;
152-
153-
idst = raw_cpu_ptr(t->dst_cache);
154-
155-
rcu_read_lock();
156-
do {
157-
seq = read_seqbegin(&idst->lock);
158-
dst = rcu_dereference(idst->dst);
159-
cookie = idst->cookie;
160-
} while (read_seqretry(&idst->lock, seq));
161-
162-
if (dst && !atomic_inc_not_zero(&dst->__refcnt))
163-
dst = NULL;
164-
rcu_read_unlock();
165-
166-
if (dst && dst->obsolete && !dst->ops->check(dst, cookie)) {
167-
ip6_tnl_per_cpu_dst_set(idst, NULL);
168-
dst_release(dst);
169-
dst = NULL;
170-
}
171-
return dst;
172-
}
173-
EXPORT_SYMBOL_GPL(ip6_tnl_dst_get);
174-
175-
void ip6_tnl_dst_reset(struct ip6_tnl *t)
176-
{
177-
int i;
178-
179-
for_each_possible_cpu(i)
180-
ip6_tnl_per_cpu_dst_set(per_cpu_ptr(t->dst_cache, i), NULL);
181-
}
182-
EXPORT_SYMBOL_GPL(ip6_tnl_dst_reset);
183-
184-
void ip6_tnl_dst_set(struct ip6_tnl *t, struct dst_entry *dst)
185-
{
186-
ip6_tnl_per_cpu_dst_set(raw_cpu_ptr(t->dst_cache), dst);
187-
188-
}
189-
EXPORT_SYMBOL_GPL(ip6_tnl_dst_set);
190-
191-
void ip6_tnl_dst_destroy(struct ip6_tnl *t)
192-
{
193-
if (!t->dst_cache)
194-
return;
195-
196-
ip6_tnl_dst_reset(t);
197-
free_percpu(t->dst_cache);
198-
}
199-
EXPORT_SYMBOL_GPL(ip6_tnl_dst_destroy);
200-
201-
int ip6_tnl_dst_init(struct ip6_tnl *t)
202-
{
203-
int i;
204-
205-
t->dst_cache = alloc_percpu(struct ip6_tnl_dst);
206-
if (!t->dst_cache)
207-
return -ENOMEM;
208-
209-
for_each_possible_cpu(i)
210-
seqlock_init(&per_cpu_ptr(t->dst_cache, i)->lock);
211-
212-
return 0;
213-
}
214-
EXPORT_SYMBOL_GPL(ip6_tnl_dst_init);
215-
216125
/**
217126
* ip6_tnl_lookup - fetch tunnel matching the end-point addresses
218127
* @remote: the address of the tunnel exit-point
@@ -329,7 +238,7 @@ static void ip6_dev_free(struct net_device *dev)
329238
{
330239
struct ip6_tnl *t = netdev_priv(dev);
331240

332-
ip6_tnl_dst_destroy(t);
241+
dst_cache_destroy(&t->dst_cache);
333242
free_percpu(dev->tstats);
334243
free_netdev(dev);
335244
}
@@ -462,7 +371,7 @@ ip6_tnl_dev_uninit(struct net_device *dev)
462371
RCU_INIT_POINTER(ip6n->tnls_wc[0], NULL);
463372
else
464373
ip6_tnl_unlink(ip6n, t);
465-
ip6_tnl_dst_reset(t);
374+
dst_cache_reset(&t->dst_cache);
466375
dev_put(dev);
467376
}
468377

@@ -1069,7 +978,7 @@ static int ip6_tnl_xmit2(struct sk_buff *skb,
1069978
memcpy(&fl6->daddr, addr6, sizeof(fl6->daddr));
1070979
neigh_release(neigh);
1071980
} else if (!fl6->flowi6_mark)
1072-
dst = ip6_tnl_dst_get(t);
981+
dst = dst_cache_get(&t->dst_cache);
1073982

1074983
if (!ip6_tnl_xmit_ctl(t, &fl6->saddr, &fl6->daddr))
1075984
goto tx_err_link_failure;
@@ -1133,7 +1042,7 @@ static int ip6_tnl_xmit2(struct sk_buff *skb,
11331042
}
11341043

11351044
if (!fl6->flowi6_mark && ndst)
1136-
ip6_tnl_dst_set(t, ndst);
1045+
dst_cache_set_ip6(&t->dst_cache, ndst, &fl6->saddr);
11371046
skb_dst_set(skb, dst);
11381047

11391048
skb->transport_header = skb->network_header;
@@ -1366,7 +1275,7 @@ ip6_tnl_change(struct ip6_tnl *t, const struct __ip6_tnl_parm *p)
13661275
t->parms.flowinfo = p->flowinfo;
13671276
t->parms.link = p->link;
13681277
t->parms.proto = p->proto;
1369-
ip6_tnl_dst_reset(t);
1278+
dst_cache_reset(&t->dst_cache);
13701279
ip6_tnl_link_config(t);
13711280
return 0;
13721281
}
@@ -1637,7 +1546,7 @@ ip6_tnl_dev_init_gen(struct net_device *dev)
16371546
if (!dev->tstats)
16381547
return -ENOMEM;
16391548

1640-
ret = ip6_tnl_dst_init(t);
1549+
ret = dst_cache_init(&t->dst_cache, GFP_KERNEL);
16411550
if (ret) {
16421551
free_percpu(dev->tstats);
16431552
dev->tstats = NULL;

net/ipv6/ip6_vti.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -640,7 +640,7 @@ vti6_tnl_change(struct ip6_tnl *t, const struct __ip6_tnl_parm *p)
640640
t->parms.i_key = p->i_key;
641641
t->parms.o_key = p->o_key;
642642
t->parms.proto = p->proto;
643-
ip6_tnl_dst_reset(t);
643+
dst_cache_reset(&t->dst_cache);
644644
vti6_link_config(t);
645645
return 0;
646646
}

0 commit comments

Comments
 (0)