[PATCH 4.13 044/160] IPv4: early demux can return an error code

From: Greg Kroah-Hartman
Date: Tue Oct 10 2017 - 16:26:15 EST


4.13-stable review patch. If anyone has any objections, please let me know.

------------------

From: Paolo Abeni <pabeni@xxxxxxxxxx>


[ Upstream commit 7487449c86c65202b3b725c4524cb48dd65e4e6f ]

Currently no error is emitted, but this infrastructure will
used by the next patch to allow source address validation
for mcast sockets.
Since early demux can do a route lookup and an ipv4 route
lookup can return an error code this is consistent with the
current ipv4 route infrastructure.

Signed-off-by: Paolo Abeni <pabeni@xxxxxxxxxx>
Signed-off-by: David S. Miller <davem@xxxxxxxxxxxxx>
Signed-off-by: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx>
---
include/net/protocol.h | 4 ++--
include/net/tcp.h | 2 +-
include/net/udp.h | 2 +-
net/ipv4/ip_input.c | 25 +++++++++++++++----------
net/ipv4/tcp_ipv4.c | 9 +++++----
net/ipv4/udp.c | 11 ++++++-----
6 files changed, 30 insertions(+), 23 deletions(-)

--- a/include/net/protocol.h
+++ b/include/net/protocol.h
@@ -39,8 +39,8 @@

/* This is used to register protocols. */
struct net_protocol {
- void (*early_demux)(struct sk_buff *skb);
- void (*early_demux_handler)(struct sk_buff *skb);
+ int (*early_demux)(struct sk_buff *skb);
+ int (*early_demux_handler)(struct sk_buff *skb);
int (*handler)(struct sk_buff *skb);
void (*err_handler)(struct sk_buff *skb, u32 info);
unsigned int no_policy:1,
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -347,7 +347,7 @@ void tcp_v4_err(struct sk_buff *skb, u32

void tcp_shutdown(struct sock *sk, int how);

-void tcp_v4_early_demux(struct sk_buff *skb);
+int tcp_v4_early_demux(struct sk_buff *skb);
int tcp_v4_rcv(struct sk_buff *skb);

int tcp_v4_tw_remember_stamp(struct inet_timewait_sock *tw);
--- a/include/net/udp.h
+++ b/include/net/udp.h
@@ -259,7 +259,7 @@ static inline struct sk_buff *skb_recv_u
return __skb_recv_udp(sk, flags, noblock, &peeked, &off, err);
}

-void udp_v4_early_demux(struct sk_buff *skb);
+int udp_v4_early_demux(struct sk_buff *skb);
bool udp_sk_rx_dst_set(struct sock *sk, struct dst_entry *dst);
int udp_get_port(struct sock *sk, unsigned short snum,
int (*saddr_cmp)(const struct sock *,
--- a/net/ipv4/ip_input.c
+++ b/net/ipv4/ip_input.c
@@ -311,9 +311,10 @@ drop:
static int ip_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
{
const struct iphdr *iph = ip_hdr(skb);
- struct rtable *rt;
+ int (*edemux)(struct sk_buff *skb);
struct net_device *dev = skb->dev;
- void (*edemux)(struct sk_buff *skb);
+ struct rtable *rt;
+ int err;

/* if ingress device is enslaved to an L3 master device pass the
* skb to its handler for processing
@@ -331,7 +332,9 @@ static int ip_rcv_finish(struct net *net

ipprot = rcu_dereference(inet_protos[protocol]);
if (ipprot && (edemux = READ_ONCE(ipprot->early_demux))) {
- edemux(skb);
+ err = edemux(skb);
+ if (unlikely(err))
+ goto drop_error;
/* must reload iph, skb->head might have changed */
iph = ip_hdr(skb);
}
@@ -342,13 +345,10 @@ static int ip_rcv_finish(struct net *net
* how the packet travels inside Linux networking.
*/
if (!skb_valid_dst(skb)) {
- int err = ip_route_input_noref(skb, iph->daddr, iph->saddr,
- iph->tos, dev);
- if (unlikely(err)) {
- if (err == -EXDEV)
- __NET_INC_STATS(net, LINUX_MIB_IPRPFILTER);
- goto drop;
- }
+ err = ip_route_input_noref(skb, iph->daddr, iph->saddr,
+ iph->tos, dev);
+ if (unlikely(err))
+ goto drop_error;
}

#ifdef CONFIG_IP_ROUTE_CLASSID
@@ -399,6 +399,11 @@ static int ip_rcv_finish(struct net *net
drop:
kfree_skb(skb);
return NET_RX_DROP;
+
+drop_error:
+ if (err == -EXDEV)
+ __NET_INC_STATS(net, LINUX_MIB_IPRPFILTER);
+ goto drop;
}

/*
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -1504,23 +1504,23 @@ csum_err:
}
EXPORT_SYMBOL(tcp_v4_do_rcv);

-void tcp_v4_early_demux(struct sk_buff *skb)
+int tcp_v4_early_demux(struct sk_buff *skb)
{
const struct iphdr *iph;
const struct tcphdr *th;
struct sock *sk;

if (skb->pkt_type != PACKET_HOST)
- return;
+ return 0;

if (!pskb_may_pull(skb, skb_transport_offset(skb) + sizeof(struct tcphdr)))
- return;
+ return 0;

iph = ip_hdr(skb);
th = tcp_hdr(skb);

if (th->doff < sizeof(struct tcphdr) / 4)
- return;
+ return 0;

sk = __inet_lookup_established(dev_net(skb->dev), &tcp_hashinfo,
iph->saddr, th->source,
@@ -1539,6 +1539,7 @@ void tcp_v4_early_demux(struct sk_buff *
skb_dst_set_noref(skb, dst);
}
}
+ return 0;
}

/* Packet is added to VJ-style prequeue for processing in process
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -2217,7 +2217,7 @@ static struct sock *__udp4_lib_demux_loo
return NULL;
}

-void udp_v4_early_demux(struct sk_buff *skb)
+int udp_v4_early_demux(struct sk_buff *skb)
{
struct net *net = dev_net(skb->dev);
const struct iphdr *iph;
@@ -2229,7 +2229,7 @@ void udp_v4_early_demux(struct sk_buff *

/* validate the packet */
if (!pskb_may_pull(skb, skb_transport_offset(skb) + sizeof(struct udphdr)))
- return;
+ return 0;

iph = ip_hdr(skb);
uh = udp_hdr(skb);
@@ -2239,14 +2239,14 @@ void udp_v4_early_demux(struct sk_buff *
struct in_device *in_dev = __in_dev_get_rcu(skb->dev);

if (!in_dev)
- return;
+ return 0;

/* we are supposed to accept bcast packets */
if (skb->pkt_type == PACKET_MULTICAST) {
ours = ip_check_mc_rcu(in_dev, iph->daddr, iph->saddr,
iph->protocol);
if (!ours)
- return;
+ return 0;
}

sk = __udp4_lib_mcast_demux_lookup(net, uh->dest, iph->daddr,
@@ -2257,7 +2257,7 @@ void udp_v4_early_demux(struct sk_buff *
}

if (!sk || !refcount_inc_not_zero(&sk->sk_refcnt))
- return;
+ return 0;

skb->sk = sk;
skb->destructor = sock_efree;
@@ -2272,6 +2272,7 @@ void udp_v4_early_demux(struct sk_buff *
*/
skb_dst_set_noref(skb, dst);
}
+ return 0;
}

int udp_rcv(struct sk_buff *skb)