diff options
| -rw-r--r-- | net/l2tp/l2tp_core.c | 152 | ||||
| -rw-r--r-- | net/l2tp/l2tp_core.h | 6 | ||||
| -rw-r--r-- | net/l2tp/l2tp_eth.c | 10 | ||||
| -rw-r--r-- | net/l2tp/l2tp_ip.c | 17 | ||||
| -rw-r--r-- | net/l2tp/l2tp_ip6.c | 18 | ||||
| -rw-r--r-- | net/l2tp/l2tp_netlink.c | 45 | ||||
| -rw-r--r-- | net/l2tp/l2tp_ppp.c | 75 |
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 | } |
| 279 | EXPORT_SYMBOL_GPL(l2tp_session_find); | 279 | EXPORT_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 | */ | ||
| 284 | struct 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 | } | ||
| 328 | EXPORT_SYMBOL_GPL(l2tp_session_get); | ||
| 329 | |||
| 281 | struct l2tp_session *l2tp_session_find_nth(struct l2tp_tunnel *tunnel, int nth) | 330 | struct 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 | */ |
| 306 | struct l2tp_session *l2tp_session_find_by_ifname(struct net *net, char *ifname) | 355 | struct 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 | } |
| 326 | EXPORT_SYMBOL_GPL(l2tp_session_find_by_ifname); | 380 | EXPORT_SYMBOL_GPL(l2tp_session_get_by_ifname); |
| 381 | |||
| 382 | static 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 | |||
| 416 | exist_glob: | ||
| 417 | spin_unlock_bh(&pn->l2tp_session_hlist_lock); | ||
| 418 | exist: | ||
| 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 | */ |
| 637 | void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb, | 736 | void 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 | ||
| 809 | discard: | 898 | discard: |
| @@ -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 | } |
| 818 | EXPORT_SYMBOL(l2tp_recv_common); | 905 | EXPORT_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 | } | ||
