.. | .. |
---|
104 | 104 | /* per-net private data for this module */ |
---|
105 | 105 | static unsigned int l2tp_net_id; |
---|
106 | 106 | struct l2tp_net { |
---|
107 | | - struct list_head l2tp_tunnel_list; |
---|
108 | | - /* Lock for write access to l2tp_tunnel_list */ |
---|
109 | | - spinlock_t l2tp_tunnel_list_lock; |
---|
| 107 | + /* Lock for write access to l2tp_tunnel_idr */ |
---|
| 108 | + spinlock_t l2tp_tunnel_idr_lock; |
---|
| 109 | + struct idr l2tp_tunnel_idr; |
---|
110 | 110 | struct hlist_head l2tp_session_hlist[L2TP_HASH_SIZE_2]; |
---|
111 | 111 | /* Lock for write access to l2tp_session_hlist */ |
---|
112 | 112 | spinlock_t l2tp_session_hlist_lock; |
---|
.. | .. |
---|
208 | 208 | struct l2tp_tunnel *tunnel; |
---|
209 | 209 | |
---|
210 | 210 | rcu_read_lock_bh(); |
---|
211 | | - list_for_each_entry_rcu(tunnel, &pn->l2tp_tunnel_list, list) { |
---|
212 | | - if (tunnel->tunnel_id == tunnel_id && |
---|
213 | | - refcount_inc_not_zero(&tunnel->ref_count)) { |
---|
214 | | - rcu_read_unlock_bh(); |
---|
215 | | - |
---|
216 | | - return tunnel; |
---|
217 | | - } |
---|
| 211 | + tunnel = idr_find(&pn->l2tp_tunnel_idr, tunnel_id); |
---|
| 212 | + if (tunnel && refcount_inc_not_zero(&tunnel->ref_count)) { |
---|
| 213 | + rcu_read_unlock_bh(); |
---|
| 214 | + return tunnel; |
---|
218 | 215 | } |
---|
219 | 216 | rcu_read_unlock_bh(); |
---|
220 | 217 | |
---|
.. | .. |
---|
224 | 221 | |
---|
225 | 222 | struct l2tp_tunnel *l2tp_tunnel_get_nth(const struct net *net, int nth) |
---|
226 | 223 | { |
---|
227 | | - const struct l2tp_net *pn = l2tp_pernet(net); |
---|
| 224 | + struct l2tp_net *pn = l2tp_pernet(net); |
---|
| 225 | + unsigned long tunnel_id, tmp; |
---|
228 | 226 | struct l2tp_tunnel *tunnel; |
---|
229 | 227 | int count = 0; |
---|
230 | 228 | |
---|
231 | 229 | rcu_read_lock_bh(); |
---|
232 | | - list_for_each_entry_rcu(tunnel, &pn->l2tp_tunnel_list, list) { |
---|
233 | | - if (++count > nth && |
---|
| 230 | + idr_for_each_entry_ul(&pn->l2tp_tunnel_idr, tunnel, tmp, tunnel_id) { |
---|
| 231 | + if (tunnel && ++count > nth && |
---|
234 | 232 | refcount_inc_not_zero(&tunnel->ref_count)) { |
---|
235 | 233 | rcu_read_unlock_bh(); |
---|
236 | 234 | return tunnel; |
---|
.. | .. |
---|
1043 | 1041 | IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED | IPSKB_REROUTED); |
---|
1044 | 1042 | nf_reset_ct(skb); |
---|
1045 | 1043 | |
---|
1046 | | - bh_lock_sock(sk); |
---|
| 1044 | + bh_lock_sock_nested(sk); |
---|
1047 | 1045 | if (sock_owned_by_user(sk)) { |
---|
1048 | 1046 | kfree_skb(skb); |
---|
1049 | 1047 | ret = NET_XMIT_DROP; |
---|
.. | .. |
---|
1150 | 1148 | } |
---|
1151 | 1149 | |
---|
1152 | 1150 | /* Remove hooks into tunnel socket */ |
---|
| 1151 | + write_lock_bh(&sk->sk_callback_lock); |
---|
1153 | 1152 | sk->sk_destruct = tunnel->old_sk_destruct; |
---|
1154 | 1153 | sk->sk_user_data = NULL; |
---|
| 1154 | + write_unlock_bh(&sk->sk_callback_lock); |
---|
1155 | 1155 | |
---|
1156 | 1156 | /* Call the original destructor */ |
---|
1157 | 1157 | if (sk->sk_destruct) |
---|
.. | .. |
---|
1227 | 1227 | l2tp_tunnel_delete(tunnel); |
---|
1228 | 1228 | } |
---|
1229 | 1229 | |
---|
| 1230 | +static void l2tp_tunnel_remove(struct net *net, struct l2tp_tunnel *tunnel) |
---|
| 1231 | +{ |
---|
| 1232 | + struct l2tp_net *pn = l2tp_pernet(net); |
---|
| 1233 | + |
---|
| 1234 | + spin_lock_bh(&pn->l2tp_tunnel_idr_lock); |
---|
| 1235 | + idr_remove(&pn->l2tp_tunnel_idr, tunnel->tunnel_id); |
---|
| 1236 | + spin_unlock_bh(&pn->l2tp_tunnel_idr_lock); |
---|
| 1237 | +} |
---|
| 1238 | + |
---|
1230 | 1239 | /* Workqueue tunnel deletion function */ |
---|
1231 | 1240 | static void l2tp_tunnel_del_work(struct work_struct *work) |
---|
1232 | 1241 | { |
---|
.. | .. |
---|
1234 | 1243 | del_work); |
---|
1235 | 1244 | struct sock *sk = tunnel->sock; |
---|
1236 | 1245 | struct socket *sock = sk->sk_socket; |
---|
1237 | | - struct l2tp_net *pn; |
---|
1238 | 1246 | |
---|
1239 | 1247 | l2tp_tunnel_closeall(tunnel); |
---|
1240 | 1248 | |
---|
.. | .. |
---|
1248 | 1256 | } |
---|
1249 | 1257 | } |
---|
1250 | 1258 | |
---|
1251 | | - /* Remove the tunnel struct from the tunnel list */ |
---|
1252 | | - pn = l2tp_pernet(tunnel->l2tp_net); |
---|
1253 | | - spin_lock_bh(&pn->l2tp_tunnel_list_lock); |
---|
1254 | | - list_del_rcu(&tunnel->list); |
---|
1255 | | - spin_unlock_bh(&pn->l2tp_tunnel_list_lock); |
---|
1256 | | - |
---|
| 1259 | + l2tp_tunnel_remove(tunnel->l2tp_net, tunnel); |
---|
1257 | 1260 | /* drop initial ref */ |
---|
1258 | 1261 | l2tp_tunnel_dec_refcount(tunnel); |
---|
1259 | 1262 | |
---|
.. | .. |
---|
1384 | 1387 | return err; |
---|
1385 | 1388 | } |
---|
1386 | 1389 | |
---|
1387 | | -static struct lock_class_key l2tp_socket_class; |
---|
1388 | | - |
---|
1389 | 1390 | int l2tp_tunnel_create(int fd, int version, u32 tunnel_id, u32 peer_tunnel_id, |
---|
1390 | 1391 | struct l2tp_tunnel_cfg *cfg, struct l2tp_tunnel **tunnelp) |
---|
1391 | 1392 | { |
---|
.. | .. |
---|
1455 | 1456 | int l2tp_tunnel_register(struct l2tp_tunnel *tunnel, struct net *net, |
---|
1456 | 1457 | struct l2tp_tunnel_cfg *cfg) |
---|
1457 | 1458 | { |
---|
1458 | | - struct l2tp_tunnel *tunnel_walk; |
---|
1459 | | - struct l2tp_net *pn; |
---|
| 1459 | + struct l2tp_net *pn = l2tp_pernet(net); |
---|
| 1460 | + u32 tunnel_id = tunnel->tunnel_id; |
---|
1460 | 1461 | struct socket *sock; |
---|
1461 | 1462 | struct sock *sk; |
---|
1462 | 1463 | int ret; |
---|
| 1464 | + |
---|
| 1465 | + spin_lock_bh(&pn->l2tp_tunnel_idr_lock); |
---|
| 1466 | + ret = idr_alloc_u32(&pn->l2tp_tunnel_idr, NULL, &tunnel_id, tunnel_id, |
---|
| 1467 | + GFP_ATOMIC); |
---|
| 1468 | + spin_unlock_bh(&pn->l2tp_tunnel_idr_lock); |
---|
| 1469 | + if (ret) |
---|
| 1470 | + return ret == -ENOSPC ? -EEXIST : ret; |
---|
1463 | 1471 | |
---|
1464 | 1472 | if (tunnel->fd < 0) { |
---|
1465 | 1473 | ret = l2tp_tunnel_sock_create(net, tunnel->tunnel_id, |
---|
.. | .. |
---|
1471 | 1479 | sock = sockfd_lookup(tunnel->fd, &ret); |
---|
1472 | 1480 | if (!sock) |
---|
1473 | 1481 | goto err; |
---|
1474 | | - |
---|
1475 | | - ret = l2tp_validate_socket(sock->sk, net, tunnel->encap); |
---|
1476 | | - if (ret < 0) |
---|
1477 | | - goto err_sock; |
---|
1478 | 1482 | } |
---|
1479 | | - |
---|
1480 | | - tunnel->l2tp_net = net; |
---|
1481 | | - pn = l2tp_pernet(net); |
---|
1482 | 1483 | |
---|
1483 | 1484 | sk = sock->sk; |
---|
1484 | | - sock_hold(sk); |
---|
1485 | | - tunnel->sock = sk; |
---|
1486 | | - |
---|
1487 | | - spin_lock_bh(&pn->l2tp_tunnel_list_lock); |
---|
1488 | | - list_for_each_entry(tunnel_walk, &pn->l2tp_tunnel_list, list) { |
---|
1489 | | - if (tunnel_walk->tunnel_id == tunnel->tunnel_id) { |
---|
1490 | | - spin_unlock_bh(&pn->l2tp_tunnel_list_lock); |
---|
1491 | | - sock_put(sk); |
---|
1492 | | - ret = -EEXIST; |
---|
1493 | | - goto err_sock; |
---|
1494 | | - } |
---|
1495 | | - } |
---|
1496 | | - list_add_rcu(&tunnel->list, &pn->l2tp_tunnel_list); |
---|
1497 | | - spin_unlock_bh(&pn->l2tp_tunnel_list_lock); |
---|
| 1485 | + lock_sock(sk); |
---|
| 1486 | + write_lock_bh(&sk->sk_callback_lock); |
---|
| 1487 | + ret = l2tp_validate_socket(sk, net, tunnel->encap); |
---|
| 1488 | + if (ret < 0) |
---|
| 1489 | + goto err_inval_sock; |
---|
| 1490 | + rcu_assign_sk_user_data(sk, tunnel); |
---|
| 1491 | + write_unlock_bh(&sk->sk_callback_lock); |
---|
1498 | 1492 | |
---|
1499 | 1493 | if (tunnel->encap == L2TP_ENCAPTYPE_UDP) { |
---|
1500 | 1494 | struct udp_tunnel_sock_cfg udp_cfg = { |
---|
.. | .. |
---|
1505 | 1499 | }; |
---|
1506 | 1500 | |
---|
1507 | 1501 | setup_udp_tunnel_sock(net, sock, &udp_cfg); |
---|
1508 | | - } else { |
---|
1509 | | - sk->sk_user_data = tunnel; |
---|
1510 | 1502 | } |
---|
1511 | 1503 | |
---|
1512 | 1504 | tunnel->old_sk_destruct = sk->sk_destruct; |
---|
1513 | 1505 | sk->sk_destruct = &l2tp_tunnel_destruct; |
---|
1514 | | - lockdep_set_class_and_name(&sk->sk_lock.slock, &l2tp_socket_class, |
---|
1515 | | - "l2tp_sock"); |
---|
1516 | 1506 | sk->sk_allocation = GFP_ATOMIC; |
---|
| 1507 | + release_sock(sk); |
---|
| 1508 | + |
---|
| 1509 | + sock_hold(sk); |
---|
| 1510 | + tunnel->sock = sk; |
---|
| 1511 | + tunnel->l2tp_net = net; |
---|
| 1512 | + |
---|
| 1513 | + spin_lock_bh(&pn->l2tp_tunnel_idr_lock); |
---|
| 1514 | + idr_replace(&pn->l2tp_tunnel_idr, tunnel, tunnel->tunnel_id); |
---|
| 1515 | + spin_unlock_bh(&pn->l2tp_tunnel_idr_lock); |
---|
1517 | 1516 | |
---|
1518 | 1517 | trace_register_tunnel(tunnel); |
---|
1519 | 1518 | |
---|
.. | .. |
---|
1522 | 1521 | |
---|
1523 | 1522 | return 0; |
---|
1524 | 1523 | |
---|
1525 | | -err_sock: |
---|
| 1524 | +err_inval_sock: |
---|
| 1525 | + write_unlock_bh(&sk->sk_callback_lock); |
---|
| 1526 | + release_sock(sk); |
---|
| 1527 | + |
---|
1526 | 1528 | if (tunnel->fd < 0) |
---|
1527 | 1529 | sock_release(sock); |
---|
1528 | 1530 | else |
---|
1529 | 1531 | sockfd_put(sock); |
---|
1530 | 1532 | err: |
---|
| 1533 | + l2tp_tunnel_remove(net, tunnel); |
---|
1531 | 1534 | return ret; |
---|
1532 | 1535 | } |
---|
1533 | 1536 | EXPORT_SYMBOL_GPL(l2tp_tunnel_register); |
---|
.. | .. |
---|
1641 | 1644 | struct l2tp_net *pn = net_generic(net, l2tp_net_id); |
---|
1642 | 1645 | int hash; |
---|
1643 | 1646 | |
---|
1644 | | - INIT_LIST_HEAD(&pn->l2tp_tunnel_list); |
---|
1645 | | - spin_lock_init(&pn->l2tp_tunnel_list_lock); |
---|
| 1647 | + idr_init(&pn->l2tp_tunnel_idr); |
---|
| 1648 | + spin_lock_init(&pn->l2tp_tunnel_idr_lock); |
---|
1646 | 1649 | |
---|
1647 | 1650 | for (hash = 0; hash < L2TP_HASH_SIZE_2; hash++) |
---|
1648 | 1651 | INIT_HLIST_HEAD(&pn->l2tp_session_hlist[hash]); |
---|
.. | .. |
---|
1656 | 1659 | { |
---|
1657 | 1660 | struct l2tp_net *pn = l2tp_pernet(net); |
---|
1658 | 1661 | struct l2tp_tunnel *tunnel = NULL; |
---|
| 1662 | + unsigned long tunnel_id, tmp; |
---|
1659 | 1663 | int hash; |
---|
1660 | 1664 | |
---|
1661 | 1665 | rcu_read_lock_bh(); |
---|
1662 | | - list_for_each_entry_rcu(tunnel, &pn->l2tp_tunnel_list, list) { |
---|
1663 | | - l2tp_tunnel_delete(tunnel); |
---|
| 1666 | + idr_for_each_entry_ul(&pn->l2tp_tunnel_idr, tunnel, tmp, tunnel_id) { |
---|
| 1667 | + if (tunnel) |
---|
| 1668 | + l2tp_tunnel_delete(tunnel); |
---|
1664 | 1669 | } |
---|
1665 | 1670 | rcu_read_unlock_bh(); |
---|
1666 | 1671 | |
---|
.. | .. |
---|
1670 | 1675 | |
---|
1671 | 1676 | for (hash = 0; hash < L2TP_HASH_SIZE_2; hash++) |
---|
1672 | 1677 | WARN_ON_ONCE(!hlist_empty(&pn->l2tp_session_hlist[hash])); |
---|
| 1678 | + idr_destroy(&pn->l2tp_tunnel_idr); |
---|
1673 | 1679 | } |
---|
1674 | 1680 | |
---|
1675 | 1681 | static struct pernet_operations l2tp_net_ops = { |
---|