aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/l2tp/l2tp_core.c152
-rw-r--r--net/l2tp/l2tp_core.h6
-rw-r--r--net/l2tp/l2tp_eth.c10
-rw-r--r--net/l2tp/l2tp_ip.c17
-rw-r--r--net/l2tp/l2tp_ip6.c18
-rw-r--r--net/l2tp/l2tp_netlink.c45
-rw-r--r--net/l2tp/l2tp_ppp.c75
7 files changed, 222 insertions, 101 deletions
diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c
index 8adab6335ced..e927422d8c58 100644
--- a/net/l2tp/l2tp_core.c
+++ b/net/l2tp/l2tp_core.c
@@ -278,6 +278,55 @@ struct l2tp_session *l2tp_session_find(struct net *net, struct l2tp_tunnel *tunn
278} 278}
279EXPORT_SYMBOL_GPL(l2tp_session_find); 279EXPORT_SYMBOL_GPL(l2tp_session_find);
280 280
281/* Like l2tp_session_find() but takes a reference on the returned session.
282 * Optionally calls session->ref() too if do_ref is true.
283 */
284struct l2tp_session *l2tp_session_get(struct net *net,
285 struct l2tp_tunnel *tunnel,
286 u32 session_id, bool do_ref)
287{
288 struct hlist_head *session_list;
289 struct l2tp_session *session;
290
291 if (!tunnel) {
292 struct l2tp_net *pn = l2tp_pernet(net);
293
294 session_list = l2tp_session_id_hash_2(pn, session_id);
295
296 rcu_read_lock_bh();
297 hlist_for_each_entry_rcu(session, session_list, global_hlist) {
298 if (session->session_id == session_id) {
299 l2tp_session_inc_refcount(session);
300 if (do_ref && session->ref)
301 session->ref(session);
302 rcu_read_unlock_bh();
303
304 return session;
305 }
306 }
307 rcu_read_unlock_bh();
308
309 return NULL;
310 }
311
312 session_list = l2tp_session_id_hash(tunnel, session_id);
313 read_lock_bh(&tunnel->hlist_lock);
314 hlist_for_each_entry(session, session_list, hlist) {
315 if (session->session_id == session_id) {
316 l2tp_session_inc_refcount(session);
317 if (do_ref && session->ref)
318 session->ref(session);
319 read_unlock_bh(&tunnel->hlist_lock);
320
321 return session;
322 }
323 }
324 read_unlock_bh(&tunnel->hlist_lock);
325
326 return NULL;
327}
328EXPORT_SYMBOL_GPL(l2tp_session_get);
329
281struct l2tp_session *l2tp_session_find_nth(struct l2tp_tunnel *tunnel, int nth) 330struct l2tp_session *l2tp_session_find_nth(struct l2tp_tunnel *tunnel, int nth)
282{ 331{
283 int hash; 332 int hash;
@@ -303,7 +352,8 @@ EXPORT_SYMBOL_GPL(l2tp_session_find_nth);
303/* Lookup a session by interface name. 352/* Lookup a session by interface name.
304 * This is very inefficient but is only used by management interfaces. 353 * This is very inefficient but is only used by management interfaces.
305 */ 354 */
306struct l2tp_session *l2tp_session_find_by_ifname(struct net *net, char *ifname) 355struct l2tp_session *l2tp_session_get_by_ifname(struct net *net, char *ifname,
356 bool do_ref)
307{ 357{
308 struct l2tp_net *pn = l2tp_pernet(net); 358 struct l2tp_net *pn = l2tp_pernet(net);
309 int hash; 359 int hash;
@@ -313,7 +363,11 @@ struct l2tp_session *l2tp_session_find_by_ifname(struct net *net, char *ifname)
313 for (hash = 0; hash < L2TP_HASH_SIZE_2; hash++) { 363 for (hash = 0; hash < L2TP_HASH_SIZE_2; hash++) {
314 hlist_for_each_entry_rcu(session, &pn->l2tp_session_hlist[hash], global_hlist) { 364 hlist_for_each_entry_rcu(session, &pn->l2tp_session_hlist[hash], global_hlist) {
315 if (!strcmp(session->ifname, ifname)) { 365 if (!strcmp(session->ifname, ifname)) {
366 l2tp_session_inc_refcount(session);
367 if (do_ref && session->ref)
368 session->ref(session);
316 rcu_read_unlock_bh(); 369 rcu_read_unlock_bh();
370
317 return session; 371 return session;
318 } 372 }
319 } 373 }
@@ -323,7 +377,49 @@ struct l2tp_session *l2tp_session_find_by_ifname(struct net *net, char *ifname)
323 377
324 return NULL; 378 return NULL;
325} 379}
326EXPORT_SYMBOL_GPL(l2tp_session_find_by_ifname); 380EXPORT_SYMBOL_GPL(l2tp_session_get_by_ifname);
381
382static int l2tp_session_add_to_tunnel(struct l2tp_tunnel *tunnel,
383 struct l2tp_session *session)
384{
385 struct l2tp_session *session_walk;
386 struct hlist_head *g_head;
387 struct hlist_head *head;
388 struct l2tp_net *pn;
389
390 head = l2tp_session_id_hash(tunnel, session->session_id);
391
392 write_lock_bh(&tunnel->hlist_lock);
393 hlist_for_each_entry(session_walk, head, hlist)
394 if (session_walk->session_id == session->session_id)
395 goto exist;
396
397 if (tunnel->version == L2TP_HDR_VER_3) {
398 pn = l2tp_pernet(tunnel->l2tp_net);
399 g_head = l2tp_session_id_hash_2(l2tp_pernet(tunnel->l2tp_net),
400 session->session_id);
401
402 spin_lock_bh(&pn->l2tp_session_hlist_lock);
403 hlist_for_each_entry(session_walk, g_head, global_hlist)
404 if (session_walk->session_id == session->session_id)
405 goto exist_glob;
406
407 hlist_add_head_rcu(&session->global_hlist, g_head);
408 spin_unlock_bh(&pn->l2tp_session_hlist_lock);
409 }
410
411 hlist_add_head(&session->hlist, head);
412 write_unlock_bh(&tunnel->hlist_lock);
413
414 return 0;
415
416exist_glob:
417 spin_unlock_bh(&pn->l2tp_session_hlist_lock);
418exist:
419 write_unlock_bh(&tunnel->hlist_lock);
420
421 return -EEXIST;
422}
327 423
328/* Lookup a tunnel by id 424/* Lookup a tunnel by id
329 */ 425 */
@@ -633,6 +729,9 @@ discard:
633 * a data (not control) frame before coming here. Fields up to the 729 * a data (not control) frame before coming here. Fields up to the
634 * session-id have already been parsed and ptr points to the data 730 * session-id have already been parsed and ptr points to the data
635 * after the session-id. 731 * after the session-id.
732 *
733 * session->ref() must have been called prior to l2tp_recv_common().
734 * session->deref() will be called automatically after skb is processed.
636 */ 735 */
637void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb, 736void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb,
638 unsigned char *ptr, unsigned char *optr, u16 hdrflags, 737 unsigned char *ptr, unsigned char *optr, u16 hdrflags,
@@ -642,14 +741,6 @@ void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb,
642 int offset; 741 int offset;
643 u32 ns, nr; 742 u32 ns, nr;
644 743
645 /* The ref count is increased since we now hold a pointer to
646 * the session. Take care to decrement the refcnt when exiting
647 * this function from now on...
648 */
649 l2tp_session_inc_refcount(session);
650 if (session->ref)
651 (*session->ref)(session);
652
653 /* Parse and check optional cookie */ 744 /* Parse and check optional cookie */
654 if (session->peer_cookie_len > 0) { 745 if (session->peer_cookie_len > 0) {
655 if (memcmp(ptr, &session->peer_cookie[0], session->peer_cookie_len)) { 746 if (memcmp(ptr, &session->peer_cookie[0], session->peer_cookie_len)) {
@@ -802,8 +893,6 @@ void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb,
802 /* Try to dequeue as many skbs from reorder_q as we can. */ 893 /* Try to dequeue as many skbs from reorder_q as we can. */
803 l2tp_recv_dequeue(session); 894 l2tp_recv_dequeue(session);
804 895
805 l2tp_session_dec_refcount(session);
806
807 return; 896 return;
808 897
809discard: 898discard:
@@ -812,8 +901,6 @@ discard:
812 901
813 if (session->deref) 902 if (session->deref)
814 (*session->deref)(session); 903 (*session->deref)(session);
815
816 l2tp_session_dec_refcount(session);
817} 904}
818EXPORT_SYMBOL(l2tp_recv_common); 905EXPORT_SYMBOL(l2tp_recv_common);
819 906
@@ -920,8 +1007,14 @@ static int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb,
920 } 1007 }
921 1008
922 /* Find the session context */ 1009 /* Find the session context */
923 session = l2tp_session_find(tunnel->l2tp_net, tunnel, session_id); 1010 session = l2tp_session_get(tunnel->l2tp_net, tunnel, session_id, true);
924 if (!session || !session->recv_skb) { 1011 if (!session || !session->recv_skb) {
1012 if (session) {
1013 if (session->deref)
1014 session->deref(session);
1015 l2tp_session_dec_refcount(session);
1016 }