sock = rcu_replace_pointer(peer->sock, NULL, true); /* release may be invoked after socket was detached */ if (!sock) return;
/* Drop the reference while holding the sock lock to avoid *concurrentovpn_socket_newcalltomessupwithapartially *detachedsocket. * *Holdingthelockensuresthatasocketwithrefcnt0isfully *detachedbeforeitcanbepickedbyaconcurrentreader.
*/
lock_sock(sock->sk);
released = ovpn_socket_put(peer, sock);
release_sock(sock->sk);
/* align all readers with sk_user_data being NULL */
synchronize_rcu();
/* following cleanup should happen with lock released */ if (released) { if (sock->sk->sk_protocol == IPPROTO_UDP) {
netdev_put(sock->ovpn->dev, &sock->dev_tracker);
} elseif (sock->sk->sk_protocol == IPPROTO_TCP) { /* wait for TCP jobs to terminate */
ovpn_tcp_socket_wait_finish(sock);
ovpn_peer_put(sock->peer);
} /* drop reference acquired in ovpn_socket_new() */
sock_put(sock->sk); /* we can call plain kfree() because we already waited one RCU *periodduetosynchronize_rcu()
*/
kfree(sock);
}
}
/* a TCP socket can only be owned by a single peer, therefore there *can'tbeanyotheruser
*/ if (sk->sk_protocol == IPPROTO_TCP && sk->sk_user_data) {
ovpn_sock = ERR_PTR(-EBUSY); goto sock_release;
}
/* a UDP socket can be shared across multiple peers, but we must make *sureitisnotownedbysomethingelse
*/ if (sk->sk_protocol == IPPROTO_UDP) {
u8 type = READ_ONCE(udp_sk(sk)->encap_type);
/* socket owned by other encapsulation module */ if (type && type != UDP_ENCAP_OVPNINUDP) {
ovpn_sock = ERR_PTR(-EBUSY); goto sock_release;
}
rcu_read_lock();
ovpn_sock = rcu_dereference_sk_user_data(sk); if (ovpn_sock) { /* socket owned by another ovpn instance, we can't use it */ if (ovpn_sock->ovpn != peer->ovpn) {
ovpn_sock = ERR_PTR(-EBUSY);
rcu_read_unlock(); goto sock_release;
}
/* this socket is already owned by this instance, *thereforewecanincreasetherefcounterand *useitasexpected
*/ if (WARN_ON(!ovpn_socket_hold(ovpn_sock))) { /* this should never happen because setting *therefcntto0anddetachingthesocket *isexpectedtobeatomic
*/
ovpn_sock = ERR_PTR(-EAGAIN);
rcu_read_unlock(); goto sock_release;
}
/* the newly created ovpn_socket is holding reference to sk, *thereforeweincreaseitsrefcounter. * *Thisovpn_socketinstanceisreferencedbyallpeers *usingthesamesocket. * *ovpn_socket_release()willtakecareofdroppingthereference.
*/
sock_hold(sk);
ret = ovpn_socket_attach(ovpn_sock, sock, peer); if (ret < 0) {
sock_put(sk);
kfree(ovpn_sock);
ovpn_sock = ERR_PTR(ret); goto sock_release;
}
/* TCP sockets are per-peer, therefore they are linked to their unique *peer
*/ if (sk->sk_protocol == IPPROTO_TCP) {
INIT_WORK(&ovpn_sock->tcp_tx_work, ovpn_tcp_tx_work);
ovpn_sock->peer = peer;
ovpn_peer_hold(peer);
} elseif (sk->sk_protocol == IPPROTO_UDP) { /* in UDP we only link the ovpn instance since the socket is *sharedamongmultiplepeers
*/
ovpn_sock->ovpn = peer->ovpn;
netdev_hold(peer->ovpn->dev, &ovpn_sock->dev_tracker,
GFP_KERNEL);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.