diff options
Diffstat (limited to 'net/ipv4/ipip.c')
| -rw-r--r-- | net/ipv4/ipip.c | 71 |
1 files changed, 42 insertions, 29 deletions
diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index ae40ed1ba560..c5b1f71c3cd8 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c | |||
| @@ -134,7 +134,13 @@ static void ipip_fb_tunnel_init(struct net_device *dev); | |||
| 134 | static void ipip_tunnel_init(struct net_device *dev); | 134 | static void ipip_tunnel_init(struct net_device *dev); |
| 135 | static void ipip_tunnel_setup(struct net_device *dev); | 135 | static void ipip_tunnel_setup(struct net_device *dev); |
| 136 | 136 | ||
| 137 | static DEFINE_RWLOCK(ipip_lock); | 137 | /* |
| 138 | * Locking : hash tables are protected by RCU and a spinlock | ||
| 139 | */ | ||
| 140 | static DEFINE_SPINLOCK(ipip_lock); | ||
| 141 | |||
| 142 | #define for_each_ip_tunnel_rcu(start) \ | ||
| 143 | for (t = rcu_dereference(start); t; t = rcu_dereference(t->next)) | ||
| 138 | 144 | ||
| 139 | static struct ip_tunnel * ipip_tunnel_lookup(struct net *net, | 145 | static struct ip_tunnel * ipip_tunnel_lookup(struct net *net, |
| 140 | __be32 remote, __be32 local) | 146 | __be32 remote, __be32 local) |
| @@ -144,20 +150,21 @@ static struct ip_tunnel * ipip_tunnel_lookup(struct net *net, | |||
| 144 | struct ip_tunnel *t; | 150 | struct ip_tunnel *t; |
| 145 | struct ipip_net *ipn = net_generic(net, ipip_net_id); | 151 | struct ipip_net *ipn = net_generic(net, ipip_net_id); |
| 146 | 152 | ||
| 147 | for (t = ipn->tunnels_r_l[h0^h1]; t; t = t->next) { | 153 | for_each_ip_tunnel_rcu(ipn->tunnels_r_l[h0 ^ h1]) |
| 148 | if (local == t->parms.iph.saddr && | 154 | if (local == t->parms.iph.saddr && |
| 149 | remote == t->parms.iph.daddr && (t->dev->flags&IFF_UP)) | 155 | remote == t->parms.iph.daddr && (t->dev->flags&IFF_UP)) |
| 150 | return t; | 156 | return t; |
| 151 | } | 157 | |
| 152 | for (t = ipn->tunnels_r[h0]; t; t = t->next) { | 158 | for_each_ip_tunnel_rcu(ipn->tunnels_r[h0]) |
| 153 | if (remote == t->parms.iph.daddr && (t->dev->flags&IFF_UP)) | 159 | if (remote == t->parms.iph.daddr && (t->dev->flags&IFF_UP)) |
| 154 | return t; | 160 | return t; |
| 155 | } | 161 | |
| 156 | for (t = ipn->tunnels_l[h1]; t; t = t->next) { | 162 | for_each_ip_tunnel_rcu(ipn->tunnels_l[h1]) |
| 157 | if (local == t->parms.iph.saddr && (t->dev->flags&IFF_UP)) | 163 | if (local == t->parms.iph.saddr && (t->dev->flags&IFF_UP)) |
| 158 | return t; | 164 | return t; |
| 159 | } | 165 | |
| 160 | if ((t = ipn->tunnels_wc[0]) != NULL && (t->dev->flags&IFF_UP)) | 166 | t = rcu_dereference(ipn->tunnels_wc[0]); |
| 167 | if (t && (t->dev->flags&IFF_UP)) | ||
| 161 | return t; | 168 | return t; |
| 162 | return NULL; | 169 | return NULL; |
| 163 | } | 170 | } |
| @@ -193,9 +200,9 @@ static void ipip_tunnel_unlink(struct ipip_net *ipn, struct ip_tunnel *t) | |||
| 193 | 200 | ||
| 194 | for (tp = ipip_bucket(ipn, t); *tp; tp = &(*tp)->next) { | 201 | for (tp = ipip_bucket(ipn, t); *tp; tp = &(*tp)->next) { |
| 195 | if (t == *tp) { | 202 | if (t == *tp) { |
| 196 | write_lock_bh(&ipip_lock); | 203 | spin_lock_bh(&ipip_lock); |
| 197 | *tp = t->next; | 204 | *tp = t->next; |
| 198 | write_unlock_bh(&ipip_lock); | 205 | spin_unlock_bh(&ipip_lock); |
| 199 | break; | 206 | break; |
| 200 | } | 207 | } |
| 201 | } | 208 | } |
| @@ -205,10 +212,10 @@ static void ipip_tunnel_link(struct ipip_net *ipn, struct ip_tunnel *t) | |||
| 205 | { | 212 | { |
| 206 | struct ip_tunnel **tp = ipip_bucket(ipn, t); | 213 | struct ip_tunnel **tp = ipip_bucket(ipn, t); |
| 207 | 214 | ||
| 215 | spin_lock_bh(&ipip_lock); | ||
| 208 | t->next = *tp; | 216 | t->next = *tp; |
| 209 | write_lock_bh(&ipip_lock); | 217 | rcu_assign_pointer(*tp, t); |
| 210 | *tp = t; | 218 | spin_unlock_bh(&ipip_lock); |
| 211 | write_unlock_bh(&ipip_lock); | ||
| 212 | } | 219 | } |
| 213 | 220 | ||
| 214 | static struct ip_tunnel * ipip_tunnel_locate(struct net *net, | 221 | static struct ip_tunnel * ipip_tunnel_locate(struct net *net, |
| @@ -267,9 +274,9 @@ static void ipip_tunnel_uninit(struct net_device *dev) | |||
| 267 | struct ipip_net *ipn = net_generic(net, ipip_net_id); | 274 | struct ipip_net *ipn = net_generic(net, ipip_net_id); |
| 268 | 275 | ||
| 269 | if (dev == ipn->fb_tunnel_dev) { | 276 | if (dev == ipn->fb_tunnel_dev) { |
| 270 | write_lock_bh(&ipip_lock); | 277 | spin_lock_bh(&ipip_lock); |
| 271 | ipn->tunnels_wc[0] = NULL; | 278 | ipn->tunnels_wc[0] = NULL; |
| 272 | write_unlock_bh(&ipip_lock); | 279 | spin_unlock_bh(&ipip_lock); |
| 273 | } else | 280 | } else |
| 274 | ipip_tunnel_unlink(ipn, netdev_priv(dev)); | 281 | ipip_tunnel_unlink(ipn, netdev_priv(dev)); |
| 275 | dev_put(dev); | 282 | dev_put(dev); |
| @@ -318,7 +325,7 @@ static int ipip_err(struct sk_buff *skb, u32 info) | |||
| 318 | 325 | ||
| 319 | err = -ENOENT; | 326 | err = -ENOENT; |
| 320 | 327 | ||
| 321 | read_lock(&ipip_lock); | 328 | rcu_read_lock(); |
| 322 | t = ipip_tunnel_lookup(dev_net(skb->dev), iph->daddr, iph->saddr); | 329 | t = ipip_tunnel_lookup(dev_net(skb->dev), iph->daddr, iph->saddr); |
| 323 | if (t == NULL || t->parms.iph.daddr == 0) | 330 | if (t == NULL || t->parms.iph.daddr == 0) |
| 324 | goto out; | 331 | goto out; |
| @@ -333,7 +340,7 @@ static int ipip_err(struct sk_buff *skb, u32 info) | |||
| 333 | t->err_count = 1; | 340 | t->err_count = 1; |
| 334 | t->err_time = jiffies; | 341 | t->err_time = jiffies; |
| 335 | out: | 342 | out: |
| 336 | read_unlock(&ipip_lock); | 343 | rcu_read_unlock(); |
| 337 | return err; | 344 | return err; |
| 338 | } | 345 | } |
| 339 | 346 | ||
| @@ -351,11 +358,11 @@ static int ipip_rcv(struct sk_buff *skb) | |||
| 351 | struct ip_tunnel *tunnel; | 358 | struct ip_tunnel *tunnel; |
| 352 | const struct iphdr *iph = ip_hdr(skb); | 359 | const struct iphdr *iph = ip_hdr(skb); |
| 353 | 360 | ||
| 354 | read_lock(&ipip_lock); | 361 | rcu_read_lock(); |
| 355 | if ((tunnel = ipip_tunnel_lookup(dev_net(skb->dev), | 362 | if ((tunnel = ipip_tunnel_lookup(dev_net(skb->dev), |
| 356 | iph->saddr, iph->daddr)) != NULL) { | 363 | iph->saddr, iph->daddr)) != NULL) { |
| 357 | if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) { | 364 | if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) { |
| 358 | read_unlock(&ipip_lock); | 365 | rcu_read_unlock(); |
| 359 | kfree_skb(skb); | 366 | kfree_skb(skb); |
| 360 | return 0; | 367 | return 0; |
| 361 | } | 368 | } |
| @@ -374,10 +381,10 @@ static int ipip_rcv(struct sk_buff *skb) | |||
| 374 | nf_reset(skb); | 381 | nf_reset(skb); |
| 375 | ipip_ecn_decapsulate(iph, skb); | 382 | ipip_ecn_decapsulate(iph, skb); |
| 376 | netif_rx(skb); | 383 | netif_rx(skb); |
| 377 | read_unlock(&ipip_lock); | 384 | rcu_read_unlock(); |
| 378 | return 0; | 385 | return 0; |
| 379 | } | 386 | } |
| 380 | read_unlock(&ipip_lock); | 387 | rcu_read_unlock(); |
| 381 | 388 | ||
| 382 | return -1; | 389 | return -1; |
| 383 | } | 390 | } |
| @@ -390,7 +397,8 @@ static int ipip_rcv(struct sk_buff *skb) | |||
| 390 | static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) | 397 | static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) |
| 391 | { | 398 | { |
| 392 | struct ip_tunnel *tunnel = netdev_priv(dev); | 399 | struct ip_tunnel *tunnel = netdev_priv(dev); |
| 393 | struct net_device_stats *stats = &tunnel->dev->stats; | 400 | struct net_device_stats *stats = &dev->stats; |
| 401 | struct netdev_queue *txq = netdev_get_tx_queue(dev, 0); | ||
| 394 | struct iphdr *tiph = &tunnel->parms.iph; | 402 | struct iphdr *tiph = &tunnel->parms.iph; |
| 395 | u8 tos = tunnel->parms.iph.tos; | 403 | u8 tos = tunnel->parms.iph.tos; |
| 396 | __be16 df = tiph->frag_off; | 404 | __be16 df = tiph->frag_off; |
| @@ -480,7 +488,7 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) | |||
| 480 | struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom); | 488 | struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom); |
| 481 | if (!new_skb) { | 489 | if (!new_skb) { |
| 482 | ip_rt_put(rt); | 490 | ip_rt_put(rt); |
| 483 | stats->tx_dropped++; | 491 | txq->tx_dropped++; |
| 484 | dev_kfree_skb(skb); | 492 | dev_kfree_skb(skb); |
| 485 | return NETDEV_TX_OK; | 493 | return NETDEV_TX_OK; |
| 486 | } | 494 | } |
| @@ -748,16 +756,19 @@ static struct xfrm_tunnel ipip_handler = { | |||
| 748 | static const char banner[] __initconst = | 756 | static const char banner[] __initconst = |
| 749 | KERN_INFO "IPv4 over IPv4 tunneling driver\n"; | 757 | KERN_INFO "IPv4 over IPv4 tunneling driver\n"; |
| 750 | 758 | ||
| 751 | static void ipip_destroy_tunnels(struct ipip_net *ipn) | 759 | static void ipip_destroy_tunnels(struct ipip_net *ipn, struct list_head *head) |
| 752 | { | 760 | { |
| 753 | int prio; | 761 | int prio; |
| 754 | 762 | ||
| 755 | for (prio = 1; prio < 4; prio++) { | 763 | for (prio = 1; prio < 4; prio++) { |
| 756 | int h; | 764 | int h; |
| 757 | for (h = 0; h < HASH_SIZE; h++) { | 765 | for (h = 0; h < HASH_SIZE; h++) { |
| 758 | struct ip_tunnel *t; | 766 | struct ip_tunnel *t = ipn->tunnels[prio][h]; |
| 759 | while ((t = ipn->tunnels[prio][h]) != NULL) | 767 | |
| 760 | unregister_netdevice(t->dev); | 768 | while (t != NULL) { |
| 769 | unregister_netdevice_queue(t->dev, head); | ||
| 770 | t = t->next; | ||
| 771 | } | ||
| 761 | } | 772 | } |
| 762 | } | 773 | } |
| 763 | } | 774 | } |
| @@ -810,11 +821,13 @@ err_alloc: | |||
| 810 | static void ipip_exit_net(struct net *net) | 821 | static void ipip_exit_net(struct net *net) |
| 811 | { | 822 | { |
| 812 | struct ipip_net *ipn; | 823 | struct ipip_net *ipn; |
| 824 | LIST_HEAD(list); | ||
| 813 | 825 | ||
| 814 | ipn = net_generic(net, ipip_net_id); | 826 | ipn = net_generic(net, ipip_net_id); |
| 815 | rtnl_lock(); | 827 | rtnl_lock(); |
| 816 | ipip_destroy_tunnels(ipn); | 828 | ipip_destroy_tunnels(ipn, &list); |
| 817 | unregister_netdevice(ipn->fb_tunnel_dev); | 829 | unregister_netdevice_queue(ipn->fb_tunnel_dev, &list); |
| 830 | unregister_netdevice_many(&list); | ||
| 818 | rtnl_unlock(); | 831 | rtnl_unlock(); |
| 819 | kfree(ipn); | 832 | kfree(ipn); |
| 820 | } | 833 | } |
