l2tp: capture more tx errors in data plane stats

l2tp_xmit_skb has a number of failure paths which are not reflected in
the tunnel and session statistics because the stats are updated by
l2tp_xmit_core.  Hence any errors occurring before l2tp_xmit_core is
called are missed from the statistics.

Refactor the transmit path slightly to capture all error paths.

l2tp_xmit_skb now leaves all the actual work of transmission to
l2tp_xmit_core, and updates the statistics based on l2tp_xmit_core's
return code.

Signed-off-by: Tom Parkin <tparkin@katalix.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Tom Parkin 2020-09-03 09:54:50 +01:00 committed by David S. Miller
parent c9ccd4c63c
commit de68b039e9

View file

@ -985,56 +985,39 @@ 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)
/* 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)
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 + session->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;
@ -1048,8 +1031,7 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb)
/* 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);
@ -1069,7 +1051,6 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb)
}
inet = inet_sk(sk);
fl = &inet->cork.fl;
switch (tunnel->encap) {
case L2TP_ENCAPTYPE_UDP:
/* Setup UDP header */
@ -1097,12 +1078,34 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb)
break;
}
l2tp_xmit_core(session, skb, fl);
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);
/*****************************************************************************