// SPDX-License-Identifier: GPL-2.0-or-later /***************************************************************************** * Linux PPP over L2TP (PPPoX/PPPoL2TP) Sockets * * PPPoX --- Generic PPP encapsulation socket family * PPPoL2TP --- PPP over L2TP (RFC 2661) * * Version: 2.0.0 * * Authors: James Chapman (jchapman@katalix.com) * * Based on original work by Martijn van Oosterhout <kleptog@svana.org> * * License:
*/
/* This driver handles only L2TP data frames; control frames are handled by a * userspace application. * * To send data in an L2TP session, userspace opens a PPPoL2TP socket and * attaches it to a bound UDP socket with local tunnel_id / session_id and * peer tunnel_id / session_id set. Data can then be sent or received using * regular socket sendmsg() / recvmsg() calls. Kernel parameters of the socket * can be read or modified using ioctl() or [gs]etsockopt() calls. * * When a PPPoL2TP socket is connected with local and peer session_id values * zero, the socket is treated as a special tunnel management socket. * * Here's example userspace code to create a socket for sending/receiving data * over an L2TP session:- * * struct sockaddr_pppol2tp sax; * int fd; * int session_fd; * * fd = socket(AF_PPPOX, SOCK_DGRAM, PX_PROTO_OL2TP); * * sax.sa_family = AF_PPPOX; * sax.sa_protocol = PX_PROTO_OL2TP; * sax.pppol2tp.fd = tunnel_fd; // bound UDP socket * sax.pppol2tp.addr.sin_addr.s_addr = addr->sin_addr.s_addr; * sax.pppol2tp.addr.sin_port = addr->sin_port; * sax.pppol2tp.addr.sin_family = AF_INET; * sax.pppol2tp.s_tunnel = tunnel_id; * sax.pppol2tp.s_session = session_id; * sax.pppol2tp.d_tunnel = peer_tunnel_id; * sax.pppol2tp.d_session = peer_session_id; * * session_fd = connect(fd, (struct sockaddr *)&sax, sizeof(sax)); * * A pppd plugin that allows PPP traffic to be carried over L2TP using * this driver is available from the OpenL2TP project at * http://openl2tp.sourceforge.net.
*/
/* Space for UDP, L2TP and PPP headers */ #define PPPOL2TP_HEADER_OVERHEAD 40
/* Number of bytes to build transmit L2TP headers. * Unfortunately the size is different depending on whether sequence numbers * are enabled.
*/ #define PPPOL2TP_L2TP_HDR_SIZE_SEQ 10 #define PPPOL2TP_L2TP_HDR_SIZE_NOSEQ 6
/* Private data of each session. This data lives at the end of struct * l2tp_session, referenced via session->priv[].
*/ struct pppol2tp_session { int owner; /* pid that opened the socket */
struct mutex sk_lock; /* Protects .sk */ struct sock __rcu *sk; /* Pointer to the session PPPoX socket */ struct sock *__sk; /* Copy of .sk, for cleanup */
};
/***************************************************************************** * Receive data handling
*****************************************************************************/
/* Receive message. This is the recvmsg for the PPPoL2TP socket.
*/ staticint pppol2tp_recvmsg(struct socket *sock, struct msghdr *msg,
size_t len, int flags)
{ int err; struct sk_buff *skb; struct sock *sk = sock->sk;
err = -EIO; if (sk->sk_state & PPPOX_BOUND) goto end;
/* If the socket is bound, send it in to PPP's input queue. Otherwise * queue it on the session socket.
*/
rcu_read_lock();
sk = pppol2tp_session_get_sock(session); if (!sk) goto no_sock;
/* If the first two bytes are 0xFF03, consider that it is the PPP's * Address and Control fields and skip them. The L2TP module has always * worked this way, although, in theory, the use of these fields should * be negotiated and handled at the PPP layer. These fields are * constant: 0xFF is the All-Stations Address and 0x03 the Unnumbered * Information command with Poll/Final bit set to zero (RFC 1662).
*/ if (pskb_may_pull(skb, 2) && skb->data[0] == PPP_ALLSTATIONS &&
skb->data[1] == PPP_UI)
skb_pull(skb, 2);
if (sk->sk_state & PPPOX_BOUND) { struct pppox_sock *po;
po = pppox_sk(sk);
ppp_input(&po->chan, skb);
} else { if (sock_queue_rcv_skb(sk, skb) < 0) {
atomic_long_inc(&session->stats.rx_errors);
kfree_skb(skb);
}
}
rcu_read_unlock();
return;
no_sock:
rcu_read_unlock();
pr_warn_ratelimited("%s: no socket in recv\n", session->name);
kfree_skb(skb);
}
/* 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.
*/ staticint pppol2tp_sendmsg(struct socket *sock, struct msghdr *m,
size_t total_len)
{ struct sock *sk = sock->sk; struct sk_buff *skb; int error; struct l2tp_session *session; struct l2tp_tunnel *tunnel; int uhlen;
/* Transmit function called by generic PPP driver. Sends PPP frame * over PPPoL2TP socket. * * This is almost the same as pppol2tp_sendmsg(), but rather than * being called with a msghdr from userspace, it is called with a skb * from the kernel. * * The supplied skb from ppp doesn't have enough headroom for the * insertion of L2TP, UDP and IP headers so we need to allocate more * headroom in the skb. This will create a cloned skb. But we must be * careful in the error case because the caller will expect to free * the skb it supplied, not our cloned skb. So we take care to always * leave the original skb unfreed if we return an error.
*/ staticint pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
{ struct sock *sk = (struct sock *)chan->private; struct l2tp_session *session; struct l2tp_tunnel *tunnel; int uhlen, headroom;
if (sock_flag(sk, SOCK_DEAD) || !(sk->sk_state & PPPOX_CONNECTED)) goto abort;
/* Get session and tunnel contexts from the socket */
session = pppol2tp_sock_to_session(sk); if (!session) goto abort;
/* drop ref taken when we referenced socket via sk_user_data */
l2tp_session_put(session);
}
}
/* Called when the PPPoX socket (session) is closed.
*/ staticint pppol2tp_release(struct socket *sock)
{ struct sock *sk = sock->sk; struct l2tp_session *session; int error;
/* Signal the death of the socket. */
sk->sk_state = PPPOX_DEAD;
sock_orphan(sk);
sock->sk = NULL;
session = pppol2tp_sock_to_session(sk); if (session) {
l2tp_session_delete(session); /* drop ref taken by pppol2tp_sock_to_session */
l2tp_session_put(session);
}
/* Rough estimation of the maximum payload size a tunnel can transmit without * fragmenting at the lower IP layer. Assumes L2TPv2 with sequence * numbers and no IP option. Not quite accurate, but the result is mostly * unused anyway.
*/ staticint pppol2tp_tunnel_mtu(conststruct l2tp_tunnel *tunnel)
{ int mtu;
mtu = l2tp_tunnel_dst_mtu(tunnel); if (mtu <= PPPOL2TP_HEADER_OVERHEAD) return 1500 - PPPOL2TP_HEADER_OVERHEAD;
return mtu - PPPOL2TP_HEADER_OVERHEAD;
}
staticstruct l2tp_tunnel *pppol2tp_tunnel_get(struct net *net, conststruct l2tp_connect_info *info, bool *new_tunnel)
{ struct l2tp_tunnel *tunnel; int error;
*new_tunnel = false;
tunnel = l2tp_tunnel_get(net, info->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 (!info->session_id && !info->peer_session_id) { if (!tunnel) { struct l2tp_tunnel_cfg tcfg = {
.encap = L2TP_ENCAPTYPE_UDP,
};
/* Prevent l2tp_tunnel_register() from trying to set up * a kernel socket.
*/ if (info->fd < 0) return ERR_PTR(-EBADF);
/* Using a pre-existing session is fine as long as it hasn't * been connected yet.
*/
mutex_lock(&ps->sk_lock); if (rcu_dereference_protected(ps->sk,
lockdep_is_held(&ps->sk_lock)) ||
ps->__sk) {
mutex_unlock(&ps->sk_lock);
error = -EEXIST; goto end;
}
} else {
cfg.pw_type = L2TP_PWTYPE_PPP;
/* Special case: if source & dest session_id == 0x0000, this * socket is being created to manage the tunnel. Just set up * the internal context for use by ioctl() and sockopt() * handlers.
*/ if (session->session_id == 0 && session->peer_session_id == 0) {
error = 0; goto out_no_ppp;
}
/* The only header we need to worry about is the L2TP * header. This size is different depending on whether * sequence numbers are enabled for the data channel.
*/
po->chan.hdrlen = PPPOL2TP_L2TP_HDR_SIZE_NOSEQ;
out_no_ppp: /* This is how we get the session context from the socket. */
sock_hold(sk);
rcu_assign_sk_user_data(sk, session);
rcu_assign_pointer(ps->sk, sk);
mutex_unlock(&ps->sk_lock);
/* Keep the reference we've grabbed on the session: sk doesn't expect * the session to disappear. pppol2tp_session_close() is responsible * for dropping it.
*/
drop_refcnt = false;
sk->sk_state = PPPOX_CONNECTED;
end: if (error) { if (new_session)
l2tp_session_delete(session); if (new_tunnel)
l2tp_tunnel_delete(tunnel);
} if (drop_refcnt)
l2tp_session_put(session);
l2tp_tunnel_put(tunnel);
release_sock(sk);
return error;
}
#ifdef CONFIG_L2TP_V3
/* Called when creating sessions via the netlink interface. */ staticint pppol2tp_session_create(struct net *net, struct l2tp_tunnel *tunnel,
u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg)
{ int error; struct l2tp_session *session;
/* Error if tunnel socket is not prepped */ if (!tunnel->sock) {
error = -ENOENT; goto err;
}
/* Allocate and initialize a new session context. */
session = l2tp_session_create(sizeof(struct pppol2tp_session),
tunnel, session_id,
peer_session_id, cfg); if (IS_ERR(session)) {
error = PTR_ERR(session); goto err;
}
pppol2tp_session_init(session);
error = l2tp_session_register(session, tunnel); if (error < 0) goto err_sess;
/**************************************************************************** * ioctl() handlers. * * The PPPoX socket is created for L2TP sessions: tunnels have their own UDP * sockets. However, in order to control kernel tunnel features, we allow * userspace to create a special "tunnel" PPPoX socket which is used for * control only. Tunnel PPPoX sockets have session_id == 0 and simply allow * the user application to issue L2TP setsockopt(), getsockopt() and ioctl() * calls.
****************************************************************************/
if (!stats->session_id) {
pppol2tp_copy_stats(stats, &tunnel->stats); return 0;
}
/* If session_id is set, search the corresponding session in the * context of this tunnel and record the session's statistics.
*/
session = l2tp_session_get(tunnel->l2tp_net, tunnel->sock, tunnel->version,
tunnel->tunnel_id, stats->session_id); if (!session) return -EBADR;
if (session->pwtype != L2TP_PWTYPE_PPP) {
l2tp_session_put(session); return -EBADR;
}
if (copy_to_user((void __user *)arg, &stats, sizeof(stats))) return -EFAULT; break;
default: return -ENOIOCTLCMD;
}
return 0;
}
/***************************************************************************** * setsockopt() / getsockopt() support. * * The PPPoX socket is created for L2TP sessions: tunnels have their own UDP * sockets. In order to control kernel tunnel features, we allow userspace to * create a special "tunnel" PPPoX socket which is used for control only. * Tunnel PPPoX sockets have session_id == 0 and simply allow the user * application to issue L2TP setsockopt(), getsockopt() and ioctl() calls.
*****************************************************************************/
/* Tunnel setsockopt() helper.
*/ staticint pppol2tp_tunnel_setsockopt(struct sock *sk, struct l2tp_tunnel *tunnel, int optname, int val)
{ int err = 0;
switch (optname) { case PPPOL2TP_SO_DEBUG: /* Tunnel debug flags option is deprecated */ break;
default:
err = -ENOPROTOOPT; break;
}
return err;
}
/* Session setsockopt helper.
*/ staticint pppol2tp_session_setsockopt(struct sock *sk, struct l2tp_session *session, int optname, int val)
{ int err = 0;
switch (optname) { case PPPOL2TP_SO_RECVSEQ: if (val != 0 && val != 1) {
err = -EINVAL; break;
}
session->recv_seq = !!val; break;
case PPPOL2TP_SO_SENDSEQ: if (val != 0 && val != 1) {
err = -EINVAL; break;
}
session->send_seq = !!val;
{ struct pppox_sock *po = pppox_sk(sk);
case PPPOL2TP_SO_LNSMODE: if (val != 0 && val != 1) {
err = -EINVAL; break;
}
session->lns_mode = !!val; break;
case PPPOL2TP_SO_DEBUG: /* Session debug flags option is deprecated */ break;
case PPPOL2TP_SO_REORDERTO:
session->reorder_timeout = msecs_to_jiffies(val); break;
default:
err = -ENOPROTOOPT; break;
}
return err;
}
/* Main setsockopt() entry point. * Does API checks, then calls either the tunnel or session setsockopt * handler, according to whether the PPPoL2TP socket is a for a regular * session or the special tunnel type.
*/ staticint pppol2tp_setsockopt(struct socket *sock, int level, int optname,
sockptr_t optval, unsignedint optlen)
{ struct sock *sk = sock->sk; struct l2tp_session *session; struct l2tp_tunnel *tunnel; int val; int err;
if (level != SOL_PPPOL2TP) return -EINVAL;
if (optlen < sizeof(int)) return -EINVAL;
if (copy_from_sockptr(&val, optval, sizeof(int))) return -EFAULT;
err = -ENOTCONN; if (!sk->sk_user_data) goto end;
/* Get session context from the socket */
err = -EBADF;
session = pppol2tp_sock_to_session(sk); if (!session) goto end;
/* Special case: if session_id == 0x0000, treat as operation on tunnel
*/ if (session->session_id == 0 && session->peer_session_id == 0) {
tunnel = session->tunnel;
err = pppol2tp_tunnel_setsockopt(sk, tunnel, optname, val);
} else {
err = pppol2tp_session_setsockopt(sk, session, optname, val);
}
l2tp_session_put(session);
end: return err;
}
/* Tunnel getsockopt helper. Called with sock locked.
*/ staticint pppol2tp_tunnel_getsockopt(struct sock *sk, struct l2tp_tunnel *tunnel, int optname, int *val)
{ int err = 0;
switch (optname) { case PPPOL2TP_SO_DEBUG: /* Tunnel debug flags option is deprecated */
*val = 0; break;
default:
err = -ENOPROTOOPT; break;
}
return err;
}
/* Session getsockopt helper. Called with sock locked.
*/ staticint pppol2tp_session_getsockopt(struct sock *sk, struct l2tp_session *session, int optname, int *val)
{ int err = 0;
switch (optname) { case PPPOL2TP_SO_RECVSEQ:
*val = session->recv_seq; break;
case PPPOL2TP_SO_SENDSEQ:
*val = session->send_seq; break;
case PPPOL2TP_SO_LNSMODE:
*val = session->lns_mode; break;
case PPPOL2TP_SO_DEBUG: /* Session debug flags option is deprecated */
*val = 0; break;
case PPPOL2TP_SO_REORDERTO:
*val = (int)jiffies_to_msecs(session->reorder_timeout); break;
default:
err = -ENOPROTOOPT;
}
return err;
}
/* Main getsockopt() entry point. * Does API checks, then calls either the tunnel or session getsockopt * handler, according to whether the PPPoX socket is a for a regular session * or the special tunnel type.
*/ staticint pppol2tp_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen)
{ struct sock *sk = sock->sk; struct l2tp_session *session; struct l2tp_tunnel *tunnel; int val, len; int err;
if (level != SOL_PPPOL2TP) return -EINVAL;
if (get_user(len, optlen)) return -EFAULT;
if (len < 0) return -EINVAL;
len = min_t(unsignedint, len, sizeof(int));
err = -ENOTCONN; if (!sk->sk_user_data) goto end;
/* Get the session context */
err = -EBADF;
session = pppol2tp_sock_to_session(sk); if (!session) goto end;
/* Special case: if session_id == 0x0000, treat as operation on tunnel */ if (session->session_id == 0 && session->peer_session_id == 0) {
tunnel = session->tunnel;
err = pppol2tp_tunnel_getsockopt(sk, tunnel, optname, &val); if (err) goto end_put_sess;
} else {
err = pppol2tp_session_getsockopt(sk, session, optname, &val); if (err) goto end_put_sess;
}
err = -EFAULT; if (put_user(len, optlen)) goto end_put_sess;
if (copy_to_user((void __user *)optval, &val, len)) goto end_put_sess;
/***************************************************************************** * /proc filesystem for debug * Since the original pppol2tp driver provided /proc/net/pppol2tp for * L2TPv2, we dump only L2TPv2 tunnels and sessions here.
*****************************************************************************/
#ifdef CONFIG_PROC_FS
struct pppol2tp_seq_data { struct seq_net_private p; unsignedlong tkey; /* lookup key of current tunnel */ unsignedlong skey; /* lookup key of current session */ struct l2tp_tunnel *tunnel; struct l2tp_session *session; /* NULL means get next tunnel */
};
staticvoid pppol2tp_next_tunnel(struct net *net, struct pppol2tp_seq_data *pd)
{ /* Drop reference taken during previous invocation */ if (pd->tunnel)
l2tp_tunnel_put(pd->tunnel);
for (;;) {
pd->tunnel = l2tp_tunnel_get_next(net, &pd->tkey);
pd->tkey++;
/* Only accept L2TPv2 tunnels */ if (!pd->tunnel || pd->tunnel->version == 2) return;
l2tp_tunnel_put(pd->tunnel);
}
}
staticvoid pppol2tp_next_session(struct net *net, struct pppol2tp_seq_data *pd)
{ /* Drop reference taken during previous invocation */ if (pd->session)
l2tp_session_put(pd->session);
/* Drop reference taken by last invocation of pppol2tp_next_session() * or pppol2tp_next_tunnel().
*/ if (pd->session) {
l2tp_session_put(pd->session);
pd->session = NULL;
} if (pd->tunnel) {
l2tp_tunnel_put(pd->tunnel);
pd->tunnel = NULL;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.