Commit 969afb43 authored by David S. Miller's avatar David S. Miller

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: default avatarDavid S. Miller <davem@davemloft.net>
parents bbfeba26 c1b2e36b
......@@ -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
......
This diff is collapsed.
......@@ -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);
......
......@@ -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;
}
}
......
......@@ -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;
}
......
......@@ -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 @@ static struct sock *__l2tp_ip_bind_lookup(const struct net *net, __be32 laddr,
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 @@ static int l2tp_ip_recv(struct sk_buff *skb)
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 @@ static int l2tp_ip_recv(struct sk_buff *skb)
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 @@ static int l2tp_ip_recv(struct sk_buff *skb)
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 @@ static int l2tp_ip_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
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);
......
......@@ -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 @@ static struct sock *__l2tp_ip6_bind_lookup(const struct net *net,
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 @@ static int l2tp_ip6_recv(struct sk_buff *skb)
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 @@ static int l2tp_ip6_recv(struct sk_buff *skb)
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 @@ static int l2tp_ip6_recv(struct sk_buff *skb)
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);
......
......@@ -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;
}
......
......@@ -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 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr,
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 @@ static int pppol2tp_getsockopt(struct socket *sock, int level, int optname,
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;
}
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment