Merge branch 'l2tp-misc-improvements'
James Chapman says: ==================== l2tp: misc improvements This series makes several improvements to l2tp: * update documentation to be consistent with recent l2tp changes. * move l2tp_ip socket tables to per-net data. * fix handling of hash key collisions in l2tp_v3_session_get * implement and use get-next APIs for management and procfs/debugfs. * improve l2tp refcount helpers. * use per-cpu dev->tstats in l2tpeth devices. * fix a lockdep splat. * fix a race between l2tp_pre_exit_net and pppol2tp_release. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
@@ -638,9 +638,8 @@ Tunnels are identified by a unique tunnel id. The id is 16-bit for
|
||||
L2TPv2 and 32-bit for L2TPv3. Internally, the id is stored as a 32-bit
|
||||
value.
|
||||
|
||||
Tunnels are kept in a per-net list, indexed by tunnel id. The tunnel
|
||||
id namespace is shared by L2TPv2 and L2TPv3. The tunnel context can be
|
||||
derived from the socket's sk_user_data.
|
||||
Tunnels are kept in a per-net list, indexed by tunnel id. The
|
||||
tunnel id namespace is shared by L2TPv2 and L2TPv3.
|
||||
|
||||
Handling tunnel socket close is perhaps the most tricky part of the
|
||||
L2TP implementation. If userspace closes a tunnel socket, the L2TP
|
||||
@@ -652,9 +651,7 @@ socket's encap_destroy handler is invoked, which L2TP uses to initiate
|
||||
its tunnel close actions. For L2TPIP sockets, the socket's close
|
||||
handler initiates the same tunnel close actions. All sessions are
|
||||
first closed. Each session drops its tunnel ref. When the tunnel ref
|
||||
reaches zero, the tunnel puts its socket ref. When the socket is
|
||||
eventually destroyed, its sk_destruct finally frees the L2TP tunnel
|
||||
context.
|
||||
reaches zero, the tunnel drops its socket ref.
|
||||
|
||||
Sessions
|
||||
--------
|
||||
@@ -667,10 +664,7 @@ pseudowire) or other data types such as PPP, ATM, HDLC or Frame
|
||||
Relay. Linux currently implements only Ethernet and PPP session types.
|
||||
|
||||
Some L2TP session types also have a socket (PPP pseudowires) while
|
||||
others do not (Ethernet pseudowires). We can't therefore use the
|
||||
socket reference count as the reference count for session
|
||||
contexts. The L2TP implementation therefore has its own internal
|
||||
reference counts on the session contexts.
|
||||
others do not (Ethernet pseudowires).
|
||||
|
||||
Like tunnels, L2TP sessions are identified by a unique
|
||||
session id. Just as with tunnel ids, the session id is 16-bit for
|
||||
@@ -680,21 +674,19 @@ value.
|
||||
Sessions hold a ref on their parent tunnel to ensure that the tunnel
|
||||
stays extant while one or more sessions references it.
|
||||
|
||||
Sessions are kept in a per-tunnel list, indexed by session id. L2TPv3
|
||||
sessions are also kept in a per-net list indexed by session id,
|
||||
because L2TPv3 session ids are unique across all tunnels and L2TPv3
|
||||
data packets do not contain a tunnel id in the header. This list is
|
||||
therefore needed to find the session context associated with a
|
||||
received data packet when the tunnel context cannot be derived from
|
||||
the tunnel socket.
|
||||
Sessions are kept in a per-net list. L2TPv2 sessions and L2TPv3
|
||||
sessions are stored in separate lists. L2TPv2 sessions are keyed
|
||||
by a 32-bit key made up of the 16-bit tunnel ID and 16-bit
|
||||
session ID. L2TPv3 sessions are keyed by the 32-bit session ID, since
|
||||
L2TPv3 session ids are unique across all tunnels.
|
||||
|
||||
Although the L2TPv3 RFC specifies that L2TPv3 session ids are not
|
||||
scoped by the tunnel, the kernel does not police this for L2TPv3 UDP
|
||||
tunnels and does not add sessions of L2TPv3 UDP tunnels into the
|
||||
per-net session list. In the UDP receive code, we must trust that the
|
||||
tunnel can be identified using the tunnel socket's sk_user_data and
|
||||
lookup the session in the tunnel's session list instead of the per-net
|
||||
session list.
|
||||
scoped by the tunnel, the Linux implementation has historically
|
||||
allowed this. Such session id collisions are supported using a per-net
|
||||
hash table keyed by sk and session ID. When looking up L2TPv3
|
||||
sessions, the list entry may link to multiple sessions with that
|
||||
session ID, in which case the session matching the given sk (tunnel)
|
||||
is used.
|
||||
|
||||
PPP
|
||||
---
|
||||
@@ -714,10 +706,9 @@ The L2TP PPP implementation handles the closing of a PPPoL2TP socket
|
||||
by closing its corresponding L2TP session. This is complicated because
|
||||
it must consider racing with netlink session create/destroy requests
|
||||
and pppol2tp_connect trying to reconnect with a session that is in the
|
||||
process of being closed. Unlike tunnels, PPP sessions do not hold a
|
||||
ref on their associated socket, so code must be careful to sock_hold
|
||||
the socket where necessary. For all the details, see commit
|
||||
3d609342cc04129ff7568e19316ce3d7451a27e8.
|
||||
process of being closed. PPP sessions hold a ref on their associated
|
||||
socket in order that the socket remains extants while the session
|
||||
references it.
|
||||
|
||||
Ethernet
|
||||
--------
|
||||
@@ -761,15 +752,10 @@ Limitations
|
||||
|
||||
The current implementation has a number of limitations:
|
||||
|
||||
1) Multiple UDP sockets with the same 5-tuple address cannot be
|
||||
used. The kernel's tunnel context is identified using private
|
||||
data associated with the socket so it is important that each
|
||||
socket is uniquely identified by its address.
|
||||
|
||||
2) Interfacing with openvswitch is not yet implemented. It may be
|
||||
1) Interfacing with openvswitch is not yet implemented. It may be
|
||||
useful to map OVS Ethernet and VLAN ports into L2TPv3 tunnels.
|
||||
|
||||
3) VLAN pseudowires are implemented using an ``l2tpethN`` interface
|
||||
2) VLAN pseudowires are implemented using an ``l2tpethN`` interface
|
||||
configured with a VLAN sub-interface. Since L2TPv3 VLAN
|
||||
pseudowires carry one and only one VLAN, it may be better to use
|
||||
a single netdevice rather than an ``l2tpethN`` and ``l2tpethN``:M
|
||||
|
||||
+135
-52
@@ -117,12 +117,12 @@ struct l2tp_net {
|
||||
struct hlist_head l2tp_v3_session_htable[16];
|
||||
};
|
||||
|
||||
static inline u32 l2tp_v2_session_key(u16 tunnel_id, u16 session_id)
|
||||
static u32 l2tp_v2_session_key(u16 tunnel_id, u16 session_id)
|
||||
{
|
||||
return ((u32)tunnel_id) << 16 | session_id;
|
||||
}
|
||||
|
||||
static inline unsigned long l2tp_v3_session_hashkey(struct sock *sk, u32 session_id)
|
||||
static unsigned long l2tp_v3_session_hashkey(struct sock *sk, u32 session_id)
|
||||
{
|
||||
return ((unsigned long)sk) + session_id;
|
||||
}
|
||||
@@ -135,7 +135,7 @@ static bool l2tp_sk_is_v6(struct sock *sk)
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline struct l2tp_net *l2tp_pernet(const struct net *net)
|
||||
static struct l2tp_net *l2tp_pernet(const struct net *net)
|
||||
{
|
||||
return net_generic(net, l2tp_net_id);
|
||||
}
|
||||
@@ -170,7 +170,7 @@ static void l2tp_session_free(struct l2tp_session *session)
|
||||
{
|
||||
trace_free_session(session);
|
||||
if (session->tunnel)
|
||||
l2tp_tunnel_dec_refcount(session->tunnel);
|
||||
l2tp_tunnel_put(session->tunnel);
|
||||
kfree_rcu(session, rcu);
|
||||
}
|
||||
|
||||
@@ -197,31 +197,19 @@ struct l2tp_tunnel *l2tp_sk_to_tunnel(const struct sock *sk)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(l2tp_sk_to_tunnel);
|
||||
|
||||
void l2tp_tunnel_inc_refcount(struct l2tp_tunnel *tunnel)
|
||||
{
|
||||
refcount_inc(&tunnel->ref_count);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(l2tp_tunnel_inc_refcount);
|
||||
|
||||
void l2tp_tunnel_dec_refcount(struct l2tp_tunnel *tunnel)
|
||||
void l2tp_tunnel_put(struct l2tp_tunnel *tunnel)
|
||||
{
|
||||
if (refcount_dec_and_test(&tunnel->ref_count))
|
||||
l2tp_tunnel_free(tunnel);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(l2tp_tunnel_dec_refcount);
|
||||
EXPORT_SYMBOL_GPL(l2tp_tunnel_put);
|
||||
|
||||
void l2tp_session_inc_refcount(struct l2tp_session *session)
|
||||
{
|
||||
refcount_inc(&session->ref_count);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(l2tp_session_inc_refcount);
|
||||
|
||||
void l2tp_session_dec_refcount(struct l2tp_session *session)
|
||||
void l2tp_session_put(struct l2tp_session *session)
|
||||
{
|
||||
if (refcount_dec_and_test(&session->ref_count))
|
||||
l2tp_session_free(session);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(l2tp_session_dec_refcount);
|
||||
EXPORT_SYMBOL_GPL(l2tp_session_put);
|
||||
|
||||
/* Lookup a tunnel. A new reference is held on the returned tunnel. */
|
||||
struct l2tp_tunnel *l2tp_tunnel_get(const struct net *net, u32 tunnel_id)
|
||||
@@ -241,26 +229,27 @@ struct l2tp_tunnel *l2tp_tunnel_get(const struct net *net, u32 tunnel_id)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(l2tp_tunnel_get);
|
||||
|
||||
struct l2tp_tunnel *l2tp_tunnel_get_nth(const struct net *net, int nth)
|
||||
struct l2tp_tunnel *l2tp_tunnel_get_next(const struct net *net, unsigned long *key)
|
||||
{
|
||||
struct l2tp_net *pn = l2tp_pernet(net);
|
||||
unsigned long tunnel_id, tmp;
|
||||
struct l2tp_tunnel *tunnel;
|
||||
int count = 0;
|
||||
struct l2tp_tunnel *tunnel = NULL;
|
||||
|
||||
rcu_read_lock_bh();
|
||||
idr_for_each_entry_ul(&pn->l2tp_tunnel_idr, tunnel, tmp, tunnel_id) {
|
||||
if (tunnel && ++count > nth &&
|
||||
refcount_inc_not_zero(&tunnel->ref_count)) {
|
||||
again:
|
||||
tunnel = idr_get_next_ul(&pn->l2tp_tunnel_idr, key);
|
||||
if (tunnel) {
|
||||
if (refcount_inc_not_zero(&tunnel->ref_count)) {
|
||||
rcu_read_unlock_bh();
|
||||
return tunnel;
|
||||
}
|
||||
(*key)++;
|
||||
goto again;
|
||||
}
|
||||
rcu_read_unlock_bh();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(l2tp_tunnel_get_nth);
|
||||
EXPORT_SYMBOL_GPL(l2tp_tunnel_get_next);
|
||||
|
||||
struct l2tp_session *l2tp_v3_session_get(const struct net *net, struct sock *sk, u32 session_id)
|
||||
{
|
||||
@@ -291,7 +280,8 @@ struct l2tp_session *l2tp_v3_session_get(const struct net *net, struct sock *sk,
|
||||
*/
|
||||
struct l2tp_tunnel *tunnel = READ_ONCE(session->tunnel);
|
||||
|
||||
if (tunnel && tunnel->sock == sk &&
|
||||
if (session->session_id == session_id &&
|
||||
tunnel && tunnel->sock == sk &&
|
||||
refcount_inc_not_zero(&session->ref_count)) {
|
||||
rcu_read_unlock_bh();
|
||||
return session;
|
||||
@@ -332,24 +322,109 @@ struct l2tp_session *l2tp_session_get(const struct net *net, struct sock *sk, in
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(l2tp_session_get);
|
||||
|
||||
struct l2tp_session *l2tp_session_get_nth(struct l2tp_tunnel *tunnel, int nth)
|
||||
static struct l2tp_session *l2tp_v2_session_get_next(const struct net *net,
|
||||
u16 tid,
|
||||
unsigned long *key)
|
||||
{
|
||||
struct l2tp_session *session;
|
||||
int count = 0;
|
||||
struct l2tp_net *pn = l2tp_pernet(net);
|
||||
struct l2tp_session *session = NULL;
|
||||
|
||||
/* Start searching within the range of the tid */
|
||||
if (*key == 0)
|
||||
*key = l2tp_v2_session_key(tid, 0);
|
||||
|
||||
rcu_read_lock_bh();
|
||||
list_for_each_entry_rcu(session, &tunnel->session_list, list) {
|
||||
if (++count > nth) {
|
||||
l2tp_session_inc_refcount(session);
|
||||
again:
|
||||
session = idr_get_next_ul(&pn->l2tp_v2_session_idr, key);
|
||||
if (session) {
|
||||
struct l2tp_tunnel *tunnel = READ_ONCE(session->tunnel);
|
||||
|
||||
/* ignore sessions with id 0 as they are internal for pppol2tp */
|
||||
if (session->session_id == 0) {
|
||||
(*key)++;
|
||||
goto again;
|
||||
}
|
||||
|
||||
if (tunnel && tunnel->tunnel_id == tid &&
|
||||
refcount_inc_not_zero(&session->ref_count)) {
|
||||
rcu_read_unlock_bh();
|
||||
return session;
|
||||
}
|
||||
|
||||
(*key)++;
|
||||
if (tunnel->tunnel_id == tid)
|
||||
goto again;
|
||||
}
|
||||
rcu_read_unlock_bh();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(l2tp_session_get_nth);
|
||||
|
||||
static struct l2tp_session *l2tp_v3_session_get_next(const struct net *net,
|
||||
u32 tid, struct sock *sk,
|
||||
unsigned long *key)
|
||||
{
|
||||
struct l2tp_net *pn = l2tp_pernet(net);
|
||||
struct l2tp_session *session = NULL;
|
||||
|
||||
rcu_read_lock_bh();
|
||||
again:
|
||||
session = idr_get_next_ul(&pn->l2tp_v3_session_idr, key);
|
||||
if (session && !hash_hashed(&session->hlist)) {
|
||||
struct l2tp_tunnel *tunnel = READ_ONCE(session->tunnel);
|
||||
|
||||
if (tunnel && tunnel->tunnel_id == tid &&
|
||||
refcount_inc_not_zero(&session->ref_count)) {
|
||||
rcu_read_unlock_bh();
|
||||
return session;
|
||||
}
|
||||
|
||||
(*key)++;
|
||||
goto again;
|
||||
}
|
||||
|
||||
/* If we get here and session is non-NULL, the IDR entry may be one
|
||||
* where the session_id collides with one in another tunnel. Check
|
||||
* session_htable for a match. There can only be one session of a given
|
||||
* ID per tunnel so we can return as soon as a match is found.
|
||||
*/
|
||||
if (session && hash_hashed(&session->hlist)) {
|
||||
unsigned long hkey = l2tp_v3_session_hashkey(sk, session->session_id);
|
||||
u32 sid = session->session_id;
|
||||
|
||||
hash_for_each_possible_rcu(pn->l2tp_v3_session_htable, session,
|
||||
hlist, hkey) {
|
||||
struct l2tp_tunnel *tunnel = READ_ONCE(session->tunnel);
|
||||
|
||||
if (session->session_id == sid &&
|
||||
tunnel && tunnel->tunnel_id == tid &&
|
||||
refcount_inc_not_zero(&session->ref_count)) {
|
||||
rcu_read_unlock_bh();
|
||||
return session;
|
||||
}
|
||||
}
|
||||
|
||||
/* If no match found, the colliding session ID isn't in our
|
||||
* tunnel so try the next session ID.
|
||||
*/
|
||||
(*key)++;
|
||||
goto again;
|
||||
}
|
||||
|
||||
rcu_read_unlock_bh();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct l2tp_session *l2tp_session_get_next(const struct net *net, struct sock *sk, int pver,
|
||||
u32 tunnel_id, unsigned long *key)
|
||||
{
|
||||
if (pver == L2TP_HDR_VER_2)
|
||||
return l2tp_v2_session_get_next(net, tunnel_id, key);
|
||||
else
|
||||
return l2tp_v3_session_get_next(net, tunnel_id, sk, key);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(l2tp_session_get_next);
|
||||
|
||||
/* Lookup a session by interface name.
|
||||
* This is very inefficient but is only used by management interfaces.
|
||||
@@ -367,7 +442,7 @@ struct l2tp_session *l2tp_session_get_by_ifname(const struct net *net,
|
||||
if (tunnel) {
|
||||
list_for_each_entry_rcu(session, &tunnel->session_list, list) {
|
||||
if (!strcmp(session->ifname, ifname)) {
|
||||
l2tp_session_inc_refcount(session);
|
||||
refcount_inc(&session->ref_count);
|
||||
rcu_read_unlock_bh();
|
||||
|
||||
return session;
|
||||
@@ -384,7 +459,7 @@ EXPORT_SYMBOL_GPL(l2tp_session_get_by_ifname);
|
||||
static void l2tp_session_coll_list_add(struct l2tp_session_coll_list *clist,
|
||||
struct l2tp_session *session)
|
||||
{
|
||||
l2tp_session_inc_refcount(session);
|
||||
refcount_inc(&session->ref_count);
|
||||
WARN_ON_ONCE(session->coll_list);
|
||||
session->coll_list = clist;
|
||||
spin_lock(&clist->lock);
|
||||
@@ -470,7 +545,7 @@ static void l2tp_session_collision_del(struct l2tp_net *pn,
|
||||
spin_unlock(&clist->lock);
|
||||
if (refcount_dec_and_test(&clist->ref_count))
|
||||
kfree(clist);
|
||||
l2tp_session_dec_refcount(session);
|
||||
l2tp_session_put(session);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -519,7 +594,7 @@ int l2tp_session_register(struct l2tp_session *session,
|
||||
goto out;
|
||||
}
|
||||
|
||||
l2tp_tunnel_inc_refcount(tunnel);
|
||||
refcount_inc(&tunnel->ref_count);
|
||||
WRITE_ONCE(session->tunnel, tunnel);
|
||||
list_add_rcu(&session->list, &tunnel->session_list);
|
||||
|
||||
@@ -1002,7 +1077,7 @@ int l2tp_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
|
||||
|
||||
if (!session || !session->recv_skb) {
|
||||
if (session)
|
||||
l2tp_session_dec_refcount(session);
|
||||
l2tp_session_put(session);
|
||||
|
||||
/* Not found? Pass to userspace to deal with */
|
||||
goto pass;
|
||||
@@ -1016,12 +1091,12 @@ int l2tp_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
|
||||
|
||||
if (version == L2TP_HDR_VER_3 &&
|
||||
l2tp_v3_ensure_opt_in_linear(session, skb, &ptr, &optr)) {
|
||||
l2tp_session_dec_refcount(session);
|
||||
l2tp_session_put(session);
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
l2tp_recv_common(session, skb, ptr, optr, hdrflags, length);
|
||||
l2tp_session_dec_refcount(session);
|
||||
l2tp_session_put(session);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -1321,7 +1396,7 @@ static void l2tp_udp_encap_destroy(struct sock *sk)
|
||||
tunnel = l2tp_sk_to_tunnel(sk);
|
||||
if (tunnel) {
|
||||
l2tp_tunnel_delete(tunnel);
|
||||
l2tp_tunnel_dec_refcount(tunnel);
|
||||
l2tp_tunnel_put(tunnel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1356,10 +1431,10 @@ static void l2tp_tunnel_del_work(struct work_struct *work)
|
||||
|
||||
l2tp_tunnel_remove(tunnel->l2tp_net, tunnel);
|
||||
/* drop initial ref */
|
||||
l2tp_tunnel_dec_refcount(tunnel);
|
||||
l2tp_tunnel_put(tunnel);
|
||||
|
||||
/* drop workqueue ref */
|
||||
l2tp_tunnel_dec_refcount(tunnel);
|
||||
l2tp_tunnel_put(tunnel);
|
||||
}
|
||||
|
||||
/* Create a socket for the tunnel, if one isn't set up by
|
||||
@@ -1547,7 +1622,7 @@ static int l2tp_validate_socket(const struct sock *sk, const struct net *net,
|
||||
|
||||
tunnel = l2tp_sk_to_tunnel(sk);
|
||||
if (tunnel) {
|
||||
l2tp_tunnel_dec_refcount(tunnel);
|
||||
l2tp_tunnel_put(tunnel);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
@@ -1639,7 +1714,7 @@ void l2tp_tunnel_delete(struct l2tp_tunnel *tunnel)
|
||||
{
|
||||
if (!test_and_set_bit(0, &tunnel->dead)) {
|
||||
trace_delete_tunnel(tunnel);
|
||||
l2tp_tunnel_inc_refcount(tunnel);
|
||||
refcount_inc(&tunnel->ref_count);
|
||||
queue_work(l2tp_wq, &tunnel->del_work);
|
||||
}
|
||||
}
|
||||
@@ -1649,7 +1724,7 @@ void l2tp_session_delete(struct l2tp_session *session)
|
||||
{
|
||||
if (!test_and_set_bit(0, &session->dead)) {
|
||||
trace_delete_session(session);
|
||||
l2tp_session_inc_refcount(session);
|
||||
refcount_inc(&session->ref_count);
|
||||
queue_work(l2tp_wq, &session->del_work);
|
||||
}
|
||||
}
|
||||
@@ -1667,10 +1742,10 @@ static void l2tp_session_del_work(struct work_struct *work)
|
||||
(*session->session_close)(session);
|
||||
|
||||
/* drop initial ref */
|
||||
l2tp_session_dec_refcount(session);
|
||||
l2tp_session_put(session);
|
||||
|
||||
/* drop workqueue ref */
|
||||
l2tp_session_dec_refcount(session);
|
||||
l2tp_session_put(session);
|
||||
}
|
||||
|
||||
/* We come here whenever a session's send_seq, cookie_len or
|
||||
@@ -1780,8 +1855,16 @@ static __net_exit void l2tp_pre_exit_net(struct net *net)
|
||||
}
|
||||
rcu_read_unlock_bh();
|
||||
|
||||
if (l2tp_wq)
|
||||
if (l2tp_wq) {
|
||||
/* ensure that all TUNNEL_DELETE work items are run before
|
||||
* draining the work queue since TUNNEL_DELETE requests may
|
||||
* queue SESSION_DELETE work items for each session in the
|
||||
* tunnel. drain_workqueue may otherwise warn if SESSION_DELETE
|
||||
* requests are queued while the work queue is being drained.
|
||||
*/
|
||||
__flush_workqueue(l2tp_wq);
|
||||
drain_workqueue(l2tp_wq);
|
||||
}
|
||||
}
|
||||
|
||||
static __net_exit void l2tp_exit_net(struct net *net)
|
||||
|
||||
@@ -209,23 +209,22 @@ static inline void *l2tp_session_priv(struct l2tp_session *session)
|
||||
}
|
||||
|
||||
/* Tunnel and session refcounts */
|
||||
void l2tp_tunnel_inc_refcount(struct l2tp_tunnel *tunnel);
|
||||
void l2tp_tunnel_dec_refcount(struct l2tp_tunnel *tunnel);
|
||||
void l2tp_session_inc_refcount(struct l2tp_session *session);
|
||||
void l2tp_session_dec_refcount(struct l2tp_session *session);
|
||||
void l2tp_tunnel_put(struct l2tp_tunnel *tunnel);
|
||||
void l2tp_session_put(struct l2tp_session *session);
|
||||
|
||||
/* Tunnel and session lookup.
|
||||
* These functions take a reference on the instances they return, so
|
||||
* the caller must ensure that the reference is dropped appropriately.
|
||||
*/
|
||||
struct l2tp_tunnel *l2tp_tunnel_get(const struct net *net, u32 tunnel_id);
|
||||
struct l2tp_tunnel *l2tp_tunnel_get_nth(const struct net *net, int nth);
|
||||
struct l2tp_tunnel *l2tp_tunnel_get_next(const struct net *net, unsigned long *key);
|
||||
|
||||
struct l2tp_session *l2tp_v3_session_get(const struct net *net, struct sock *sk, u32 session_id);
|
||||
struct l2tp_session *l2tp_v2_session_get(const struct net *net, u16 tunnel_id, u16 session_id);
|
||||
struct l2tp_session *l2tp_session_get(const struct net *net, struct sock *sk, int pver,
|
||||
u32 tunnel_id, u32 session_id);
|
||||
struct l2tp_session *l2tp_session_get_nth(struct l2tp_tunnel *tunnel, int nth);
|
||||
struct l2tp_session *l2tp_session_get_next(const struct net *net, struct sock *sk, int pver,
|
||||
u32 tunnel_id, unsigned long *key);
|
||||
struct l2tp_session *l2tp_session_get_by_ifname(const struct net *net,
|
||||
const char *ifname);
|
||||
|
||||
|
||||
+13
-11
@@ -34,8 +34,8 @@ static struct dentry *rootdir;
|
||||
struct l2tp_dfs_seq_data {
|
||||
struct net *net;
|
||||
netns_tracker ns_tracker;
|
||||
int tunnel_idx; /* current tunnel */
|
||||
int session_idx; /* index of session within current tunnel */
|
||||
unsigned long tkey; /* lookup key of current tunnel */
|
||||
unsigned long skey; /* lookup key of current session */
|
||||
struct l2tp_tunnel *tunnel;
|
||||
struct l2tp_session *session; /* NULL means get next tunnel */
|
||||
};
|
||||
@@ -44,23 +44,25 @@ static void l2tp_dfs_next_tunnel(struct l2tp_dfs_seq_data *pd)
|
||||
{
|
||||
/* Drop reference taken during previous invocation */
|
||||
if (pd->tunnel)
|
||||
l2tp_tunnel_dec_refcount(pd->tunnel);
|
||||
l2tp_tunnel_put(pd->tunnel);
|
||||
|
||||
pd->tunnel = l2tp_tunnel_get_nth(pd->net, pd->tunnel_idx);
|
||||
pd->tunnel_idx++;
|
||||
pd->tunnel = l2tp_tunnel_get_next(pd->net, &pd->tkey);
|
||||
pd->tkey++;
|
||||
}
|
||||
|
||||
static void l2tp_dfs_next_session(struct l2tp_dfs_seq_data *pd)
|
||||
{
|
||||
/* Drop reference taken during previous invocation */
|
||||
if (pd->session)
|
||||
l2tp_session_dec_refcount(pd->session);
|
||||
l2tp_session_put(pd->session);
|
||||
|
||||
pd->session = l2tp_session_get_nth(pd->tunnel, pd->session_idx);
|
||||
pd->session_idx++;
|
||||
pd->session = l2tp_session_get_next(pd->net, pd->tunnel->sock,
|
||||
pd->tunnel->version,
|
||||
pd->tunnel->tunnel_id, &pd->skey);
|
||||
pd->skey++;
|
||||
|
||||
if (!pd->session) {
|
||||
pd->session_idx = 0;
|
||||
pd->skey = 0;
|
||||
l2tp_dfs_next_tunnel(pd);
|
||||
}
|
||||
}
|
||||
@@ -109,11 +111,11 @@ static void l2tp_dfs_seq_stop(struct seq_file *p, void *v)
|
||||
* or l2tp_dfs_next_tunnel().
|
||||
*/
|
||||
if (pd->session) {
|
||||
l2tp_session_dec_refcount(pd->session);
|
||||
l2tp_session_put(pd->session);
|
||||
pd->session = NULL;
|
||||
}
|
||||
if (pd->tunnel) {
|
||||
l2tp_tunnel_dec_refcount(pd->tunnel);
|
||||
l2tp_tunnel_put(pd->tunnel);
|
||||
pd->tunnel = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
+15
-27
@@ -72,31 +72,19 @@ static netdev_tx_t l2tp_eth_dev_xmit(struct sk_buff *skb, struct net_device *dev
|
||||
unsigned int len = skb->len;
|
||||
int ret = l2tp_xmit_skb(session, skb);
|
||||
|
||||
if (likely(ret == NET_XMIT_SUCCESS)) {
|
||||
DEV_STATS_ADD(dev, tx_bytes, len);
|
||||
DEV_STATS_INC(dev, tx_packets);
|
||||
} else {
|
||||
if (likely(ret == NET_XMIT_SUCCESS))
|
||||
dev_sw_netstats_tx_add(dev, 1, len);
|
||||
else
|
||||
DEV_STATS_INC(dev, tx_dropped);
|
||||
}
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
static void l2tp_eth_get_stats64(struct net_device *dev,
|
||||
struct rtnl_link_stats64 *stats)
|
||||
{
|
||||
stats->tx_bytes = DEV_STATS_READ(dev, tx_bytes);
|
||||
stats->tx_packets = DEV_STATS_READ(dev, tx_packets);
|
||||
stats->tx_dropped = DEV_STATS_READ(dev, tx_dropped);
|
||||
stats->rx_bytes = DEV_STATS_READ(dev, rx_bytes);
|
||||
stats->rx_packets = DEV_STATS_READ(dev, rx_packets);
|
||||
stats->rx_errors = DEV_STATS_READ(dev, rx_errors);
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
static const struct net_device_ops l2tp_eth_netdev_ops = {
|
||||
.ndo_init = l2tp_eth_dev_init,
|
||||
.ndo_uninit = l2tp_eth_dev_uninit,
|
||||
.ndo_start_xmit = l2tp_eth_dev_xmit,
|
||||
.ndo_get_stats64 = l2tp_eth_get_stats64,
|
||||
.ndo_get_stats64 = dev_get_tstats64,
|
||||
.ndo_set_mac_address = eth_mac_addr,
|
||||
};
|
||||
|
||||
@@ -112,6 +100,7 @@ static void l2tp_eth_dev_setup(struct net_device *dev)
|
||||
dev->features |= NETIF_F_LLTX;
|
||||
dev->netdev_ops = &l2tp_eth_netdev_ops;
|
||||
dev->needs_free_netdev = true;
|
||||
dev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS;
|
||||
}
|
||||
|
||||
static void l2tp_eth_dev_recv(struct l2tp_session *session, struct sk_buff *skb, int data_len)
|
||||
@@ -138,12 +127,11 @@ static void l2tp_eth_dev_recv(struct l2tp_session *session, struct sk_buff *skb,
|
||||
if (!dev)
|
||||
goto error_rcu;
|
||||
|
||||
if (dev_forward_skb(dev, skb) == NET_RX_SUCCESS) {
|
||||
DEV_STATS_INC(dev, rx_packets);
|
||||
DEV_STATS_ADD(dev, rx_bytes, data_len);
|
||||
} else {
|
||||
if (dev_forward_skb(dev, skb) == NET_RX_SUCCESS)
|
||||
dev_sw_netstats_rx_add(dev, data_len);
|
||||
else
|
||||
DEV_STATS_INC(dev, rx_errors);
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
return;
|
||||
@@ -283,7 +271,7 @@ static int l2tp_eth_create(struct net *net, struct l2tp_tunnel *tunnel,
|
||||
|
||||
spriv = l2tp_session_priv(session);
|
||||
|
||||
l2tp_session_inc_refcount(session);
|
||||
refcount_inc(&session->ref_count);
|
||||
|
||||
rtnl_lock();
|
||||
|
||||
@@ -301,7 +289,7 @@ static int l2tp_eth_create(struct net *net, struct l2tp_tunnel *tunnel,
|
||||
if (rc < 0) {
|
||||
rtnl_unlock();
|
||||
l2tp_session_delete(session);
|
||||
l2tp_session_dec_refcount(session);
|
||||
l2tp_session_put(session);
|
||||
free_netdev(dev);
|
||||
|
||||
return rc;
|
||||
@@ -312,17 +300,17 @@ static int l2tp_eth_create(struct net *net, struct l2tp_tunnel *tunnel,
|
||||
|
||||
rtnl_unlock();
|
||||
|
||||
l2tp_session_dec_refcount(session);
|
||||
l2tp_session_put(session);
|
||||
|
||||
__module_get(THIS_MODULE);
|
||||
|
||||
return 0;
|
||||
|
||||
err_sess_dev:
|
||||
l2tp_session_dec_refcount(session);
|
||||
l2tp_session_put(session);
|
||||
free_netdev(dev);
|
||||
err_sess:
|
||||
l2tp_session_dec_refcount(session);
|
||||
l2tp_session_put(session);
|
||||
err:
|
||||
return rc;
|
||||
}
|
||||
|
||||
+87
-29
@@ -22,9 +22,19 @@
|
||||
#include <net/tcp_states.h>
|
||||
#include <net/protocol.h>
|
||||
#include <net/xfrm.h>
|
||||
#include <net/net_namespace.h>
|
||||
#include <net/netns/generic.h>
|
||||
|
||||
#include "l2tp_core.h"
|
||||
|
||||
/* per-net private data for this module */
|
||||
static unsigned int l2tp_ip_net_id;
|
||||
struct l2tp_ip_net {
|
||||
rwlock_t l2tp_ip_lock;
|
||||
struct hlist_head l2tp_ip_table;
|
||||
struct hlist_head l2tp_ip_bind_table;
|
||||
};
|
||||
|
||||
struct l2tp_ip_sock {
|
||||
/* inet_sock has to be the first member of l2tp_ip_sock */
|
||||
struct inet_sock inet;
|
||||
@@ -33,21 +43,23 @@ struct l2tp_ip_sock {
|
||||
u32 peer_conn_id;
|
||||
};
|
||||
|
||||
static DEFINE_RWLOCK(l2tp_ip_lock);
|
||||
static struct hlist_head l2tp_ip_table;
|
||||
static struct hlist_head l2tp_ip_bind_table;
|
||||
|
||||
static inline struct l2tp_ip_sock *l2tp_ip_sk(const struct sock *sk)
|
||||
static struct l2tp_ip_sock *l2tp_ip_sk(const struct sock *sk)
|
||||
{
|
||||
return (struct l2tp_ip_sock *)sk;
|
||||
}
|
||||
|
||||
static struct l2tp_ip_net *l2tp_ip_pernet(const struct net *net)
|
||||
{
|
||||
return net_generic(net, l2tp_ip_net_id);
|
||||
}
|
||||
|
||||
static struct sock *__l2tp_ip_bind_lookup(const struct net *net, __be32 laddr,
|
||||
__be32 raddr, int dif, u32 tunnel_id)
|
||||
{
|
||||
struct l2tp_ip_net *pn = l2tp_ip_pernet(net);
|
||||
struct sock *sk;
|
||||
|
||||
sk_for_each_bound(sk, &l2tp_ip_bind_table) {
|
||||
sk_for_each_bound(sk, &pn->l2tp_ip_bind_table) {
|
||||
const struct l2tp_ip_sock *l2tp = l2tp_ip_sk(sk);
|
||||
const struct inet_sock *inet = inet_sk(sk);
|
||||
int bound_dev_if;
|
||||
@@ -113,6 +125,7 @@ found:
|
||||
static int l2tp_ip_recv(struct sk_buff *skb)
|
||||
{
|
||||
struct net *net = dev_net(skb->dev);
|
||||
struct l2tp_ip_net *pn;
|
||||
struct sock *sk;
|
||||
u32 session_id;
|
||||
u32 tunnel_id;
|
||||
@@ -121,6 +134,8 @@ static int l2tp_ip_recv(struct sk_buff *skb)
|
||||
struct l2tp_tunnel *tunnel = NULL;
|
||||
struct iphdr *iph;
|
||||
|
||||
pn = l2tp_ip_pernet(net);
|
||||
|
||||
if (!pskb_may_pull(skb, 4))
|
||||
goto discard;
|
||||
|
||||
@@ -152,7 +167,7 @@ static int l2tp_ip_recv(struct sk_buff *skb)
|
||||
goto discard_sess;
|
||||
|
||||
l2tp_recv_common(session, skb, ptr, optr, 0, skb->len);
|
||||
l2tp_session_dec_refcount(session);
|
||||
l2tp_session_put(session);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -167,15 +182,15 @@ pass_up:
|
||||
tunnel_id = ntohl(*(__be32 *)&skb->data[4]);
|
||||
iph = (struct iphdr *)skb_network_header(skb);
|
||||
|
||||
read_lock_bh(&l2tp_ip_lock);
|
||||
read_lock_bh(&pn->l2tp_ip_lock);
|
||||
sk = __l2tp_ip_bind_lookup(net, iph->daddr, iph->saddr, inet_iif(skb),
|
||||
tunnel_id);
|
||||
if (!sk) {
|
||||
read_unlock_bh(&l2tp_ip_lock);
|
||||
read_unlock_bh(&pn->l2tp_ip_lock);
|
||||
goto discard;
|
||||
}
|
||||
sock_hold(sk);
|
||||
read_unlock_bh(&l2tp_ip_lock);
|
||||
read_unlock_bh(&pn->l2tp_ip_lock);
|
||||
|
||||
if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb))
|
||||
goto discard_put;
|
||||
@@ -185,7 +200,7 @@ pass_up:
|
||||
return sk_receive_skb(sk, skb, 1);
|
||||
|
||||
discard_sess:
|
||||
l2tp_session_dec_refcount(session);
|
||||
l2tp_session_put(session);
|
||||
goto discard;
|
||||
|
||||
discard_put:
|
||||
@@ -198,21 +213,25 @@ discard:
|
||||
|
||||
static int l2tp_ip_hash(struct sock *sk)
|
||||
{
|
||||
struct l2tp_ip_net *pn = l2tp_ip_pernet(sock_net(sk));
|
||||
|
||||
if (sk_unhashed(sk)) {
|
||||
write_lock_bh(&l2tp_ip_lock);
|
||||
sk_add_node(sk, &l2tp_ip_table);
|
||||
write_unlock_bh(&l2tp_ip_lock);
|
||||
write_lock_bh(&pn->l2tp_ip_lock);
|
||||
sk_add_node(sk, &pn->l2tp_ip_table);
|
||||
write_unlock_bh(&pn->l2tp_ip_lock);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void l2tp_ip_unhash(struct sock *sk)
|
||||
{
|
||||
struct l2tp_ip_net *pn = l2tp_ip_pernet(sock_net(sk));
|
||||
|
||||
if (sk_unhashed(sk))
|
||||
return;
|
||||
write_lock_bh(&l2tp_ip_lock);
|
||||
write_lock_bh(&pn->l2tp_ip_lock);
|
||||
sk_del_node_init(sk);
|
||||
write_unlock_bh(&l2tp_ip_lock);
|
||||
write_unlock_bh(&pn->l2tp_ip_lock);
|
||||
}
|
||||
|
||||
static int l2tp_ip_open(struct sock *sk)
|
||||
@@ -226,10 +245,12 @@ static int l2tp_ip_open(struct sock *sk)
|
||||
|
||||
static void l2tp_ip_close(struct sock *sk, long timeout)
|
||||
{
|
||||
write_lock_bh(&l2tp_ip_lock);
|
||||
struct l2tp_ip_net *pn = l2tp_ip_pernet(sock_net(sk));
|
||||
|
||||
write_lock_bh(&pn->l2tp_ip_lock);
|
||||
hlist_del_init(&sk->sk_bind_node);
|
||||
sk_del_node_init(sk);
|
||||
write_unlock_bh(&l2tp_ip_lock);
|
||||
write_unlock_bh(&pn->l2tp_ip_lock);
|
||||
sk_common_release(sk);
|
||||
}
|
||||
|
||||
@@ -244,7 +265,7 @@ static void l2tp_ip_destroy_sock(struct sock *sk)
|
||||
tunnel = l2tp_sk_to_tunnel(sk);
|
||||
if (tunnel) {
|
||||
l2tp_tunnel_delete(tunnel);
|
||||
l2tp_tunnel_dec_refcount(tunnel);
|
||||
l2tp_tunnel_put(tunnel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,6 +274,7 @@ static int l2tp_ip_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
|
||||
struct inet_sock *inet = inet_sk(sk);
|
||||
struct sockaddr_l2tpip *addr = (struct sockaddr_l2tpip *)uaddr;
|
||||
struct net *net = sock_net(sk);
|
||||
struct l2tp_ip_net *pn;
|
||||
int ret;
|
||||
int chk_addr_ret;
|
||||
|
||||
@@ -283,10 +305,11 @@ static int l2tp_ip_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
|
||||
if (chk_addr_ret == RTN_MULTICAST || chk_addr_ret == RTN_BROADCAST)
|
||||
inet->inet_saddr = 0; /* Use device */
|
||||
|
||||
write_lock_bh(&l2tp_ip_lock);
|
||||
pn = l2tp_ip_pernet(net);
|
||||
write_lock_bh(&pn->l2tp_ip_lock);
|
||||
if (__l2tp_ip_bind_lookup(net, addr->l2tp_addr.s_addr, 0,
|
||||
sk->sk_bound_dev_if, addr->l2tp_conn_id)) {
|
||||
write_unlock_bh(&l2tp_ip_lock);
|
||||
write_unlock_bh(&pn->l2tp_ip_lock);
|
||||
ret = -EADDRINUSE;
|
||||
goto out;
|
||||
}
|
||||
@@ -294,9 +317,9 @@ static int l2tp_ip_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
|
||||
sk_dst_reset(sk);
|
||||
l2tp_ip_sk(sk)->conn_id = addr->l2tp_conn_id;
|
||||
|
||||
sk_add_bind_node(sk, &l2tp_ip_bind_table);
|
||||
sk_add_bind_node(sk, &pn->l2tp_ip_bind_table);
|
||||
sk_del_node_init(sk);
|
||||
write_unlock_bh(&l2tp_ip_lock);
|
||||
write_unlock_bh(&pn->l2tp_ip_lock);
|
||||
|
||||
ret = 0;
|
||||
sock_reset_flag(sk, SOCK_ZAPPED);
|
||||
@@ -310,6 +333,7 @@ out:
|
||||
static int l2tp_ip_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
|
||||
{
|
||||
struct sockaddr_l2tpip *lsa = (struct sockaddr_l2tpip *)uaddr;
|
||||
struct l2tp_ip_net *pn = l2tp_ip_pernet(sock_net(sk));
|
||||
int rc;
|
||||
|
||||
if (addr_len < sizeof(*lsa))
|
||||
@@ -332,10 +356,10 @@ static int l2tp_ip_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len
|
||||
|
||||
l2tp_ip_sk(sk)->peer_conn_id = lsa->l2tp_conn_id;
|
||||
|
||||
write_lock_bh(&l2tp_ip_lock);
|
||||
write_lock_bh(&pn->l2tp_ip_lock);
|
||||
hlist_del_init(&sk->sk_bind_node);
|
||||
sk_add_bind_node(sk, &l2tp_ip_bind_table);
|
||||
write_unlock_bh(&l2tp_ip_lock);
|
||||
sk_add_bind_node(sk, &pn->l2tp_ip_bind_table);
|
||||
write_unlock_bh(&pn->l2tp_ip_lock);
|
||||
|
||||
out_sk:
|
||||
release_sock(sk);
|
||||
@@ -640,25 +664,58 @@ static struct net_protocol l2tp_ip_protocol __read_mostly = {
|
||||
.handler = l2tp_ip_recv,
|
||||
};
|
||||
|
||||
static __net_init int l2tp_ip_init_net(struct net *net)
|
||||
{
|
||||
struct l2tp_ip_net *pn = net_generic(net, l2tp_ip_net_id);
|
||||
|
||||
rwlock_init(&pn->l2tp_ip_lock);
|
||||
INIT_HLIST_HEAD(&pn->l2tp_ip_table);
|
||||
INIT_HLIST_HEAD(&pn->l2tp_ip_bind_table);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __net_exit void l2tp_ip_exit_net(struct net *net)
|
||||
{
|
||||
struct l2tp_ip_net *pn = l2tp_ip_pernet(net);
|
||||
|
||||
write_lock_bh(&pn->l2tp_ip_lock);
|
||||
WARN_ON_ONCE(hlist_count_nodes(&pn->l2tp_ip_table) != 0);
|
||||
WARN_ON_ONCE(hlist_count_nodes(&pn->l2tp_ip_bind_table) != 0);
|
||||
write_unlock_bh(&pn->l2tp_ip_lock);
|
||||
}
|
||||
|
||||
static struct pernet_operations l2tp_ip_net_ops = {
|
||||
.init = l2tp_ip_init_net,
|
||||
.exit = l2tp_ip_exit_net,
|
||||
.id = &l2tp_ip_net_id,
|
||||
.size = sizeof(struct l2tp_ip_net),
|
||||
};
|
||||
|
||||
static int __init l2tp_ip_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
pr_info("L2TP IP encapsulation support (L2TPv3)\n");
|
||||
|
||||
err = register_pernet_device(&l2tp_ip_net_ops);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = proto_register(&l2tp_ip_prot, 1);
|
||||
if (err != 0)
|
||||
goto out;
|
||||
goto out1;
|
||||
|
||||
err = inet_add_protocol(&l2tp_ip_protocol, IPPROTO_L2TP);
|
||||
if (err)
|
||||
goto out1;
|
||||
goto out2;
|
||||
|
||||
inet_register_protosw(&l2tp_ip_protosw);
|
||||
return 0;
|
||||
|
||||
out1:
|
||||
out2:
|
||||
proto_unregister(&l2tp_ip_prot);
|
||||
out1:
|
||||
unregister_pernet_device(&l2tp_ip_net_ops);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
@@ -668,6 +725,7 @@ static void __exit l2tp_ip_exit(void)
|
||||
inet_unregister_protosw(&l2tp_ip_protosw);
|
||||
inet_del_protocol(&l2tp_ip_protocol, IPPROTO_L2TP);
|
||||
proto_unregister(&l2tp_ip_prot);
|
||||
unregister_pernet_device(&l2tp_ip_net_ops);
|
||||
}
|
||||
|
||||
module_init(l2tp_ip_init);
|
||||
|
||||
+89
-29
@@ -22,6 +22,8 @@
|
||||
#include <net/tcp_states.h>
|
||||
#include <net/protocol.h>
|
||||
#include <net/xfrm.h>
|
||||
#include <net/net_namespace.h>
|
||||
#include <net/netns/generic.h>
|
||||
|
||||
#include <net/transp_v6.h>
|
||||
#include <net/addrconf.h>
|
||||
@@ -29,6 +31,14 @@
|
||||
|
||||
#include "l2tp_core.h"
|
||||
|
||||
/* per-net private data for this module */
|
||||
static unsigned int l2tp_ip6_net_id;
|
||||
struct l2tp_ip6_net {
|
||||
rwlock_t l2tp_ip6_lock;
|
||||
struct hlist_head l2tp_ip6_table;
|
||||
struct hlist_head l2tp_ip6_bind_table;
|
||||
};
|
||||
|
||||
struct l2tp_ip6_sock {
|
||||
/* inet_sock has to be the first member of l2tp_ip6_sock */
|
||||
struct inet_sock inet;
|
||||
@@ -39,23 +49,25 @@ struct l2tp_ip6_sock {
|
||||
struct ipv6_pinfo inet6;
|
||||
};
|
||||
|
||||
static DEFINE_RWLOCK(l2tp_ip6_lock);
|
||||
static struct hlist_head l2tp_ip6_table;
|
||||
static struct hlist_head l2tp_ip6_bind_table;
|
||||
|
||||
static inline struct l2tp_ip6_sock *l2tp_ip6_sk(const struct sock *sk)
|
||||
static struct l2tp_ip6_sock *l2tp_ip6_sk(const struct sock *sk)
|
||||
{
|
||||
return (struct l2tp_ip6_sock *)sk;
|
||||
}
|
||||
|
||||
static struct l2tp_ip6_net *l2tp_ip6_pernet(const struct net *net)
|
||||
{
|
||||
return net_generic(net, l2tp_ip6_net_id);
|
||||
}
|
||||
|
||||
static struct sock *__l2tp_ip6_bind_lookup(const struct net *net,
|
||||
const struct in6_addr *laddr,
|
||||
const struct in6_addr *raddr,
|
||||
int dif, u32 tunnel_id)
|
||||
{
|
||||
struct l2tp_ip6_net *pn = l2tp_ip6_pernet(net);
|
||||
struct sock *sk;
|
||||
|
||||
sk_for_each_bound(sk, &l2tp_ip6_bind_table) {
|
||||
sk_for_each_bound(sk, &pn->l2tp_ip6_bind_table) {
|
||||
const struct in6_addr *sk_laddr = inet6_rcv_saddr(sk);
|
||||
const struct in6_addr *sk_raddr = &sk->sk_v6_daddr;
|
||||
const struct l2tp_ip6_sock *l2tp = l2tp_ip6_sk(sk);
|
||||
@@ -123,6 +135,7 @@ found:
|
||||
static int l2tp_ip6_recv(struct sk_buff *skb)
|
||||
{
|
||||
struct net *net = dev_net(skb->dev);
|
||||
struct l2tp_ip6_net *pn;
|
||||
struct sock *sk;
|
||||
u32 session_id;
|
||||
u32 tunnel_id;
|
||||
@@ -131,6 +144,8 @@ static int l2tp_ip6_recv(struct sk_buff *skb)
|
||||
struct l2tp_tunnel *tunnel = NULL;
|
||||
struct ipv6hdr *iph;
|
||||
|
||||
pn = l2tp_ip6_pernet(net);
|
||||
|
||||
if (!pskb_may_pull(skb, 4))
|
||||
goto discard;
|
||||
|
||||
@@ -162,7 +177,7 @@ static int l2tp_ip6_recv(struct sk_buff *skb)
|
||||
goto discard_sess;
|
||||
|
||||
l2tp_recv_common(session, skb, ptr, optr, 0, skb->len);
|
||||
l2tp_session_dec_refcount(session);
|
||||
l2tp_session_put(session);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -177,15 +192,15 @@ pass_up:
|
||||
tunnel_id = ntohl(*(__be32 *)&skb->data[4]);
|
||||
iph = ipv6_hdr(skb);
|
||||
|
||||
read_lock_bh(&l2tp_ip6_lock);
|
||||
read_lock_bh(&pn->l2tp_ip6_lock);
|
||||
sk = __l2tp_ip6_bind_lookup(net, &iph->daddr, &iph->saddr,
|
||||
inet6_iif(skb), tunnel_id);
|
||||
if (!sk) {
|
||||
read_unlock_bh(&l2tp_ip6_lock);
|
||||
read_unlock_bh(&pn->l2tp_ip6_lock);
|
||||
goto discard;
|
||||
}
|
||||
sock_hold(sk);
|
||||
read_unlock_bh(&l2tp_ip6_lock);
|
||||
read_unlock_bh(&pn->l2tp_ip6_lock);
|
||||
|
||||
if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb))
|
||||
goto discard_put;
|
||||
@@ -195,7 +210,7 @@ pass_up:
|
||||
return sk_receive_skb(sk, skb, 1);
|
||||
|
||||
discard_sess:
|
||||
l2tp_session_dec_refcount(session);
|
||||
l2tp_session_put(session);
|
||||
goto discard;
|
||||
|
||||
discard_put:
|
||||
@@ -208,21 +223,25 @@ discard:
|
||||
|
||||
static int l2tp_ip6_hash(struct sock *sk)
|
||||
{
|
||||
struct l2tp_ip6_net *pn = l2tp_ip6_pernet(sock_net(sk));
|
||||
|
||||
if (sk_unhashed(sk)) {
|
||||
write_lock_bh(&l2tp_ip6_lock);
|
||||
sk_add_node(sk, &l2tp_ip6_table);
|
||||
write_unlock_bh(&l2tp_ip6_lock);
|
||||
write_lock_bh(&pn->l2tp_ip6_lock);
|
||||
sk_add_node(sk, &pn->l2tp_ip6_table);
|
||||
write_unlock_bh(&pn->l2tp_ip6_lock);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void l2tp_ip6_unhash(struct sock *sk)
|
||||
{
|
||||
struct l2tp_ip6_net *pn = l2tp_ip6_pernet(sock_net(sk));
|
||||
|
||||
if (sk_unhashed(sk))
|
||||
return;
|
||||
write_lock_bh(&l2tp_ip6_lock);
|
||||
write_lock_bh(&pn->l2tp_ip6_lock);
|
||||
sk_del_node_init(sk);
|
||||
write_unlock_bh(&l2tp_ip6_lock);
|
||||
write_unlock_bh(&pn->l2tp_ip6_lock);
|
||||
}
|
||||
|
||||
static int l2tp_ip6_open(struct sock *sk)
|
||||
@@ -236,10 +255,12 @@ static int l2tp_ip6_open(struct sock *sk)
|
||||
|
||||
static void l2tp_ip6_close(struct sock *sk, long timeout)
|
||||
{
|
||||
write_lock_bh(&l2tp_ip6_lock);
|
||||
struct l2tp_ip6_net *pn = l2tp_ip6_pernet(sock_net(sk));
|
||||
|
||||
write_lock_bh(&pn->l2tp_ip6_lock);
|
||||
hlist_del_init(&sk->sk_bind_node);
|
||||
sk_del_node_init(sk);
|
||||
write_unlock_bh(&l2tp_ip6_lock);
|
||||
write_unlock_bh(&pn->l2tp_ip6_lock);
|
||||
|
||||
sk_common_release(sk);
|
||||
}
|
||||
@@ -255,7 +276,7 @@ static void l2tp_ip6_destroy_sock(struct sock *sk)
|
||||
tunnel = l2tp_sk_to_tunnel(sk);
|
||||
if (tunnel) {
|
||||
l2tp_tunnel_delete(tunnel);
|
||||
l2tp_tunnel_dec_refcount(tunnel);
|
||||
l2tp_tunnel_put(tunnel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,11 +286,14 @@ static int l2tp_ip6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
|
||||
struct ipv6_pinfo *np = inet6_sk(sk);
|
||||
struct sockaddr_l2tpip6 *addr = (struct sockaddr_l2tpip6 *)uaddr;
|
||||
struct net *net = sock_net(sk);
|
||||
struct l2tp_ip6_net *pn;
|
||||
__be32 v4addr = 0;
|
||||
int bound_dev_if;
|
||||
int addr_type;
|
||||
int err;
|
||||
|
||||
pn = l2tp_ip6_pernet(net);
|
||||
|
||||
if (addr->l2tp_family != AF_INET6)
|
||||
return -EINVAL;
|
||||
if (addr_len < sizeof(*addr))
|
||||
@@ -327,10 +351,10 @@ static int l2tp_ip6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
write_lock_bh(&l2tp_ip6_lock);
|
||||
write_lock_bh(&pn->l2tp_ip6_lock);
|
||||
if (__l2tp_ip6_bind_lookup(net, &addr->l2tp_addr, NULL, bound_dev_if,
|
||||
addr->l2tp_conn_id)) {
|
||||
write_unlock_bh(&l2tp_ip6_lock);
|
||||
write_unlock_bh(&pn->l2tp_ip6_lock);
|
||||
err = -EADDRINUSE;
|
||||
goto out_unlock;
|
||||
}
|
||||
@@ -343,9 +367,9 @@ static int l2tp_ip6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
|
||||
|
||||
l2tp_ip6_sk(sk)->conn_id = addr->l2tp_conn_id;
|
||||
|
||||
sk_add_bind_node(sk, &l2tp_ip6_bind_table);
|
||||
sk_add_bind_node(sk, &pn->l2tp_ip6_bind_table);
|
||||
sk_del_node_init(sk);
|
||||
write_unlock_bh(&l2tp_ip6_lock);
|
||||
write_unlock_bh(&pn->l2tp_ip6_lock);
|
||||
|
||||
sock_reset_flag(sk, SOCK_ZAPPED);
|
||||
release_sock(sk);
|
||||
@@ -367,6 +391,7 @@ static int l2tp_ip6_connect(struct sock *sk, struct sockaddr *uaddr,
|
||||
struct in6_addr *daddr;
|
||||
int addr_type;
|
||||
int rc;
|
||||
struct l2tp_ip6_net *pn;
|
||||
|
||||
if (addr_len < sizeof(*lsa))
|
||||
return -EINVAL;
|
||||
@@ -398,10 +423,11 @@ static int l2tp_ip6_connect(struct sock *sk, struct sockaddr *uaddr,
|
||||
|
||||
l2tp_ip6_sk(sk)->peer_conn_id = lsa->l2tp_conn_id;
|
||||
|
||||
write_lock_bh(&l2tp_ip6_lock);
|
||||
pn = l2tp_ip6_pernet(sock_net(sk));
|
||||
write_lock_bh(&pn->l2tp_ip6_lock);
|
||||
hlist_del_init(&sk->sk_bind_node);
|
||||
sk_add_bind_node(sk, &l2tp_ip6_bind_table);
|
||||
write_unlock_bh(&l2tp_ip6_lock);
|
||||
sk_add_bind_node(sk, &pn->l2tp_ip6_bind_table);
|
||||
write_unlock_bh(&pn->l2tp_ip6_lock);
|
||||
|
||||
out_sk:
|
||||
release_sock(sk);
|
||||
@@ -768,25 +794,58 @@ static struct inet6_protocol l2tp_ip6_protocol __read_mostly = {
|
||||
.handler = l2tp_ip6_recv,
|
||||
};
|
||||
|
||||
static __net_init int l2tp_ip6_init_net(struct net *net)
|
||||
{
|
||||
struct l2tp_ip6_net *pn = net_generic(net, l2tp_ip6_net_id);
|
||||
|
||||
rwlock_init(&pn->l2tp_ip6_lock);
|
||||
INIT_HLIST_HEAD(&pn->l2tp_ip6_table);
|
||||
INIT_HLIST_HEAD(&pn->l2tp_ip6_bind_table);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __net_exit void l2tp_ip6_exit_net(struct net *net)
|
||||
{
|
||||
struct l2tp_ip6_net *pn = l2tp_ip6_pernet(net);
|
||||
|
||||
write_lock_bh(&pn->l2tp_ip6_lock);
|
||||
WARN_ON_ONCE(hlist_count_nodes(&pn->l2tp_ip6_table) != 0);
|
||||
WARN_ON_ONCE(hlist_count_nodes(&pn->l2tp_ip6_bind_table) != 0);
|
||||
write_unlock_bh(&pn->l2tp_ip6_lock);
|
||||
}
|
||||
|
||||
static struct pernet_operations l2tp_ip6_net_ops = {
|
||||
.init = l2tp_ip6_init_net,
|
||||
.exit = l2tp_ip6_exit_net,
|
||||
.id = &l2tp_ip6_net_id,
|
||||
.size = sizeof(struct l2tp_ip6_net),
|
||||
};
|
||||
|
||||
static int __init l2tp_ip6_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
pr_info("L2TP IP encapsulation support for IPv6 (L2TPv3)\n");
|
||||
|
||||
err = register_pernet_device(&l2tp_ip6_net_ops);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = proto_register(&l2tp_ip6_prot, 1);
|
||||
if (err != 0)
|
||||
goto out;
|
||||
goto out1;
|
||||
|
||||
err = inet6_add_protocol(&l2tp_ip6_protocol, IPPROTO_L2TP);
|
||||
if (err)
|
||||
goto out1;
|
||||
goto out2;
|
||||
|
||||
inet6_register_protosw(&l2tp_ip6_protosw);
|
||||
return 0;
|
||||
|
||||
out1:
|
||||
out2:
|
||||
proto_unregister(&l2tp_ip6_prot);
|
||||
out1:
|
||||
unregister_pernet_device(&l2tp_ip6_net_ops);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
@@ -796,6 +855,7 @@ static void __exit l2tp_ip6_exit(void)
|
||||
inet6_unregister_protosw(&l2tp_ip6_protosw);
|
||||
inet6_del_protocol(&l2tp_ip6_protocol, IPPROTO_L2TP);
|
||||
proto_unregister(&l2tp_ip6_prot);
|
||||
unregister_pernet_device(&l2tp_ip6_net_ops);
|
||||
}
|
||||
|
||||
module_init(l2tp_ip6_init);
|
||||
|
||||
+40
-32
@@ -63,7 +63,7 @@ static struct l2tp_session *l2tp_nl_session_get(struct genl_info *info)
|
||||
if (tunnel) {
|
||||
session = l2tp_session_get(net, tunnel->sock, tunnel->version,
|
||||
tunnel_id, session_id);
|
||||
l2tp_tunnel_dec_refcount(tunnel);
|
||||
l2tp_tunnel_put(tunnel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,7 +242,7 @@ static int l2tp_nl_cmd_tunnel_create(struct sk_buff *skb, struct genl_info *info
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
l2tp_tunnel_inc_refcount(tunnel);
|
||||
refcount_inc(&tunnel->ref_count);
|
||||
ret = l2tp_tunnel_register(tunnel, net, &cfg);
|
||||
if (ret < 0) {
|
||||
kfree(tunnel);
|
||||
@@ -250,7 +250,7 @@ static int l2tp_nl_cmd_tunnel_create(struct sk_buff *skb, struct genl_info *info
|
||||
}
|
||||
ret = l2tp_tunnel_notify(&l2tp_nl_family, info, tunnel,
|
||||
L2TP_CMD_TUNNEL_CREATE);
|
||||
l2tp_tunnel_dec_refcount(tunnel);
|
||||
l2tp_tunnel_put(tunnel);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
@@ -280,7 +280,7 @@ static int l2tp_nl_cmd_tunnel_delete(struct sk_buff *skb, struct genl_info *info
|
||||
|
||||
l2tp_tunnel_delete(tunnel);
|
||||
|
||||
l2tp_tunnel_dec_refcount(tunnel);
|
||||
l2tp_tunnel_put(tunnel);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
@@ -308,7 +308,7 @@ static int l2tp_nl_cmd_tunnel_modify(struct sk_buff *skb, struct genl_info *info
|
||||
ret = l2tp_tunnel_notify(&l2tp_nl_family, info,
|
||||
tunnel, L2TP_CMD_TUNNEL_MODIFY);
|
||||
|
||||
l2tp_tunnel_dec_refcount(tunnel);
|
||||
l2tp_tunnel_put(tunnel);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
@@ -479,42 +479,48 @@ static int l2tp_nl_cmd_tunnel_get(struct sk_buff *skb, struct genl_info *info)
|
||||
if (ret < 0)
|
||||
goto err_nlmsg_tunnel;
|
||||
|
||||
l2tp_tunnel_dec_refcount(tunnel);
|
||||
l2tp_tunnel_put(tunnel);
|
||||
|
||||
return genlmsg_unicast(net, msg, info->snd_portid);
|
||||
|
||||
err_nlmsg_tunnel:
|
||||
l2tp_tunnel_dec_refcount(tunnel);
|
||||
l2tp_tunnel_put(tunnel);
|
||||
err_nlmsg:
|
||||
nlmsg_free(msg);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct l2tp_nl_cb_data {
|
||||
unsigned long tkey;
|
||||
unsigned long skey;
|
||||
};
|
||||
|
||||
static int l2tp_nl_cmd_tunnel_dump(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
{
|
||||
int ti = cb->args[0];
|
||||
struct l2tp_nl_cb_data *cbd = (void *)&cb->ctx[0];
|
||||
unsigned long key = cbd->tkey;
|
||||
struct l2tp_tunnel *tunnel;
|
||||
struct net *net = sock_net(skb->sk);
|
||||
|
||||
for (;;) {
|
||||
tunnel = l2tp_tunnel_get_nth(net, ti);
|
||||
tunnel = l2tp_tunnel_get_next(net, &key);
|
||||
if (!tunnel)
|
||||
goto out;
|
||||
|
||||
if (l2tp_nl_tunnel_send(skb, NETLINK_CB(cb->skb).portid,
|
||||
cb->nlh->nlmsg_seq, NLM_F_MULTI,
|
||||
tunnel, L2TP_CMD_TUNNEL_GET) < 0) {
|
||||
l2tp_tunnel_dec_refcount(tunnel);
|
||||
l2tp_tunnel_put(tunnel);
|
||||
goto out;
|
||||
}
|
||||
l2tp_tunnel_dec_refcount(tunnel);
|
||||
l2tp_tunnel_put(tunnel);
|
||||
|
||||
ti++;
|
||||
key++;
|
||||
}
|
||||
|
||||
out:
|
||||
cb->args[0] = ti;
|
||||
cbd->tkey = key;
|
||||
|
||||
return skb->len;
|
||||
}
|
||||
@@ -641,12 +647,12 @@ static int l2tp_nl_cmd_session_create(struct sk_buff *skb, struct genl_info *inf
|
||||
if (session) {
|
||||
ret = l2tp_session_notify(&l2tp_nl_family, info, session,
|
||||
L2TP_CMD_SESSION_CREATE);
|
||||
l2tp_session_dec_refcount(session);
|
||||
l2tp_session_put(session);
|
||||
}
|
||||
}
|
||||
|
||||
out_tunnel:
|
||||
l2tp_tunnel_dec_refcount(tunnel);
|
||||
l2tp_tunnel_put(tunnel);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
@@ -671,7 +677,7 @@ static int l2tp_nl_cmd_session_delete(struct sk_buff *skb, struct genl_info *inf
|
||||
if (l2tp_nl_cmd_ops[pw_type] && l2tp_nl_cmd_ops[pw_type]->session_delete)
|
||||
l2tp_nl_cmd_ops[pw_type]->session_delete(session);
|
||||
|
||||
l2tp_session_dec_refcount(session);
|
||||
l2tp_session_put(session);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
@@ -707,7 +713,7 @@ static int l2tp_nl_cmd_session_modify(struct sk_buff *skb, struct genl_info *inf
|
||||
ret = l2tp_session_notify(&l2tp_nl_family, info,
|
||||
session, L2TP_CMD_SESSION_MODIFY);
|
||||
|
||||
l2tp_session_dec_refcount(session);
|
||||
l2tp_session_put(session);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
@@ -818,57 +824,59 @@ static int l2tp_nl_cmd_session_get(struct sk_buff *skb, struct genl_info *info)
|
||||
|
||||
ret = genlmsg_unicast(genl_info_net(info), msg, info->snd_portid);
|
||||
|
||||
l2tp_session_dec_refcount(session);
|
||||
l2tp_session_put(session);
|
||||
|
||||
return ret;
|
||||
|
||||
err_ref_msg:
|
||||
nlmsg_free(msg);
|
||||
err_ref:
|
||||
l2tp_session_dec_refcount(session);
|
||||
l2tp_session_put(session);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int l2tp_nl_cmd_session_dump(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
{
|
||||
struct l2tp_nl_cb_data *cbd = (void *)&cb->ctx[0];
|
||||
struct net *net = sock_net(skb->sk);
|
||||
struct l2tp_session *session;
|
||||
struct l2tp_tunnel *tunnel = NULL;
|
||||
int ti = cb->args[0];
|
||||
int si = cb->args[1];
|
||||
unsigned long tkey = cbd->tkey;
|
||||
unsigned long skey = cbd->skey;
|
||||
|
||||
for (;;) {
|
||||
if (!tunnel) {
|
||||
tunnel = l2tp_tunnel_get_nth(net, ti);
|
||||
tunnel = l2tp_tunnel_get_next(net, &tkey);
|
||||
if (!tunnel)
|
||||
goto out;
|
||||
}
|
||||
|
||||
session = l2tp_session_get_nth(tunnel, si);
|
||||
session = l2tp_session_get_next(net, tunnel->sock, tunnel->version,
|
||||
tunnel->tunnel_id, &skey);
|
||||
if (!session) {
|
||||
ti++;
|
||||
l2tp_tunnel_dec_refcount(tunnel);
|
||||
tkey++;
|
||||
l2tp_tunnel_put(tunnel);
|
||||
tunnel = NULL;
|
||||
si = 0;
|
||||
skey = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (l2tp_nl_session_send(skb, NETLINK_CB(cb->skb).portid,
|
||||
cb->nlh->nlmsg_seq, NLM_F_MULTI,
|
||||
session, L2TP_CMD_SESSION_GET) < 0) {
|
||||
l2tp_session_dec_refcount(session);
|
||||
l2tp_tunnel_dec_refcount(tunnel);
|
||||
l2tp_session_put(session);
|
||||
l2tp_tunnel_put(tunnel);
|
||||
break;
|
||||
}
|
||||
l2tp_session_dec_refcount(session);
|
||||
l2tp_session_put(session);
|
||||
|
||||
si++;
|
||||
skey++;
|
||||
}
|
||||
|
||||
out:
|
||||
cb->args[0] = ti;
|
||||
cb->args[1] = si;
|
||||
cbd->tkey = tkey;
|
||||
cbd->skey = skey;
|
||||
|
||||
return skb->len;
|
||||
}
|
||||
|
||||
+33
-31
@@ -149,7 +149,7 @@ static struct sock *pppol2tp_session_get_sock(struct l2tp_session *session)
|
||||
|
||||
/* Helpers to obtain tunnel/session contexts from sockets.
|
||||
*/
|
||||
static inline struct l2tp_session *pppol2tp_sock_to_session(struct sock *sk)
|
||||
static struct l2tp_session *pppol2tp_sock_to_session(struct sock *sk)
|
||||
{
|
||||
struct l2tp_session *session;
|
||||
|
||||
@@ -313,12 +313,12 @@ static int pppol2tp_sendmsg(struct socket *sock, struct msghdr *m,
|
||||
l2tp_xmit_skb(session, skb);
|
||||
local_bh_enable();
|
||||
|
||||
l2tp_session_dec_refcount(session);
|
||||
l2tp_session_put(session);
|
||||
|
||||
return total_len;
|
||||
|
||||
error_put_sess:
|
||||
l2tp_session_dec_refcount(session);
|
||||
l2tp_session_put(session);
|
||||
error:
|
||||
return error;
|
||||
}
|
||||
@@ -372,12 +372,12 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
|
||||
l2tp_xmit_skb(session, skb);
|
||||
local_bh_enable();
|
||||
|
||||
l2tp_session_dec_refcount(session);
|
||||
l2tp_session_put(session);
|
||||
|
||||
return 1;
|
||||
|
||||
abort_put_sess:
|
||||
l2tp_session_dec_refcount(session);
|
||||
l2tp_session_put(session);
|
||||
abort:
|
||||
/* Free the original skb */
|
||||
kfree_skb(skb);
|
||||
@@ -413,7 +413,7 @@ static void pppol2tp_session_close(struct l2tp_session *session)
|
||||
sock_put(ps->__sk);
|
||||
|
||||
/* drop ref taken when we referenced socket via sk_user_data */
|
||||
l2tp_session_dec_refcount(session);
|
||||
l2tp_session_put(session);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -444,7 +444,7 @@ static int pppol2tp_release(struct socket *sock)
|
||||
if (session) {
|
||||
l2tp_session_delete(session);
|
||||
/* drop ref taken by pppol2tp_sock_to_session */
|
||||
l2tp_session_dec_refcount(session);
|
||||
l2tp_session_put(session);
|
||||
}
|
||||
|
||||
release_sock(sk);
|
||||
@@ -668,7 +668,7 @@ static struct l2tp_tunnel *pppol2tp_tunnel_get(struct net *net,
|
||||
if (error < 0)
|
||||
return ERR_PTR(error);
|
||||
|
||||
l2tp_tunnel_inc_refcount(tunnel);
|
||||
refcount_inc(&tunnel->ref_count);
|
||||
error = l2tp_tunnel_register(tunnel, net, &tcfg);
|
||||
if (error < 0) {
|
||||
kfree(tunnel);
|
||||
@@ -684,7 +684,7 @@ static struct l2tp_tunnel *pppol2tp_tunnel_get(struct net *net,
|
||||
|
||||
/* Error if socket is not prepped */
|
||||
if (!tunnel->sock) {
|
||||
l2tp_tunnel_dec_refcount(tunnel);
|
||||
l2tp_tunnel_put(tunnel);
|
||||
return ERR_PTR(-ENOENT);
|
||||
}
|
||||
}
|
||||
@@ -774,13 +774,13 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr,
|
||||
|
||||
pppol2tp_session_init(session);
|
||||
ps = l2tp_session_priv(session);
|
||||
l2tp_session_inc_refcount(session);
|
||||
refcount_inc(&session->ref_count);
|
||||
|
||||
mutex_lock(&ps->sk_lock);
|
||||
error = l2tp_session_register(session, tunnel);
|
||||
if (error < 0) {
|
||||
mutex_unlock(&ps->sk_lock);
|
||||
l2tp_session_dec_refcount(session);
|
||||
l2tp_session_put(session);
|
||||
goto end;
|
||||
}
|
||||
|
||||
@@ -836,8 +836,8 @@ end:
|
||||
l2tp_tunnel_delete(tunnel);
|
||||
}
|
||||
if (drop_refcnt)
|
||||
l2tp_session_dec_refcount(session);
|
||||
l2tp_tunnel_dec_refcount(tunnel);
|
||||
l2tp_session_put(session);
|
||||
l2tp_tunnel_put(tunnel);
|
||||
release_sock(sk);
|
||||
|
||||
return error;
|
||||
@@ -877,7 +877,7 @@ static int pppol2tp_session_create(struct net *net, struct l2tp_tunnel *tunnel,
|
||||
return 0;
|
||||
|
||||
err_sess:
|
||||
l2tp_session_dec_refcount(session);
|
||||
l2tp_session_put(session);
|
||||
err:
|
||||
return error;
|
||||
}
|
||||
@@ -988,7 +988,7 @@ static int pppol2tp_getname(struct socket *sock, struct sockaddr *uaddr,
|
||||
|
||||
error = len;
|
||||
|
||||
l2tp_session_dec_refcount(session);
|
||||
l2tp_session_put(session);
|
||||
end:
|
||||
return error;
|
||||
}
|
||||
@@ -1038,12 +1038,12 @@ static int pppol2tp_tunnel_copy_stats(struct pppol2tp_ioc_stats *stats,
|
||||
return -EBADR;
|
||||
|
||||
if (session->pwtype != L2TP_PWTYPE_PPP) {
|
||||
l2tp_session_dec_refcount(session);
|
||||
l2tp_session_put(session);
|
||||
return -EBADR;
|
||||
}
|
||||
|
||||
pppol2tp_copy_stats(stats, &session->stats);
|
||||
l2tp_session_dec_refcount(session);
|
||||
l2tp_session_put(session);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1261,7 +1261,7 @@ static int pppol2tp_setsockopt(struct socket *sock, int level, int optname,
|
||||
err = pppol2tp_session_setsockopt(sk, session, optname, val);
|
||||
}
|
||||
|
||||
l2tp_session_dec_refcount(session);
|
||||
l2tp_session_put(session);
|
||||
end:
|
||||
return err;
|
||||
}
|
||||
@@ -1382,7 +1382,7 @@ static int pppol2tp_getsockopt(struct socket *sock, int level, int optname,
|
||||
err = 0;
|
||||
|
||||
end_put_sess:
|
||||
l2tp_session_dec_refcount(session);
|
||||
l2tp_session_put(session);
|
||||
end:
|
||||
return err;
|
||||
}
|
||||
@@ -1397,8 +1397,8 @@ end:
|
||||
|
||||
struct pppol2tp_seq_data {
|
||||
struct seq_net_private p;
|
||||
int tunnel_idx; /* current tunnel */
|
||||
int session_idx; /* index of session within current tunnel */
|
||||
unsigned long tkey; /* lookup key of current tunnel */
|
||||
unsigned long skey; /* lookup key of current session */
|
||||
struct l2tp_tunnel *tunnel;
|
||||
struct l2tp_session *session; /* NULL means get next tunnel */
|
||||
};
|
||||
@@ -1407,17 +1407,17 @@ static void pppol2tp_next_tunnel(struct net *net, struct pppol2tp_seq_data *pd)
|
||||
{
|
||||
/* Drop reference taken during previous invocation */
|
||||
if (pd->tunnel)
|
||||
l2tp_tunnel_dec_refcount(pd->tunnel);
|
||||
l2tp_tunnel_put(pd->tunnel);
|
||||
|
||||
for (;;) {
|
||||
pd->tunnel = l2tp_tunnel_get_nth(net, pd->tunnel_idx);
|
||||
pd->tunnel_idx++;
|
||||
pd->tunnel = l2tp_tunnel_get_next(net, &pd->tkey);
|
||||
pd->tkey++;
|
||||
|
||||
/* Only accept L2TPv2 tunnels */
|
||||
if (!pd->tunnel || pd->tunnel->version == 2)
|
||||
return;
|
||||
|
||||
l2tp_tunnel_dec_refcount(pd->tunnel);
|
||||
l2tp_tunnel_put(pd->tunnel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1425,13 +1425,15 @@ static void pppol2tp_next_session(struct net *net, struct pppol2tp_seq_data *pd)
|
||||
{
|
||||
/* Drop reference taken during previous invocation */
|
||||
if (pd->session)
|
||||
l2tp_session_dec_refcount(pd->session);
|
||||
l2tp_session_put(pd->session);
|
||||
|
||||
pd->session = l2tp_session_get_nth(pd->tunnel, pd->session_idx);
|
||||
pd->session_idx++;
|
||||
pd->session = l2tp_session_get_next(net, pd->tunnel->sock,
|
||||
pd->tunnel->version,
|
||||
pd->tunnel->tunnel_id, &pd->skey);
|
||||
pd->skey++;
|
||||
|
||||
if (!pd->session) {
|
||||
pd->session_idx = 0;
|
||||
pd->skey = 0;
|
||||
pppol2tp_next_tunnel(net, pd);
|
||||
}
|
||||
}
|
||||
@@ -1483,11 +1485,11 @@ static void pppol2tp_seq_stop(struct seq_file *p, void *v)
|
||||
* or pppol2tp_next_tunnel().
|
||||
*/
|
||||
if (pd->session) {
|
||||
l2tp_session_dec_refcount(pd->session);
|
||||
l2tp_session_put(pd->session);
|
||||
pd->session = NULL;
|
||||
}
|
||||
if (pd->tunnel) {
|
||||
l2tp_tunnel_dec_refcount(pd->tunnel);
|
||||
l2tp_tunnel_put(pd->tunnel);
|
||||
pd->tunnel = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user