diff mbox

[net] udp: prevent skbs lingering in tunnel socket queues

Message ID 1463666313-12743-1-git-send-email-hannes@stressinduktion.org
State Accepted, archived
Delegated to: David Miller
Headers show

Commit Message

Hannes Frederic Sowa May 19, 2016, 1:58 p.m. UTC
In case we find a socket with encapsulation enabled we should call
the encap_recv function even if just a udp header without payload is
available. The callbacks are responsible for correctly verifying and
dropping the packets.

Also, in case the header validation fails for geneve and vxlan we
shouldn't put the skb back into the socket queue, no one will pick
them up there.  Instead we can simply discard them in the respective
encap_recv functions.

Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
---
 drivers/net/geneve.c | 10 +++-------
 drivers/net/vxlan.c  |  4 ++--
 net/ipv4/udp.c       |  2 +-
 net/ipv6/udp.c       |  2 +-
 4 files changed, 7 insertions(+), 11 deletions(-)

Comments

Cong Wang May 19, 2016, 10:33 p.m. UTC | #1
On Thu, May 19, 2016 at 6:58 AM, Hannes Frederic Sowa
<hannes@stressinduktion.org> wrote:
> diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
> index 2e3ebfe5549ef5..d56c0559b477cb 100644
> --- a/net/ipv4/udp.c
> +++ b/net/ipv4/udp.c
> @@ -1565,7 +1565,7 @@ int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
>
>                 /* if we're overly short, let UDP handle it */
>                 encap_rcv = ACCESS_ONCE(up->encap_rcv);
> -               if (skb->len > sizeof(struct udphdr) && encap_rcv) {
> +               if (encap_rcv) {


I don't think you can just remove it here, l2tp_udp_recv_core() still
relies on it:

        /* UDP has verifed checksum */

        /* UDP always verifies the packet length. */
        __skb_pull(skb, sizeof(struct udphdr));
Hannes Frederic Sowa May 19, 2016, 10:50 p.m. UTC | #2
Hi Cong,

On Fri, May 20, 2016, at 00:33, Cong Wang wrote:
> On Thu, May 19, 2016 at 6:58 AM, Hannes Frederic Sowa
> <hannes@stressinduktion.org> wrote:
> > diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
> > index 2e3ebfe5549ef5..d56c0559b477cb 100644
> > --- a/net/ipv4/udp.c
> > +++ b/net/ipv4/udp.c
> > @@ -1565,7 +1565,7 @@ int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
> >
> >                 /* if we're overly short, let UDP handle it */
> >                 encap_rcv = ACCESS_ONCE(up->encap_rcv);
> > -               if (skb->len > sizeof(struct udphdr) && encap_rcv) {
> > +               if (encap_rcv) {
> 
> 
> I don't think you can just remove it here, l2tp_udp_recv_core() still
> relies on it:
> 
>         /* UDP has verifed checksum */
> 
>         /* UDP always verifies the packet length. */
>         __skb_pull(skb, sizeof(struct udphdr));

I think this is fine, we check on every entrance to udp that we may pull
(pskb_may_pull) an udphdr but we really never pull the header. At this
point we are guaranteed to have skb->len of at least sizeof(struct
udphdr).

Bye,
Hannes
Cong Wang May 20, 2016, 1:56 a.m. UTC | #3
On Thu, May 19, 2016 at 3:50 PM, Hannes Frederic Sowa
<hannes@stressinduktion.org> wrote:
> Hi Cong,
>
> On Fri, May 20, 2016, at 00:33, Cong Wang wrote:
>> On Thu, May 19, 2016 at 6:58 AM, Hannes Frederic Sowa
>> <hannes@stressinduktion.org> wrote:
>> > diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
>> > index 2e3ebfe5549ef5..d56c0559b477cb 100644
>> > --- a/net/ipv4/udp.c
>> > +++ b/net/ipv4/udp.c
>> > @@ -1565,7 +1565,7 @@ int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
>> >
>> >                 /* if we're overly short, let UDP handle it */
>> >                 encap_rcv = ACCESS_ONCE(up->encap_rcv);
>> > -               if (skb->len > sizeof(struct udphdr) && encap_rcv) {
>> > +               if (encap_rcv) {
>>
>>
>> I don't think you can just remove it here, l2tp_udp_recv_core() still
>> relies on it:
>>
>>         /* UDP has verifed checksum */
>>
>>         /* UDP always verifies the packet length. */
>>         __skb_pull(skb, sizeof(struct udphdr));
>
> I think this is fine, we check on every entrance to udp that we may pull
> (pskb_may_pull) an udphdr but we really never pull the header. At this
> point we are guaranteed to have skb->len of at least sizeof(struct
> udphdr).

Ah, yeah, __udp4_lib_rcv().

BTW, this part (the second half) of your patch is an optimization,
the rest (the first half) is a real bug fix. It makes sense to split
them if you plan to backport it to -stable.

Thanks.
Hannes Frederic Sowa May 20, 2016, 6:16 a.m. UTC | #4
On Fri, May 20, 2016, at 03:56, Cong Wang wrote:
> On Thu, May 19, 2016 at 3:50 PM, Hannes Frederic Sowa
> <hannes@stressinduktion.org> wrote:
> > Hi Cong,
> >
> > On Fri, May 20, 2016, at 00:33, Cong Wang wrote:
> >> On Thu, May 19, 2016 at 6:58 AM, Hannes Frederic Sowa
> >> <hannes@stressinduktion.org> wrote:
> >> > diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
> >> > index 2e3ebfe5549ef5..d56c0559b477cb 100644
> >> > --- a/net/ipv4/udp.c
> >> > +++ b/net/ipv4/udp.c
> >> > @@ -1565,7 +1565,7 @@ int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
> >> >
> >> >                 /* if we're overly short, let UDP handle it */
> >> >                 encap_rcv = ACCESS_ONCE(up->encap_rcv);
> >> > -               if (skb->len > sizeof(struct udphdr) && encap_rcv) {
> >> > +               if (encap_rcv) {
> >>
> >>
> >> I don't think you can just remove it here, l2tp_udp_recv_core() still
> >> relies on it:
> >>
> >>         /* UDP has verifed checksum */
> >>
> >>         /* UDP always verifies the packet length. */
> >>         __skb_pull(skb, sizeof(struct udphdr));
> >
> > I think this is fine, we check on every entrance to udp that we may pull
> > (pskb_may_pull) an udphdr but we really never pull the header. At this
> > point we are guaranteed to have skb->len of at least sizeof(struct
> > udphdr).
> 
> Ah, yeah, __udp4_lib_rcv().

Yep, or the respective IPv6 part.

> BTW, this part (the second half) of your patch is an optimization,
> the rest (the first half) is a real bug fix. It makes sense to split
> them if you plan to backport it to -stable.

The problem in the second part is that it is a '>' comparison and not a
'>=' check. Thus if you hit the socket with a UDP packet with no payload
after the UDP header it also gets enqueued in the socket queue of vxlan
or geneve.

Bye,
Hannes
David Miller May 20, 2016, 11:56 p.m. UTC | #5
From: Hannes Frederic Sowa <hannes@stressinduktion.org>
Date: Thu, 19 May 2016 15:58:33 +0200

> In case we find a socket with encapsulation enabled we should call
> the encap_recv function even if just a udp header without payload is
> available. The callbacks are responsible for correctly verifying and
> dropping the packets.
> 
> Also, in case the header validation fails for geneve and vxlan we
> shouldn't put the skb back into the socket queue, no one will pick
> them up there.  Instead we can simply discard them in the respective
> encap_recv functions.
> 
> Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org>

Looks good, applied and queued up for -stable.

Thanks!
diff mbox

Patch

diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
index a6dc11ce497f5c..cadefe4fdaa2aa 100644
--- a/drivers/net/geneve.c
+++ b/drivers/net/geneve.c
@@ -335,15 +335,15 @@  static int geneve_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
 
 	/* Need Geneve and inner Ethernet header to be present */
 	if (unlikely(!pskb_may_pull(skb, GENEVE_BASE_HLEN)))
-		goto error;
+		goto drop;
 
 	/* Return packets with reserved bits set */
 	geneveh = geneve_hdr(skb);
 	if (unlikely(geneveh->ver != GENEVE_VER))
-		goto error;
+		goto drop;
 
 	if (unlikely(geneveh->proto_type != htons(ETH_P_TEB)))
-		goto error;
+		goto drop;
 
 	gs = rcu_dereference_sk_user_data(sk);
 	if (!gs)
@@ -366,10 +366,6 @@  drop:
 	/* Consume bad packet */
 	kfree_skb(skb);
 	return 0;
-
-error:
-	/* Let the UDP layer deal with the skb */
-	return 1;
 }
 
 static struct socket *geneve_create_sock(struct net *net, bool ipv6,
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index 25ab6bf013c4d2..8ff30c3bdfceab 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -1304,7 +1304,7 @@  static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
 
 	/* Need UDP and VXLAN header to be present */
 	if (!pskb_may_pull(skb, VXLAN_HLEN))
-		return 1;
+		goto drop;
 
 	unparsed = *vxlan_hdr(skb);
 	/* VNI flag always required to be set */
@@ -1313,7 +1313,7 @@  static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
 			   ntohl(vxlan_hdr(skb)->vx_flags),
 			   ntohl(vxlan_hdr(skb)->vx_vni));
 		/* Return non vxlan pkt */
-		return 1;
+		goto drop;
 	}
 	unparsed.vx_flags &= ~VXLAN_HF_VNI;
 	unparsed.vx_vni &= ~VXLAN_VNI_MASK;
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 2e3ebfe5549ef5..d56c0559b477cb 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -1565,7 +1565,7 @@  int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
 
 		/* if we're overly short, let UDP handle it */
 		encap_rcv = ACCESS_ONCE(up->encap_rcv);
-		if (skb->len > sizeof(struct udphdr) && encap_rcv) {
+		if (encap_rcv) {
 			int ret;
 
 			/* Verify checksum before giving to encap */
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 2ba6a77a881532..2da1896af93496 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -617,7 +617,7 @@  int udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
 
 		/* if we're overly short, let UDP handle it */
 		encap_rcv = ACCESS_ONCE(up->encap_rcv);
-		if (skb->len > sizeof(struct udphdr) && encap_rcv) {
+		if (encap_rcv) {
 			int ret;
 
 			/* Verify checksum before giving to encap */