Skip to content

Commit 8d5187a

Browse files
j-c-hNipaLocal
authored andcommitted
l2tp: add tunnel/session get_next helpers
l2tp management APIs and procfs/debugfs iterate over l2tp tunnel and session lists. Since these lists are now implemented using IDR, we can use IDR get_next APIs to iterate them. Add tunnel/session get_next functions to do so. The session get_next functions get the next session in a given tunnel and need to account for l2tpv2 and l2tpv3 differences: * l2tpv2 sessions are keyed by tunnel ID / session ID. Iteration for a given tunnel ID, TID, can therefore start with a key given by TID/0 and finish when the next entry's tunnel ID is not TID. This is possible only because the tunnel ID part of the key is the upper 16 bits and the session ID part the lower 16 bits; when idr_next increments the key value, it therefore finds the next sessions of the current tunnel before those of the next tunnel. Entries with session ID 0 are always skipped because they are used internally by pppol2tp. * l2tpv3 sessions are keyed by session ID. Iteration starts at the first IDR entry and skips entries where the tunnel does not match. Iteration must also consider session ID collisions and walk the list of colliding sessions (if any) for one which matches the supplied tunnel. Signed-off-by: James Chapman <jchapman@katalix.com> Signed-off-by: Tom Parkin <tparkin@katalix.com> Signed-off-by: NipaLocal <nipa@local>
1 parent 28abb7e commit 8d5187a

File tree

2 files changed

+125
-0
lines changed

2 files changed

+125
-0
lines changed

net/l2tp/l2tp_core.c

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,28 @@ struct l2tp_tunnel *l2tp_tunnel_get_nth(const struct net *net, int nth)
257257
}
258258
EXPORT_SYMBOL_GPL(l2tp_tunnel_get_nth);
259259

260+
struct l2tp_tunnel *l2tp_tunnel_get_next(const struct net *net, unsigned long *key)
261+
{
262+
struct l2tp_net *pn = l2tp_pernet(net);
263+
struct l2tp_tunnel *tunnel = NULL;
264+
265+
rcu_read_lock_bh();
266+
again:
267+
tunnel = idr_get_next_ul(&pn->l2tp_tunnel_idr, key);
268+
if (tunnel) {
269+
if (refcount_inc_not_zero(&tunnel->ref_count)) {
270+
rcu_read_unlock_bh();
271+
return tunnel;
272+
}
273+
(*key)++;
274+
goto again;
275+
}
276+
rcu_read_unlock_bh();
277+
278+
return NULL;
279+
}
280+
EXPORT_SYMBOL_GPL(l2tp_tunnel_get_next);
281+
260282
struct l2tp_session *l2tp_v3_session_get(const struct net *net, struct sock *sk, u32 session_id)
261283
{
262284
const struct l2tp_net *pn = l2tp_pernet(net);
@@ -347,6 +369,106 @@ struct l2tp_session *l2tp_session_get_nth(struct l2tp_tunnel *tunnel, int nth)
347369
}
348370
EXPORT_SYMBOL_GPL(l2tp_session_get_nth);
349371

372+
static struct l2tp_session *l2tp_v2_session_get_next(const struct net *net, u16 tid, unsigned long *key)
373+
{
374+
struct l2tp_net *pn = l2tp_pernet(net);
375+
struct l2tp_session *session = NULL;
376+
377+
/* Start searching within the range of the tid */
378+
if (*key == 0)
379+
*key = l2tp_v2_session_key(tid, 0);
380+
381+
rcu_read_lock_bh();
382+
again:
383+
session = idr_get_next_ul(&pn->l2tp_v2_session_idr, key);
384+
if (session) {
385+
struct l2tp_tunnel *tunnel = READ_ONCE(session->tunnel);
386+
387+
/* ignore sessions with id 0 as they are internal for pppol2tp */
388+
if (session->session_id == 0) {
389+
(*key)++;
390+
goto again;
391+
}
392+
393+
if (tunnel && tunnel->tunnel_id == tid &&
394+
refcount_inc_not_zero(&session->ref_count)) {
395+
rcu_read_unlock_bh();
396+
return session;
397+
}
398+
399+
(*key)++;
400+
if (tunnel->tunnel_id == tid)
401+
goto again;
402+
}
403+
rcu_read_unlock_bh();
404+
405+
return NULL;
406+
}
407+
408+
static struct l2tp_session *l2tp_v3_session_get_next(const struct net *net, u32 tid, struct sock *sk, unsigned long *key)
409+
{
410+
struct l2tp_net *pn = l2tp_pernet(net);
411+
struct l2tp_session *session = NULL;
412+
413+
rcu_read_lock_bh();
414+
again:
415+
session = idr_get_next_ul(&pn->l2tp_v3_session_idr, key);
416+
if (session && !hash_hashed(&session->hlist)) {
417+
struct l2tp_tunnel *tunnel = READ_ONCE(session->tunnel);
418+
419+
if (tunnel && tunnel->tunnel_id == tid &&
420+
refcount_inc_not_zero(&session->ref_count)) {
421+
rcu_read_unlock_bh();
422+
return session;
423+
}
424+
425+
(*key)++;
426+
goto again;
427+
}
428+
429+
/* If we get here and session is non-NULL, the IDR entry may be one
430+
* where the session_id collides with one in another tunnel. Check
431+
* session_htable for a match. There can only be one session of a given
432+
* ID per tunnel so we can return as soon as a match is found.
433+
*/
434+
if (session && hash_hashed(&session->hlist)) {
435+
unsigned long hkey = l2tp_v3_session_hashkey(sk, session->session_id);
436+
u32 sid = session->session_id;
437+
438+
hash_for_each_possible_rcu(pn->l2tp_v3_session_htable, session,
439+
hlist, hkey) {
440+
struct l2tp_tunnel *tunnel = READ_ONCE(session->tunnel);
441+
442+
if (session->session_id == sid &&
443+
tunnel && tunnel->tunnel_id == tid &&
444+
refcount_inc_not_zero(&session->ref_count)) {
445+
rcu_read_unlock_bh();
446+
return session;
447+
}
448+
}
449+
450+
/* If no match found, the colliding session ID isn't in our
451+
* tunnel so try the next session ID.
452+
*/
453+
(*key)++;
454+
goto again;
455+
}
456+
457+
rcu_read_unlock_bh();
458+
459+
return NULL;
460+
}
461+
462+
struct l2tp_session *l2tp_session_get_next(const struct net *net, struct sock *sk, int pver,
463+
u32 tunnel_id, unsigned long *key)
464+
{
465+
if (pver == L2TP_HDR_VER_2)
466+
return l2tp_v2_session_get_next(net, tunnel_id, key);
467+
else
468+
return l2tp_v3_session_get_next(net, tunnel_id, sk, key);
469+
}
470+
EXPORT_SYMBOL_GPL(l2tp_session_get_next);
471+
350472
/* Lookup a session by interface name.
351473
* This is very inefficient but is only used by management interfaces.
352474
*/

net/l2tp/l2tp_core.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,12 +220,15 @@ void l2tp_session_dec_refcount(struct l2tp_session *session);
220220
*/
221221
struct l2tp_tunnel *l2tp_tunnel_get(const struct net *net, u32 tunnel_id);
222222
struct l2tp_tunnel *l2tp_tunnel_get_nth(const struct net *net, int nth);
223+
struct l2tp_tunnel *l2tp_tunnel_get_next(const struct net *net, unsigned long *key);
223224

224225
struct l2tp_session *l2tp_v3_session_get(const struct net *net, struct sock *sk, u32 session_id);
225226
struct l2tp_session *l2tp_v2_session_get(const struct net *net, u16 tunnel_id, u16 session_id);
226227
struct l2tp_session *l2tp_session_get(const struct net *net, struct sock *sk, int pver,
227228
u32 tunnel_id, u32 session_id);
228229
struct l2tp_session *l2tp_session_get_nth(struct l2tp_tunnel *tunnel, int nth);
230+
struct l2tp_session *l2tp_session_get_next(const struct net *net, struct sock *sk, int pver,
231+
u32 tunnel_id, unsigned long *key);
229232
struct l2tp_session *l2tp_session_get_by_ifname(const struct net *net,
230233
const char *ifname);
231234

0 commit comments

Comments
 (0)