Merge branches 'release', 'button-sysfs', 'misc', 'mismatch', 'randconfig' and 'toshi...
[sfrench/cifs-2.6.git] / drivers / net / pppol2tp.c
index f8904fd92369be8ee2ded044f8c55856f31960ac..3d10ca050b7991b9f53cf4a6cc3d5adb873137aa 100644 (file)
@@ -302,14 +302,14 @@ pppol2tp_session_find(struct pppol2tp_tunnel *tunnel, u16 session_id)
        struct pppol2tp_session *session;
        struct hlist_node *walk;
 
-       read_lock(&tunnel->hlist_lock);
+       read_lock_bh(&tunnel->hlist_lock);
        hlist_for_each_entry(session, walk, session_list, hlist) {
                if (session->tunnel_addr.s_session == session_id) {
-                       read_unlock(&tunnel->hlist_lock);
+                       read_unlock_bh(&tunnel->hlist_lock);
                        return session;
                }
        }
-       read_unlock(&tunnel->hlist_lock);
+       read_unlock_bh(&tunnel->hlist_lock);
 
        return NULL;
 }
@@ -320,14 +320,14 @@ static struct pppol2tp_tunnel *pppol2tp_tunnel_find(u16 tunnel_id)
 {
        struct pppol2tp_tunnel *tunnel = NULL;
 
-       read_lock(&pppol2tp_tunnel_list_lock);
+       read_lock_bh(&pppol2tp_tunnel_list_lock);
        list_for_each_entry(tunnel, &pppol2tp_tunnel_list, list) {
                if (tunnel->stats.tunnel_id == tunnel_id) {
-                       read_unlock(&pppol2tp_tunnel_list_lock);
+                       read_unlock_bh(&pppol2tp_tunnel_list_lock);
                        return tunnel;
                }
        }
-       read_unlock(&pppol2tp_tunnel_list_lock);
+       read_unlock_bh(&pppol2tp_tunnel_list_lock);
 
        return NULL;
 }
@@ -342,10 +342,11 @@ static struct pppol2tp_tunnel *pppol2tp_tunnel_find(u16 tunnel_id)
 static void pppol2tp_recv_queue_skb(struct pppol2tp_session *session, struct sk_buff *skb)
 {
        struct sk_buff *skbp;
+       struct sk_buff *tmp;
        u16 ns = PPPOL2TP_SKB_CB(skb)->ns;
 
-       spin_lock(&session->reorder_q.lock);
-       skb_queue_walk(&session->reorder_q, skbp) {
+       spin_lock_bh(&session->reorder_q.lock);
+       skb_queue_walk_safe(&session->reorder_q, skbp, tmp) {
                if (PPPOL2TP_SKB_CB(skbp)->ns > ns) {
                        __skb_insert(skb, skbp->prev, skbp, &session->reorder_q);
                        PRINTK(session->debug, PPPOL2TP_MSG_SEQ, KERN_DEBUG,
@@ -360,7 +361,7 @@ static void pppol2tp_recv_queue_skb(struct pppol2tp_session *session, struct sk_
        __skb_queue_tail(&session->reorder_q, skb);
 
 out:
-       spin_unlock(&session->reorder_q.lock);
+       spin_unlock_bh(&session->reorder_q.lock);
 }
 
 /* Dequeue a single skb.
@@ -371,10 +372,9 @@ static void pppol2tp_recv_dequeue_skb(struct pppol2tp_session *session, struct s
        int length = PPPOL2TP_SKB_CB(skb)->length;
        struct sock *session_sock = NULL;
 
-       /* We're about to requeue the skb, so unlink it and return resources
+       /* We're about to requeue the skb, so return resources
         * to its current owner (a socket receive buffer).
         */
-       skb_unlink(skb, &session->reorder_q);
        skb_orphan(skb);
 
        tunnel->stats.rx_packets++;
@@ -442,7 +442,7 @@ static void pppol2tp_recv_dequeue(struct pppol2tp_session *session)
         * expect to send up next, dequeue it and any other
         * in-sequence packets behind it.
         */
-       spin_lock(&session->reorder_q.lock);
+       spin_lock_bh(&session->reorder_q.lock);
        skb_queue_walk_safe(&session->reorder_q, skb, tmp) {
                if (time_after(jiffies, PPPOL2TP_SKB_CB(skb)->expires)) {
                        session->stats.rx_seq_discards++;
@@ -455,6 +455,7 @@ static void pppol2tp_recv_dequeue(struct pppol2tp_session *session)
                               skb_queue_len(&session->reorder_q));
                        __skb_unlink(skb, &session->reorder_q);
                        kfree_skb(skb);
+                       sock_put(session->sock);
                        continue;
                }
 
@@ -469,13 +470,18 @@ static void pppol2tp_recv_dequeue(struct pppol2tp_session *session)
                                goto out;
                        }
                }
-               spin_unlock(&session->reorder_q.lock);
+               __skb_unlink(skb, &session->reorder_q);
+
+               /* Process the skb. We release the queue lock while we
+                * do so to let other contexts process the queue.
+                */
+               spin_unlock_bh(&session->reorder_q.lock);
                pppol2tp_recv_dequeue_skb(session, skb);
-               spin_lock(&session->reorder_q.lock);
+               spin_lock_bh(&session->reorder_q.lock);
        }
 
 out:
-       spin_unlock(&session->reorder_q.lock);
+       spin_unlock_bh(&session->reorder_q.lock);
 }
 
 /* Internal receive frame. Do the real work of receiving an L2TP data frame
@@ -488,7 +494,7 @@ static int pppol2tp_recv_core(struct sock *sock, struct sk_buff *skb)
 {
        struct pppol2tp_session *session = NULL;
        struct pppol2tp_tunnel *tunnel;
-       unsigned char *ptr;
+       unsigned char *ptr, *optr;
        u16 hdrflags;
        u16 tunnel_id, session_id;
        int length;
@@ -496,7 +502,7 @@ static int pppol2tp_recv_core(struct sock *sock, struct sk_buff *skb)
 
        tunnel = pppol2tp_sock_to_tunnel(sock);
        if (tunnel == NULL)
-               goto error;
+               goto no_tunnel;
 
        /* UDP always verifies the packet length. */
        __skb_pull(skb, sizeof(struct udphdr));
@@ -509,7 +515,7 @@ static int pppol2tp_recv_core(struct sock *sock, struct sk_buff *skb)
        }
 
        /* Point to L2TP header */
-       ptr = skb->data;
+       optr = ptr = skb->data;
 
        /* Get L2TP header flags */
        hdrflags = ntohs(*(__be16*)ptr);
@@ -637,12 +643,14 @@ static int pppol2tp_recv_core(struct sock *sock, struct sk_buff *skb)
        /* If offset bit set, skip it. */
        if (hdrflags & L2TP_HDRFLAG_O) {
                offset = ntohs(*(__be16 *)ptr);
-               skb->transport_header += 2 + offset;
-               if (!pskb_may_pull(skb, skb_transport_offset(skb) + 2))
-                       goto discard;
+               ptr += 2 + offset;
        }
 
-       __skb_pull(skb, skb_transport_offset(skb));
+       offset = ptr - optr;
+       if (!pskb_may_pull(skb, offset))
+               goto discard;
+
+       __skb_pull(skb, offset);
 
        /* Skip PPP header, if present.  In testing, Microsoft L2TP clients
         * don't send the PPP header (PPP header compression enabled), but
@@ -652,6 +660,9 @@ static int pppol2tp_recv_core(struct sock *sock, struct sk_buff *skb)
         * Note that skb->data[] isn't dereferenced from a u16 ptr here since
         * the field may be unaligned.
         */
+       if (!pskb_may_pull(skb, 2))
+               goto discard;
+
        if ((skb->data[0] == 0xff) && (skb->data[1] == 0x03))
                skb_pull(skb, 2);
 
@@ -709,6 +720,10 @@ discard:
        return 0;
 
 error:
+       /* Put UDP header back */
+       __skb_push(skb, sizeof(struct udphdr));
+
+no_tunnel:
        return 1;
 }
 
@@ -1049,7 +1064,9 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
 
        /* Get routing info from the tunnel socket */
        dst_release(skb->dst);
-       skb->dst = sk_dst_get(sk_tun);
+       skb->dst = dst_clone(__sk_dst_get(sk_tun));
+       skb_orphan(skb);
+       skb->sk = sk_tun;
 
        /* Queue the packet to IP for output */
        len = skb->len;
@@ -1095,10 +1112,12 @@ static void pppol2tp_tunnel_closeall(struct pppol2tp_tunnel *tunnel)
        PRINTK(tunnel->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO,
               "%s: closing all sessions...\n", tunnel->name);
 
-       write_lock(&tunnel->hlist_lock);
+       write_lock_bh(&tunnel->hlist_lock);
        for (hash = 0; hash < PPPOL2TP_HASH_SIZE; hash++) {
 again:
                hlist_for_each_safe(walk, tmp, &tunnel->session_hlist[hash]) {
+                       struct sk_buff *skb;
+
                        session = hlist_entry(walk, struct pppol2tp_session, hlist);
 
                        sk = session->sock;
@@ -1115,7 +1134,7 @@ again:
                         * disappear as we're jumping between locks.
                         */
                        sock_hold(sk);
-                       write_unlock(&tunnel->hlist_lock);
+                       write_unlock_bh(&tunnel->hlist_lock);
                        lock_sock(sk);
 
                        if (sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND)) {
@@ -1127,7 +1146,10 @@ again:
                        /* Purge any queued data */
                        skb_queue_purge(&sk->sk_receive_queue);
                        skb_queue_purge(&sk->sk_write_queue);
-                       skb_queue_purge(&session->reorder_q);
+                       while ((skb = skb_dequeue(&session->reorder_q))) {
+                               kfree_skb(skb);
+                               sock_put(sk);
+                       }
 
                        release_sock(sk);
                        sock_put(sk);
@@ -1137,11 +1159,11 @@ again:
                         * list so we are guaranteed to make forward
                         * progress.
                         */
-                       write_lock(&tunnel->hlist_lock);
+                       write_lock_bh(&tunnel->hlist_lock);
                        goto again;
                }
        }
-       write_unlock(&tunnel->hlist_lock);
+       write_unlock_bh(&tunnel->hlist_lock);
 }
 
 /* Really kill the tunnel.
@@ -1150,9 +1172,9 @@ again:
 static void pppol2tp_tunnel_free(struct pppol2tp_tunnel *tunnel)
 {
        /* Remove from socket list */
-       write_lock(&pppol2tp_tunnel_list_lock);
+       write_lock_bh(&pppol2tp_tunnel_list_lock);
        list_del_init(&tunnel->list);
-       write_unlock(&pppol2tp_tunnel_list_lock);
+       write_unlock_bh(&pppol2tp_tunnel_list_lock);
 
        atomic_dec(&pppol2tp_tunnel_count);
        kfree(tunnel);
@@ -1228,9 +1250,9 @@ static void pppol2tp_session_destruct(struct sock *sk)
                                /* Delete the session socket from the
                                 * hash
                                 */
-                               write_lock(&tunnel->hlist_lock);
+                               write_lock_bh(&tunnel->hlist_lock);
                                hlist_del_init(&session->hlist);
-                               write_unlock(&tunnel->hlist_lock);
+                               write_unlock_bh(&tunnel->hlist_lock);
 
                                atomic_dec(&pppol2tp_session_count);
                        }
@@ -1375,9 +1397,9 @@ static struct sock *pppol2tp_prepare_tunnel_socket(int fd, u16 tunnel_id,
 
        /* Add tunnel to our list */
        INIT_LIST_HEAD(&tunnel->list);
-       write_lock(&pppol2tp_tunnel_list_lock);
+       write_lock_bh(&pppol2tp_tunnel_list_lock);
        list_add(&tunnel->list, &pppol2tp_tunnel_list);
-       write_unlock(&pppol2tp_tunnel_list_lock);
+       write_unlock_bh(&pppol2tp_tunnel_list_lock);
        atomic_inc(&pppol2tp_tunnel_count);
 
        /* Bump the reference count. The tunnel context is deleted
@@ -1582,11 +1604,11 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr,
        sk->sk_user_data = session;
 
        /* Add session to the tunnel's hash list */
-       write_lock(&tunnel->hlist_lock);
+       write_lock_bh(&tunnel->hlist_lock);
        hlist_add_head(&session->hlist,
                       pppol2tp_session_id_hash(tunnel,
                                                session->tunnel_addr.s_session));
-       write_unlock(&tunnel->hlist_lock);
+       write_unlock_bh(&tunnel->hlist_lock);
 
        atomic_inc(&pppol2tp_session_count);
 
@@ -2188,7 +2210,7 @@ static struct pppol2tp_session *next_session(struct pppol2tp_tunnel *tunnel, str
        int next = 0;
        int i;
 
-       read_lock(&tunnel->hlist_lock);
+       read_lock_bh(&tunnel->hlist_lock);
        for (i = 0; i < PPPOL2TP_HASH_SIZE; i++) {
                hlist_for_each_entry(session, walk, &tunnel->session_hlist[i], hlist) {
                        if (curr == NULL) {
@@ -2206,7 +2228,7 @@ static struct pppol2tp_session *next_session(struct pppol2tp_tunnel *tunnel, str
                }
        }
 out:
-       read_unlock(&tunnel->hlist_lock);
+       read_unlock_bh(&tunnel->hlist_lock);
        if (!found)
                session = NULL;
 
@@ -2217,13 +2239,13 @@ static struct pppol2tp_tunnel *next_tunnel(struct pppol2tp_tunnel *curr)
 {
        struct pppol2tp_tunnel *tunnel = NULL;
 
-       read_lock(&pppol2tp_tunnel_list_lock);
+       read_lock_bh(&pppol2tp_tunnel_list_lock);
        if (list_is_last(&curr->list, &pppol2tp_tunnel_list)) {
                goto out;
        }
        tunnel = list_entry(curr->list.next, struct pppol2tp_tunnel, list);
 out:
-       read_unlock(&pppol2tp_tunnel_list_lock);
+       read_unlock_bh(&pppol2tp_tunnel_list_lock);
 
        return tunnel;
 }
@@ -2278,10 +2300,12 @@ static void pppol2tp_seq_tunnel_show(struct seq_file *m, void *v)
                   atomic_read(&tunnel->ref_count) - 1);
        seq_printf(m, " %08x %llu/%llu/%llu %llu/%llu/%llu\n",
                   tunnel->debug,
-                  tunnel->stats.tx_packets, tunnel->stats.tx_bytes,
-                  tunnel->stats.tx_errors,
-                  tunnel->stats.rx_packets, tunnel->stats.rx_bytes,
-                  tunnel->stats.rx_errors);
+                  (unsigned long long)tunnel->stats.tx_packets,
+                  (unsigned long long)tunnel->stats.tx_bytes,
+                  (unsigned long long)tunnel->stats.tx_errors,
+                  (unsigned long long)tunnel->stats.rx_packets,
+                  (unsigned long long)tunnel->stats.rx_bytes,
+                  (unsigned long long)tunnel->stats.rx_errors);
 }
 
 static void pppol2tp_seq_session_show(struct seq_file *m, void *v)
@@ -2309,12 +2333,12 @@ static void pppol2tp_seq_session_show(struct seq_file *m, void *v)
                   jiffies_to_msecs(session->reorder_timeout));
        seq_printf(m, "   %hu/%hu %llu/%llu/%llu %llu/%llu/%llu\n",
                   session->nr, session->ns,
-                  session->stats.tx_packets,
-                  session->stats.tx_bytes,
-                  session->stats.tx_errors,
-                  session->stats.rx_packets,
-                  session->stats.rx_bytes,
-                  session->stats.rx_errors);
+                  (unsigned long long)session->stats.tx_packets,
+                  (unsigned long long)session->stats.tx_bytes,
+                  (unsigned long long)session->stats.tx_errors,
+                  (unsigned long long)session->stats.rx_packets,
+                  (unsigned long long)session->stats.rx_bytes,
+                  (unsigned long long)session->stats.rx_errors);
 }
 
 static int pppol2tp_seq_show(struct seq_file *m, void *v)
@@ -2457,9 +2481,10 @@ static int __init pppol2tp_init(void)
 
 out:
        return err;
-
+#ifdef CONFIG_PROC_FS
 out_unregister_pppox_proto:
        unregister_pppox_proto(PX_PROTO_OL2TP);
+#endif
 out_unregister_pppol2tp_proto:
        proto_unregister(&pppol2tp_sk_proto);
        goto out;
@@ -2478,7 +2503,7 @@ static void __exit pppol2tp_exit(void)
 module_init(pppol2tp_init);
 module_exit(pppol2tp_exit);
 
-MODULE_AUTHOR("Martijn van Oosterhout <kleptog@svana.org>,"
+MODULE_AUTHOR("Martijn van Oosterhout <kleptog@svana.org>, "
              "James Chapman <jchapman@katalix.com>");
 MODULE_DESCRIPTION("PPP over L2TP over UDP");
 MODULE_LICENSE("GPL");