Skip to content

Commit

Permalink
Merge branch 'l2tp-miscellaneous-cleanups'
Browse files Browse the repository at this point in the history
Tom Parkin says:

====================
l2tp: miscellaneous cleanups

This series of patches makes the following cleanups and improvements to
the l2tp code:

 * various API tweaks to remove unused parameters from function calls
 * lightly refactor the l2tp transmission path to capture more error
   conditions in the data plane statistics
 * repurpose the "magic feather" validation in l2tp to check for
   sk_user_data (ab)use as opposed to refcount debugging
 * remove some duplicated code
====================

Reviewed-by: James Chapman <jchapman@katalix.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Sep 3, 2020
2 parents ffd923f + 9d319a8 commit 08aaa08
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 80 deletions.
134 changes: 64 additions & 70 deletions net/l2tp/l2tp_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,6 @@ static bool l2tp_sk_is_v6(struct sock *sk)
}
#endif

static inline struct l2tp_tunnel *l2tp_tunnel(struct sock *sk)
{
return sk->sk_user_data;
}

static inline struct l2tp_net *l2tp_pernet(const struct net *net)
{
return net_generic(net, l2tp_net_id);
Expand Down Expand Up @@ -162,19 +157,23 @@ static void l2tp_tunnel_free(struct l2tp_tunnel *tunnel)

static void l2tp_session_free(struct l2tp_session *session)
{
struct l2tp_tunnel *tunnel = session->tunnel;

trace_free_session(session);
if (session->tunnel)
l2tp_tunnel_dec_refcount(session->tunnel);
kfree(session);
}

if (tunnel) {
struct l2tp_tunnel *l2tp_sk_to_tunnel(struct sock *sk)
{
struct l2tp_tunnel *tunnel = sk->sk_user_data;

if (tunnel)
if (WARN_ON(tunnel->magic != L2TP_TUNNEL_MAGIC))
goto out;
l2tp_tunnel_dec_refcount(tunnel);
}
return NULL;

out:
kfree(session);
return tunnel;
}
EXPORT_SYMBOL_GPL(l2tp_sk_to_tunnel);

void l2tp_tunnel_inc_refcount(struct l2tp_tunnel *tunnel)
{
Expand Down Expand Up @@ -782,9 +781,6 @@ static void l2tp_session_queue_purge(struct l2tp_session *session)
{
struct sk_buff *skb = NULL;

if (WARN_ON(session->magic != L2TP_SESSION_MAGIC))
return;

while ((skb = skb_dequeue(&session->reorder_q))) {
atomic_long_inc(&session->stats.rx_errors);
kfree_skb(skb);
Expand Down Expand Up @@ -898,9 +894,17 @@ int l2tp_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
{
struct l2tp_tunnel *tunnel;

/* Note that this is called from the encap_rcv hook inside an
* RCU-protected region, but without the socket being locked.
* Hence we use rcu_dereference_sk_user_data to access the
* tunnel data structure rather the usual l2tp_sk_to_tunnel
* accessor function.
*/
tunnel = rcu_dereference_sk_user_data(sk);
if (!tunnel)
goto pass_up;
if (WARN_ON(tunnel->magic != L2TP_TUNNEL_MAGIC))
goto pass_up;

if (l2tp_udp_recv_core(tunnel, skb))
goto pass_up;
Expand Down Expand Up @@ -985,72 +989,53 @@ static int l2tp_build_l2tpv3_header(struct l2tp_session *session, void *buf)
return bufp - optr;
}

static void l2tp_xmit_core(struct l2tp_session *session, struct sk_buff *skb,
struct flowi *fl, size_t data_len)
/* Queue the packet to IP for output: tunnel socket lock must be held */
static int l2tp_xmit_queue(struct l2tp_tunnel *tunnel, struct sk_buff *skb, struct flowi *fl)
{
struct l2tp_tunnel *tunnel = session->tunnel;
unsigned int len = skb->len;
int error;
int err;

/* Queue the packet to IP for output */
skb->ignore_df = 1;
skb_dst_drop(skb);
#if IS_ENABLED(CONFIG_IPV6)
if (l2tp_sk_is_v6(tunnel->sock))
error = inet6_csk_xmit(tunnel->sock, skb, NULL);
err = inet6_csk_xmit(tunnel->sock, skb, NULL);
else
#endif
error = ip_queue_xmit(tunnel->sock, skb, fl);
err = ip_queue_xmit(tunnel->sock, skb, fl);

/* Update stats */
if (error >= 0) {
atomic_long_inc(&tunnel->stats.tx_packets);
atomic_long_add(len, &tunnel->stats.tx_bytes);
atomic_long_inc(&session->stats.tx_packets);
atomic_long_add(len, &session->stats.tx_bytes);
} else {
atomic_long_inc(&tunnel->stats.tx_errors);
atomic_long_inc(&session->stats.tx_errors);
}
return err >= 0 ? NET_XMIT_SUCCESS : NET_XMIT_DROP;
}

/* If caller requires the skb to have a ppp header, the header must be
* inserted in the skb data before calling this function.
*/
int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len)
static int l2tp_xmit_core(struct l2tp_session *session, struct sk_buff *skb)
{
int data_len = skb->len;
struct l2tp_tunnel *tunnel = session->tunnel;
unsigned int data_len = skb->len;
struct sock *sk = tunnel->sock;
struct flowi *fl;
struct udphdr *uh;
struct inet_sock *inet;
int headroom;
int uhlen = (tunnel->encap == L2TP_ENCAPTYPE_UDP) ? sizeof(struct udphdr) : 0;
int udp_len;
int headroom, uhlen, udp_len;
int ret = NET_XMIT_SUCCESS;
struct inet_sock *inet;
struct udphdr *uh;

/* Check that there's enough headroom in the skb to insert IP,
* UDP and L2TP headers. If not enough, expand it to
* make room. Adjust truesize.
*/
headroom = NET_SKB_PAD + sizeof(struct iphdr) +
uhlen + hdr_len;
uhlen = (tunnel->encap == L2TP_ENCAPTYPE_UDP) ? sizeof(*uh) : 0;
headroom = NET_SKB_PAD + sizeof(struct iphdr) + uhlen + session->hdr_len;
if (skb_cow_head(skb, headroom)) {
kfree_skb(skb);
return NET_XMIT_DROP;
}

/* Setup L2TP header */
if (tunnel->version == L2TP_HDR_VER_2)
l2tp_build_l2tpv2_header(session, __skb_push(skb, hdr_len));
l2tp_build_l2tpv2_header(session, __skb_push(skb, session->hdr_len));
else
l2tp_build_l2tpv3_header(session, __skb_push(skb, hdr_len));
l2tp_build_l2tpv3_header(session, __skb_push(skb, session->hdr_len));

/* Reset skb netfilter state */
memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED |
IPSKB_REROUTED);
IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED | IPSKB_REROUTED);
nf_reset_ct(skb);

bh_lock_sock(sk);
Expand All @@ -1070,7 +1055,6 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len
}

inet = inet_sk(sk);
fl = &inet->cork.fl;
switch (tunnel->encap) {
case L2TP_ENCAPTYPE_UDP:
/* Setup UDP header */
Expand All @@ -1079,7 +1063,7 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len
uh = udp_hdr(skb);
uh->source = inet->inet_sport;
uh->dest = inet->inet_dport;
udp_len = uhlen + hdr_len + data_len;
udp_len = uhlen + session->hdr_len + data_len;
uh->len = htons(udp_len);

/* Calculate UDP checksum if configured to do so */
Expand All @@ -1098,12 +1082,34 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len
break;
}

l2tp_xmit_core(session, skb, fl, data_len);
ret = l2tp_xmit_queue(tunnel, skb, &inet->cork.fl);

out_unlock:
bh_unlock_sock(sk);

return ret;
}

/* If caller requires the skb to have a ppp header, the header must be
* inserted in the skb data before calling this function.
*/
int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb)
{
unsigned int len = skb->len;
int ret;

ret = l2tp_xmit_core(session, skb);
if (ret == NET_XMIT_SUCCESS) {
atomic_long_inc(&session->tunnel->stats.tx_packets);
atomic_long_add(len, &session->tunnel->stats.tx_bytes);
atomic_long_inc(&session->stats.tx_packets);
atomic_long_add(len, &session->stats.tx_bytes);
} else {
atomic_long_inc(&session->tunnel->stats.tx_errors);
atomic_long_inc(&session->stats.tx_errors);
}
return ret;
}
EXPORT_SYMBOL_GPL(l2tp_xmit_skb);

/*****************************************************************************
Expand All @@ -1116,7 +1122,7 @@ EXPORT_SYMBOL_GPL(l2tp_xmit_skb);
*/
static void l2tp_tunnel_destruct(struct sock *sk)
{
struct l2tp_tunnel *tunnel = l2tp_tunnel(sk);
struct l2tp_tunnel *tunnel = l2tp_sk_to_tunnel(sk);

if (!tunnel)
goto end;
Expand Down Expand Up @@ -1185,22 +1191,10 @@ static void l2tp_tunnel_closeall(struct l2tp_tunnel *tunnel)
again:
hlist_for_each_safe(walk, tmp, &tunnel->session_hlist[hash]) {
session = hlist_entry(walk, struct l2tp_session, hlist);

hlist_del_init(&session->hlist);

if (test_and_set_bit(0, &session->dead))
goto again;

write_unlock_bh(&tunnel->hlist_lock);

l2tp_session_unhash(session);
l2tp_session_queue_purge(session);

if (session->session_close)
(*session->session_close)(session);

l2tp_session_dec_refcount(session);

l2tp_session_delete(session);
write_lock_bh(&tunnel->hlist_lock);

/* Now restart from the beginning of this hash
Expand All @@ -1217,7 +1211,7 @@ static void l2tp_tunnel_closeall(struct l2tp_tunnel *tunnel)
/* Tunnel socket destroy hook for UDP encapsulation */
static void l2tp_udp_encap_destroy(struct sock *sk)
{
struct l2tp_tunnel *tunnel = l2tp_tunnel(sk);
struct l2tp_tunnel *tunnel = l2tp_sk_to_tunnel(sk);

if (tunnel)
l2tp_tunnel_delete(tunnel);
Expand Down Expand Up @@ -1382,7 +1376,7 @@ static int l2tp_tunnel_sock_create(struct net *net,

static struct lock_class_key l2tp_socket_class;

int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32 peer_tunnel_id,
int l2tp_tunnel_create(int fd, int version, u32 tunnel_id, u32 peer_tunnel_id,
struct l2tp_tunnel_cfg *cfg, struct l2tp_tunnel **tunnelp)
{
struct l2tp_tunnel *tunnel = NULL;
Expand Down
10 changes: 7 additions & 3 deletions net/l2tp/l2tp_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ struct l2tp_session *l2tp_session_get_by_ifname(const struct net *net,
* Creation of a new instance is a two-step process: create, then register.
* Destruction is triggered using the *_delete functions, and completes asynchronously.
*/
int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id,
int l2tp_tunnel_create(int fd, int version, u32 tunnel_id,
u32 peer_tunnel_id, struct l2tp_tunnel_cfg *cfg,
struct l2tp_tunnel **tunnelp);
int l2tp_tunnel_register(struct l2tp_tunnel *tunnel, struct net *net,
Expand All @@ -261,8 +261,7 @@ int l2tp_udp_encap_recv(struct sock *sk, struct sk_buff *skb);

/* Transmit path helpers for sending packets over the tunnel socket. */
void l2tp_session_set_header_len(struct l2tp_session *session, int version);
int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb,
int hdr_len);
int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb);

/* Pseudowire management.
* Pseudowires should register with l2tp core on module init, and unregister
Expand All @@ -274,6 +273,11 @@ void l2tp_nl_unregister_ops(enum l2tp_pwtype pw_type);
/* IOCTL helper for IP encap modules. */
int l2tp_ioctl(struct sock *sk, int cmd, unsigned long arg);

/* Extract the tunnel structure from a socket's sk_user_data pointer,
* validating the tunnel magic feather.
*/
struct l2tp_tunnel *l2tp_sk_to_tunnel(struct sock *sk);

static inline int l2tp_get_l2specific_len(struct l2tp_session *session)
{
switch (session->l2specific_type) {
Expand Down
2 changes: 1 addition & 1 deletion net/l2tp/l2tp_eth.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ static netdev_tx_t l2tp_eth_dev_xmit(struct sk_buff *skb, struct net_device *dev
struct l2tp_eth *priv = netdev_priv(dev);
struct l2tp_session *session = priv->session;
unsigned int len = skb->len;
int ret = l2tp_xmit_skb(session, skb, session->hdr_len);
int ret = l2tp_xmit_skb(session, skb);

if (likely(ret == NET_XMIT_SUCCESS)) {
atomic_long_add(len, &priv->tx_bytes);
Expand Down
2 changes: 1 addition & 1 deletion net/l2tp/l2tp_ip.c
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,8 @@ static void l2tp_ip_close(struct sock *sk, long timeout)

static void l2tp_ip_destroy_sock(struct sock *sk)
{
struct l2tp_tunnel *tunnel = l2tp_sk_to_tunnel(sk);
struct sk_buff *skb;
struct l2tp_tunnel *tunnel = sk->sk_user_data;

while ((skb = __skb_dequeue_tail(&sk->sk_write_queue)) != NULL)
kfree_skb(skb);
Expand Down
2 changes: 1 addition & 1 deletion net/l2tp/l2tp_ip6.c
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ static void l2tp_ip6_close(struct sock *sk, long timeout)

static void l2tp_ip6_destroy_sock(struct sock *sk)
{
struct l2tp_tunnel *tunnel = sk->sk_user_data;
struct l2tp_tunnel *tunnel = l2tp_sk_to_tunnel(sk);

lock_sock(sk);
ip6_flush_pending_frames(sk);
Expand Down
2 changes: 1 addition & 1 deletion net/l2tp/l2tp_netlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ static int l2tp_nl_cmd_tunnel_create(struct sk_buff *skb, struct genl_info *info
switch (cfg.encap) {
case L2TP_ENCAPTYPE_UDP:
case L2TP_ENCAPTYPE_IP:
ret = l2tp_tunnel_create(net, fd, proto_version, tunnel_id,
ret = l2tp_tunnel_create(fd, proto_version, tunnel_id,
peer_tunnel_id, &cfg, &tunnel);
break;
}
Expand Down
15 changes: 12 additions & 3 deletions net/l2tp/l2tp_ppp.c
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ static int pppol2tp_sendmsg(struct socket *sock, struct msghdr *m,
}

local_bh_disable();
l2tp_xmit_skb(session, skb, session->hdr_len);
l2tp_xmit_skb(session, skb);
local_bh_enable();

sock_put(sk);
Expand Down Expand Up @@ -375,7 +375,7 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
skb->data[1] = PPP_UI;

local_bh_disable();
l2tp_xmit_skb(session, skb, session->hdr_len);
l2tp_xmit_skb(session, skb);
local_bh_enable();

sock_put(sk);
Expand Down Expand Up @@ -712,7 +712,7 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr,
goto end;
}

error = l2tp_tunnel_create(sock_net(sk), info.fd,
error = l2tp_tunnel_create(info.fd,
info.version,
info.tunnel_id,
info.peer_tunnel_id, &tcfg,
Expand Down Expand Up @@ -1065,6 +1065,9 @@ static int pppol2tp_ioctl(struct socket *sock, unsigned int cmd,
if (!session)
return -ENOTCONN;

if (WARN_ON(session->magic != L2TP_SESSION_MAGIC))
return -EBADF;

/* Not defined for tunnels */
if (!session->session_id && !session->peer_session_id)
return -ENOSYS;
Expand All @@ -1079,6 +1082,9 @@ static int pppol2tp_ioctl(struct socket *sock, unsigned int cmd,
if (!session)
return -ENOTCONN;

if (WARN_ON(session->magic != L2TP_SESSION_MAGIC))
return -EBADF;

/* Not defined for tunnels */
if (!session->session_id && !session->peer_session_id)
return -ENOSYS;
Expand All @@ -1092,6 +1098,9 @@ static int pppol2tp_ioctl(struct socket *sock, unsigned int cmd,
if (!session)
return -ENOTCONN;

if (WARN_ON(session->magic != L2TP_SESSION_MAGIC))
return -EBADF;

/* Session 0 represents the parent tunnel */
if (!session->session_id && !session->peer_session_id) {
u32 session_id;
Expand Down

0 comments on commit 08aaa08

Please sign in to comment.