/* Space for UDP, L2TP and PPP headers */ #define PPPOL2TP_HEADER_OVERHEAD 40
/* Number of bytes to build transmit L2TP headers. *Unfortunatelythesizeisdifferentdependingonwhethersequencenumbers *areenabled.
*/ #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,referencedviasession->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 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 *queueitonthesessionsocket.
*/
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 *AddressandControlfieldsandskipthem.TheL2TPmodulehasalways *workedthisway,although,intheory,theuseofthesefieldsshould *benegotiatedandhandledatthePPPlayer.Thesefieldsare *constant:0xFFistheAll-StationsAddressand0x03theUnnumbered *InformationcommandwithPoll/Finalbitsettozero(RFC1662).
*/ 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 *whenauserapplicationdoesasendmsg()onthesessionsocket.L2TPand *PPPheadersmustbeinsertedintotheuser'sdata.
*/ 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;
/* 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 *fragmentingatthelowerIPlayer.AssumesL2TPv2withsequence *numbersandnoIPoption.Notquiteaccurate,buttheresultismostly *unusedanyway.
*/ staticint pppol2tp_tunnel_mtu(conststruct l2tp_tunnel *tunnel)
{ int mtu;
mtu = l2tp_tunnel_dst_mtu(tunnel); if (mtu <= PPPOL2TP_HEADER_OVERHEAD) return1500 - 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_idis0.Otherwiselookuptunnelusingsupplied *tunnelid.
*/ 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 *akernelsocket.
*/ if (info->fd < 0) return ERR_PTR(-EBADF);
/* Using a pre-existing session is fine as long as it hasn't *beenconnectedyet.
*/
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 *socketisbeingcreatedtomanagethetunnel.Justsetup *theinternalcontextforusebyioctl()andsockopt() *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.Thissizeisdifferentdependingonwhether *sequencenumbersareenabledforthedatachannel.
*/
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 *thesessiontodisappear.pppol2tp_session_close()isresponsible *fordroppingit.
*/
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;
if (!stats->session_id) {
pppol2tp_copy_stats(stats, &tunnel->stats); return0;
}
/* If session_id is set, search the corresponding session in the *contextofthistunnelandrecordthesession'sstatistics.
*/
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;
}
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. *DoesAPIchecks,thencallseitherthetunnelorsessionsetsockopt *handler,accordingtowhetherthePPPoL2TPsocketisaforaregular *sessionorthespecialtunneltype.
*/ 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. *DoesAPIchecks,thencallseitherthetunnelorsessiongetsockopt *handler,accordingtowhetherthePPPoXsocketisaforaregularsession *orthespecialtunneltype.
*/ 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;
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() *orpppol2tp_next_tunnel().
*/ if (pd->session) {
l2tp_session_put(pd->session);
pd->session = NULL;
} if (pd->tunnel) {
l2tp_tunnel_put(pd->tunnel);
pd->tunnel = NULL;
}
}
MODULE_AUTHOR("James Chapman <jchapman@katalix.com>");
MODULE_DESCRIPTION("PPP over L2TP over UDP");
MODULE_LICENSE("GPL");
MODULE_VERSION(PPPOL2TP_DRV_VERSION);
MODULE_ALIAS_NET_PF_PROTO(PF_PPPOX, PX_PROTO_OL2TP);
MODULE_ALIAS_L2TP_PWTYPE(7);
Messung V0.5 in Prozent
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.34Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-09-29)
¤
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.