atomic: use <linux/atomic.h>
[linux-2.6.git] / net / l2tp / l2tp_ppp.c
index baac072..f42cd09 100644 (file)
@@ -87,6 +87,7 @@
 #include <linux/hash.h>
 #include <linux/sort.h>
 #include <linux/proc_fs.h>
+#include <linux/l2tp.h>
 #include <linux/nsproxy.h>
 #include <net/net_namespace.h>
 #include <net/netns/generic.h>
@@ -96,7 +97,7 @@
 #include <net/xfrm.h>
 
 #include <asm/byteorder.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
 
 #include "l2tp_core.h"
 
@@ -134,7 +135,10 @@ struct pppol2tp_session {
 
 static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb);
 
-static struct ppp_channel_ops pppol2tp_chan_ops = { pppol2tp_xmit , NULL };
+static const struct ppp_channel_ops pppol2tp_chan_ops = {
+       .start_xmit =  pppol2tp_xmit,
+};
+
 static const struct proto_ops pppol2tp_ops;
 
 /* Helpers to obtain tunnel/session contexts from sockets.
@@ -291,17 +295,6 @@ static void pppol2tp_session_sock_put(struct l2tp_session *session)
  * Transmit handling
  ***********************************************************************/
 
-/* Tell how big L2TP headers are for a particular session. This
- * depends on whether sequence numbers are being used.
- */
-static inline int pppol2tp_l2tp_header_len(struct l2tp_session *session)
-{
-       if (session->send_seq)
-               return PPPOL2TP_L2TP_HDR_SIZE_SEQ;
-
-       return PPPOL2TP_L2TP_HDR_SIZE_NOSEQ;
-}
-
 /* This is the sendmsg for the PPPoL2TP pppol2tp_session socket.  We come here
  * when a user application does a sendmsg() on the session socket. L2TP and
  * PPP headers must be inserted into the user's data.
@@ -316,6 +309,7 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh
        struct l2tp_session *session;
        struct l2tp_tunnel *tunnel;
        struct pppol2tp_session *ps;
+       int uhlen;
 
        error = -ENOTCONN;
        if (sock_flag(sk, SOCK_DEAD) || !(sk->sk_state & PPPOX_CONNECTED))
@@ -332,10 +326,12 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh
        if (tunnel == NULL)
                goto error_put_sess;
 
+       uhlen = (tunnel->encap == L2TP_ENCAPTYPE_UDP) ? sizeof(struct udphdr) : 0;
+
        /* Allocate a socket buffer */
        error = -ENOMEM;
        skb = sock_wmalloc(sk, NET_SKB_PAD + sizeof(struct iphdr) +
-                          sizeof(struct udphdr) + session->hdr_len +
+                          uhlen + session->hdr_len +
                           sizeof(ppph) + total_len,
                           0, GFP_KERNEL);
        if (!skb)
@@ -346,7 +342,7 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh
        skb_reset_network_header(skb);
        skb_reserve(skb, sizeof(struct iphdr));
        skb_reset_transport_header(skb);
-       skb_reserve(skb, sizeof(struct udphdr));
+       skb_reserve(skb, uhlen);
 
        /* Add PPP header */
        skb->data[0] = ppph[0];
@@ -394,7 +390,6 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
        static const u8 ppph[2] = { 0xff, 0x03 };
        struct sock *sk = (struct sock *) chan->private;
        struct sock *sk_tun;
-       int hdr_len;
        struct l2tp_session *session;
        struct l2tp_tunnel *tunnel;
        struct pppol2tp_session *ps;
@@ -417,9 +412,6 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
        if (tunnel == NULL)
                goto abort_put_sess;
 
-       /* What header length is configured for this session? */
-       hdr_len = pppol2tp_l2tp_header_len(session);
-
        old_headroom = skb_headroom(skb);
        if (skb_cow_head(skb, sizeof(ppph)))
                goto abort_put_sess_tun;
@@ -432,7 +424,7 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
        skb->data[0] = ppph[0];
        skb->data[1] = ppph[1];
 
-       l2tp_xmit_skb(session, skb, hdr_len);
+       l2tp_xmit_skb(session, skb, session->hdr_len);
 
        sock_put(sk_tun);
        sock_put(sk);
@@ -608,6 +600,20 @@ out:
        return error;
 }
 
+#if defined(CONFIG_L2TP_DEBUGFS) || defined(CONFIG_L2TP_DEBUGFS_MODULE)
+static void pppol2tp_show(struct seq_file *m, void *arg)
+{
+       struct l2tp_session *session = arg;
+       struct pppol2tp_session *ps = l2tp_session_priv(session);
+
+       if (ps) {
+               struct pppox_sock *po = pppox_sk(ps->sock);
+               if (po)
+                       seq_printf(m, "   interface %s\n", ppp_dev_name(&po->chan));
+       }
+}
+#endif
+
 /* connect() handler. Attach a PPPoX socket to a tunnel UDP socket
  */
 static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr,
@@ -615,6 +621,7 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr,
 {
        struct sock *sk = sock->sk;
        struct sockaddr_pppol2tp *sp = (struct sockaddr_pppol2tp *) uservaddr;
+       struct sockaddr_pppol2tpv3 *sp3 = (struct sockaddr_pppol2tpv3 *) uservaddr;
        struct pppox_sock *po = pppox_sk(sk);
        struct l2tp_session *session = NULL;
        struct l2tp_tunnel *tunnel;
@@ -622,6 +629,10 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr,
        struct dst_entry *dst;
        struct l2tp_session_cfg cfg = { 0, };
        int error = 0;
+       u32 tunnel_id, peer_tunnel_id;
+       u32 session_id, peer_session_id;
+       int ver = 2;
+       int fd;
 
        lock_sock(sk);
 
@@ -639,22 +650,47 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr,
        if (sk->sk_user_data)
                goto end; /* socket is already attached */
 
-       /* Don't bind if s_tunnel is 0 */
+       /* Get params from socket address. Handle L2TPv2 and L2TPv3 */
+       if (sockaddr_len == sizeof(struct sockaddr_pppol2tp)) {
+               fd = sp->pppol2tp.fd;
+               tunnel_id = sp->pppol2tp.s_tunnel;
+               peer_tunnel_id = sp->pppol2tp.d_tunnel;
+               session_id = sp->pppol2tp.s_session;
+               peer_session_id = sp->pppol2tp.d_session;
+       } else if (sockaddr_len == sizeof(struct sockaddr_pppol2tpv3)) {
+               ver = 3;
+               fd = sp3->pppol2tp.fd;
+               tunnel_id = sp3->pppol2tp.s_tunnel;
+               peer_tunnel_id = sp3->pppol2tp.d_tunnel;
+               session_id = sp3->pppol2tp.s_session;
+               peer_session_id = sp3->pppol2tp.d_session;
+       } else {
+               error = -EINVAL;
+               goto end; /* bad socket address */
+       }
+
+       /* Don't bind if tunnel_id is 0 */
        error = -EINVAL;
-       if (sp->pppol2tp.s_tunnel == 0)
+       if (tunnel_id == 0)
                goto end;
 
-       /* Special case: create tunnel context if s_session and
-        * d_session is 0. Otherwise look up tunnel using supplied
+       tunnel = l2tp_tunnel_find(sock_net(sk), tunnel_id);
+
+       /* Special case: create tunnel context if session_id and
+        * peer_session_id is 0. Otherwise look up tunnel using supplied
         * tunnel id.
         */
-       if ((sp->pppol2tp.s_session == 0) && (sp->pppol2tp.d_session == 0)) {
-               error = l2tp_tunnel_create(sock_net(sk), sp->pppol2tp.fd, 2, sp->pppol2tp.s_tunnel, sp->pppol2tp.d_tunnel, NULL, &tunnel);
-               if (error < 0)
-                       goto end;
+       if ((session_id == 0) && (peer_session_id == 0)) {
+               if (tunnel == NULL) {
+                       struct l2tp_tunnel_cfg tcfg = {
+                               .encap = L2TP_ENCAPTYPE_UDP,
+                               .debug = 0,
+                       };
+                       error = l2tp_tunnel_create(sock_net(sk), fd, ver, tunnel_id, peer_tunnel_id, &tcfg, &tunnel);
+                       if (error < 0)
+                               goto end;
+               }
        } else {
-               tunnel = l2tp_tunnel_find(sock_net(sk), sp->pppol2tp.s_tunnel);
-
                /* Error if we can't find the tunnel */
                error = -ENOENT;
                if (tunnel == NULL)
@@ -668,28 +704,46 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr,
        if (tunnel->recv_payload_hook == NULL)
                tunnel->recv_payload_hook = pppol2tp_recv_payload_hook;
 
-       /* Check that this session doesn't already exist */
-       error = -EEXIST;
-       session = l2tp_session_find(tunnel, sp->pppol2tp.s_session);
-       if (session != NULL)
-               goto end;
+       if (tunnel->peer_tunnel_id == 0) {
+               if (ver == 2)
+                       tunnel->peer_tunnel_id = sp->pppol2tp.d_tunnel;
+               else
+                       tunnel->peer_tunnel_id = sp3->pppol2tp.d_tunnel;
+       }
 
-       /* Default MTU must allow space for UDP/L2TP/PPP
-        * headers.
+       /* Create session if it doesn't already exist. We handle the
+        * case where a session was previously created by the netlink
+        * interface by checking that the session doesn't already have
+        * a socket and its tunnel socket are what we expect. If any
+        * of those checks fail, return EEXIST to the caller.
         */
-       cfg.mtu = cfg.mru = 1500 - PPPOL2TP_HEADER_OVERHEAD;
-       cfg.hdr_len = PPPOL2TP_L2TP_HDR_SIZE_NOSEQ;
-       cfg.debug = tunnel->debug;
-
-       /* Allocate and initialize a new session context. */
-       session = l2tp_session_create(sizeof(struct pppol2tp_session),
-                                     tunnel, sp->pppol2tp.s_session,
-                                     sp->pppol2tp.d_session, &cfg);
+       session = l2tp_session_find(sock_net(sk), tunnel, session_id);
        if (session == NULL) {
-               error = -ENOMEM;
-               goto end;
+               /* Default MTU must allow space for UDP/L2TP/PPP
+                * headers.
+                */
+               cfg.mtu = cfg.mru = 1500 - PPPOL2TP_HEADER_OVERHEAD;
+
+               /* Allocate and initialize a new session context. */
+               session = l2tp_session_create(sizeof(struct pppol2tp_session),
+                                             tunnel, session_id,
+                                             peer_session_id, &cfg);
+               if (session == NULL) {
+                       error = -ENOMEM;
+                       goto end;
+               }
+       } else {
+               ps = l2tp_session_priv(session);
+               error = -EEXIST;
+               if (ps->sock != NULL)
+                       goto end;
+
+               /* consistency checks */
+               if (ps->tunnel_sock != tunnel->sock)
+                       goto end;
        }
 
+       /* Associate session with its PPPoL2TP socket */
        ps = l2tp_session_priv(session);
        ps->owner            = current->pid;
        ps->sock             = sk;
@@ -697,6 +751,9 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr,
 
        session->recv_skb       = pppol2tp_recv;
        session->session_close  = pppol2tp_session_close;
+#if defined(CONFIG_L2TP_DEBUGFS) || defined(CONFIG_L2TP_DEBUGFS_MODULE)
+       session->show           = pppol2tp_show;
+#endif
 
        /* We need to know each time a skb is dropped from the reorder
         * queue.
@@ -752,13 +809,80 @@ end:
        return error;
 }
 
+#ifdef CONFIG_L2TP_V3
+
+/* Called when creating sessions via the netlink interface.
+ */
+static int pppol2tp_session_create(struct net *net, u32 tunnel_id, u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg)
+{
+       int error;
+       struct l2tp_tunnel *tunnel;
+       struct l2tp_session *session;
+       struct pppol2tp_session *ps;
+
+       tunnel = l2tp_tunnel_find(net, tunnel_id);
+
+       /* Error if we can't find the tunnel */
+       error = -ENOENT;
+       if (tunnel == NULL)
+               goto out;
+
+       /* Error if tunnel socket is not prepped */
+       if (tunnel->sock == NULL)
+               goto out;
+
+       /* Check that this session doesn't already exist */
+       error = -EEXIST;
+       session = l2tp_session_find(net, tunnel, session_id);
+       if (session != NULL)
+               goto out;
+
+       /* Default MTU values. */
+       if (cfg->mtu == 0)
+               cfg->mtu = 1500 - PPPOL2TP_HEADER_OVERHEAD;
+       if (cfg->mru == 0)
+               cfg->mru = cfg->mtu;
+
+       /* Allocate and initialize a new session context. */
+       error = -ENOMEM;
+       session = l2tp_session_create(sizeof(struct pppol2tp_session),
+                                     tunnel, session_id,
+                                     peer_session_id, cfg);
+       if (session == NULL)
+               goto out;
+
+       ps = l2tp_session_priv(session);
+       ps->tunnel_sock = tunnel->sock;
+
+       PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO,
+              "%s: created\n", session->name);
+
+       error = 0;
+
+out:
+       return error;
+}
+
+/* Called when deleting sessions via the netlink interface.
+ */
+static int pppol2tp_session_delete(struct l2tp_session *session)
+{
+       struct pppol2tp_session *ps = l2tp_session_priv(session);
+
+       if (ps->sock == NULL)
+               l2tp_session_dec_refcount(session);
+
+       return 0;
+}
+
+#endif /* CONFIG_L2TP_V3 */
+
 /* getname() support.
  */
 static int pppol2tp_getname(struct socket *sock, struct sockaddr *uaddr,
                            int *usockaddr_len, int peer)
 {
-       int len = sizeof(struct sockaddr_pppol2tp);
-       struct sockaddr_pppol2tp sp;
+       int len = 0;
        int error = 0;
        struct l2tp_session *session;
        struct l2tp_tunnel *tunnel;
@@ -784,21 +908,40 @@ static int pppol2tp_getname(struct socket *sock, struct sockaddr *uaddr,
                goto end_put_sess;
        }
 
-       memset(&sp, 0, len);
-       sp.sa_family    = AF_PPPOX;
-       sp.sa_protocol  = PX_PROTO_OL2TP;
-       sp.pppol2tp.fd  = tunnel->fd;
-       sp.pppol2tp.pid = pls->owner;
-       sp.pppol2tp.s_tunnel = tunnel->tunnel_id;
-       sp.pppol2tp.d_tunnel = tunnel->peer_tunnel_id;
-       sp.pppol2tp.s_session = session->session_id;
-       sp.pppol2tp.d_session = session->peer_session_id;
        inet = inet_sk(sk);
-       sp.pppol2tp.addr.sin_family = AF_INET;
-       sp.pppol2tp.addr.sin_port = inet->inet_dport;
-       sp.pppol2tp.addr.sin_addr.s_addr = inet->inet_daddr;
-
-       memcpy(uaddr, &sp, len);
+       if (tunnel->version == 2) {
+               struct sockaddr_pppol2tp sp;
+               len = sizeof(sp);
+               memset(&sp, 0, len);
+               sp.sa_family    = AF_PPPOX;
+               sp.sa_protocol  = PX_PROTO_OL2TP;
+               sp.pppol2tp.fd  = tunnel->fd;
+               sp.pppol2tp.pid = pls->owner;
+               sp.pppol2tp.s_tunnel = tunnel->tunnel_id;
+               sp.pppol2tp.d_tunnel = tunnel->peer_tunnel_id;
+               sp.pppol2tp.s_session = session->session_id;
+               sp.pppol2tp.d_session = session->peer_session_id;
+               sp.pppol2tp.addr.sin_family = AF_INET;
+               sp.pppol2tp.addr.sin_port = inet->inet_dport;
+               sp.pppol2tp.addr.sin_addr.s_addr = inet->inet_daddr;
+               memcpy(uaddr, &sp, len);
+       } else if (tunnel->version == 3) {
+               struct sockaddr_pppol2tpv3 sp;
+               len = sizeof(sp);
+               memset(&sp, 0, len);
+               sp.sa_family    = AF_PPPOX;
+               sp.sa_protocol  = PX_PROTO_OL2TP;
+               sp.pppol2tp.fd  = tunnel->fd;
+               sp.pppol2tp.pid = pls->owner;
+               sp.pppol2tp.s_tunnel = tunnel->tunnel_id;
+               sp.pppol2tp.d_tunnel = tunnel->peer_tunnel_id;
+               sp.pppol2tp.s_session = session->session_id;
+               sp.pppol2tp.d_session = session->peer_session_id;
+               sp.pppol2tp.addr.sin_family = AF_INET;
+               sp.pppol2tp.addr.sin_port = inet->inet_dport;
+               sp.pppol2tp.addr.sin_addr.s_addr = inet->inet_daddr;
+               memcpy(uaddr, &sp, len);
+       }
 
        *usockaddr_len = len;
 
@@ -999,7 +1142,7 @@ static int pppol2tp_tunnel_ioctl(struct l2tp_tunnel *tunnel,
                if (stats.session_id != 0) {
                        /* resend to session ioctl handler */
                        struct l2tp_session *session =
-                               l2tp_session_find(tunnel, stats.session_id);
+                               l2tp_session_find(sock_net(sk), tunnel, stats.session_id);
                        if (session != NULL)
                                err = pppol2tp_session_ioctl(session, cmd, arg);
                        else
@@ -1375,6 +1518,8 @@ end:
 
 /*****************************************************************************
  * /proc filesystem for debug
+ * Since the original pppol2tp driver provided /proc/net/pppol2tp for
+ * L2TPv2, we dump only L2TPv2 tunnels and sessions here.
  *****************************************************************************/
 
 static unsigned int pppol2tp_net_id;
@@ -1391,14 +1536,24 @@ struct pppol2tp_seq_data {
 
 static void pppol2tp_next_tunnel(struct net *net, struct pppol2tp_seq_data *pd)
 {
-       pd->tunnel = l2tp_tunnel_find_nth(net, pd->tunnel_idx);
-       pd->tunnel_idx++;
+       for (;;) {
+               pd->tunnel = l2tp_tunnel_find_nth(net, pd->tunnel_idx);
+               pd->tunnel_idx++;
+
+               if (pd->tunnel == NULL)
+                       break;
+
+               /* Ignore L2TPv3 tunnels */
+               if (pd->tunnel->version < 3)
+                       break;
+       }
 }
 
 static void pppol2tp_next_session(struct net *net, struct pppol2tp_seq_data *pd)
 {
        pd->session = l2tp_session_find_nth(pd->tunnel, pd->session_idx);
        pd->session_idx++;
+
        if (pd->session == NULL) {
                pd->session_idx = 0;
                pppol2tp_next_tunnel(net, pd);
@@ -1465,6 +1620,7 @@ static void pppol2tp_seq_session_show(struct seq_file *m, void *v)
        struct l2tp_session *session = v;
        struct l2tp_tunnel *tunnel = session->tunnel;
        struct pppol2tp_session *ps = l2tp_session_priv(session);
+       struct pppox_sock *po = pppox_sk(ps->sock);
        u32 ip = 0;
        u16 port = 0;
 
@@ -1499,6 +1655,9 @@ static void pppol2tp_seq_session_show(struct seq_file *m, void *v)
                   (unsigned long long)session->stats.rx_packets,
                   (unsigned long long)session->stats.rx_bytes,
                   (unsigned long long)session->stats.rx_errors);
+
+       if (po)
+               seq_printf(m, "   interface %s\n", ppp_dev_name(&po->chan));
 }
 
 static int pppol2tp_seq_show(struct seq_file *m, void *v)
@@ -1609,11 +1768,20 @@ static const struct proto_ops pppol2tp_ops = {
        .ioctl          = pppox_ioctl,
 };
 
-static struct pppox_proto pppol2tp_proto = {
+static const struct pppox_proto pppol2tp_proto = {
        .create         = pppol2tp_create,
        .ioctl          = pppol2tp_ioctl
 };
 
+#ifdef CONFIG_L2TP_V3
+
+static const struct l2tp_nl_cmd_ops pppol2tp_nl_cmd_ops = {
+       .session_create = pppol2tp_session_create,
+       .session_delete = pppol2tp_session_delete,
+};
+
+#endif /* CONFIG_L2TP_V3 */
+
 static int __init pppol2tp_init(void)
 {
        int err;
@@ -1630,11 +1798,22 @@ static int __init pppol2tp_init(void)
        if (err)
                goto out_unregister_pppol2tp_proto;
 
+#ifdef CONFIG_L2TP_V3
+       err = l2tp_nl_register_ops(L2TP_PWTYPE_PPP, &pppol2tp_nl_cmd_ops);
+       if (err)
+               goto out_unregister_pppox;
+#endif
+
        printk(KERN_INFO "PPPoL2TP kernel driver, %s\n",
               PPPOL2TP_DRV_VERSION);
 
 out:
        return err;
+
+#ifdef CONFIG_L2TP_V3
+out_unregister_pppox:
+       unregister_pppox_proto(PX_PROTO_OL2TP);
+#endif
 out_unregister_pppol2tp_proto:
        proto_unregister(&pppol2tp_sk_proto);
 out_unregister_pppol2tp_pernet:
@@ -1644,6 +1823,9 @@ out_unregister_pppol2tp_pernet:
 
 static void __exit pppol2tp_exit(void)
 {
+#ifdef CONFIG_L2TP_V3
+       l2tp_nl_unregister_ops(L2TP_PWTYPE_PPP);
+#endif
        unregister_pppox_proto(PX_PROTO_OL2TP);
        proto_unregister(&pppol2tp_sk_proto);
        unregister_pernet_device(&pppol2tp_net_ops);