[PATCH 3.16 076/204] IPv4: early demux can return an error code

From: Ben Hutchings
Date: Thu Dec 28 2017 - 12:59:18 EST


3.16.52-rc1 review patch. If anyone has any objections, please let me know.

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

From: Paolo Abeni <pabeni@xxxxxxxxxx>

commit 7487449c86c65202b3b725c4524cb48dd65e4e6f upstream.

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>
[bwh: Backported to 3.16:
- Drop change to net_protocol::early_demux_handler
- Keep using NET_INC_STATS_BH() in ip_rcv_finish()
- Fix up additional return statement in udp_v4_early_demux()
- Adjust context]
Signed-off-by: Ben Hutchings <ben@xxxxxxxxxxxxxxx>
---
--- a/include/net/protocol.h
+++ b/include/net/protocol.h
@@ -39,7 +39,7 @@

/* This is used to register protocols. */
struct net_protocol {
- void (*early_demux)(struct sk_buff *skb);
+ int (*early_demux)(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
@@ -350,7 +350,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
@@ -177,7 +177,7 @@ int udp_lib_get_port(struct sock *sk, un
unsigned int hash2_nulladdr);

/* net/ipv4/udp.c */
-void udp_v4_early_demux(struct sk_buff *skb);
+int udp_v4_early_demux(struct sk_buff *skb);
int udp_get_port(struct sock *sk, unsigned short snum,
int (*saddr_cmp)(const struct sock *,
const struct sock *));
--- a/net/ipv4/ip_input.c
+++ b/net/ipv4/ip_input.c
@@ -313,6 +313,7 @@ static int ip_rcv_finish(struct sk_buff
{
const struct iphdr *iph = ip_hdr(skb);
struct rtable *rt;
+ int err;

if (sysctl_ip_early_demux && !skb_dst(skb) && skb->sk == NULL) {
const struct net_protocol *ipprot;
@@ -320,7 +321,9 @@ static int ip_rcv_finish(struct sk_buff

ipprot = rcu_dereference(inet_protos[protocol]);
if (ipprot && ipprot->early_demux) {
- ipprot->early_demux(skb);
+ err = ipprot->early_demux(skb);
+ if (unlikely(err))
+ goto drop_error;
/* must reload iph, skb->head might have changed */
iph = ip_hdr(skb);
}
@@ -331,14 +334,10 @@ static int ip_rcv_finish(struct sk_buff
* how the packet travels inside Linux networking.
*/
if (!skb_dst(skb)) {
- int err = ip_route_input_noref(skb, iph->daddr, iph->saddr,
- iph->tos, skb->dev);
- if (unlikely(err)) {
- if (err == -EXDEV)
- NET_INC_STATS_BH(dev_net(skb->dev),
- LINUX_MIB_IPRPFILTER);
- goto drop;
- }
+ err = ip_route_input_noref(skb, iph->daddr, iph->saddr,
+ iph->tos, skb->dev);
+ if (unlikely(err))
+ goto drop_error;
}

#ifdef CONFIG_IP_ROUTE_CLASSID
@@ -368,6 +367,11 @@ static int ip_rcv_finish(struct sk_buff
drop:
kfree_skb(skb);
return NET_RX_DROP;
+
+drop_error:
+ if (err == -EXDEV)
+ NET_INC_STATS_BH(dev_net(skb->dev), LINUX_MIB_IPRPFILTER);
+ goto drop;
}

/*
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -1616,23 +1616,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,
@@ -1651,6 +1651,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
@@ -1950,7 +1950,7 @@ static struct sock *__udp4_lib_demux_loo
return result;
}

-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;
@@ -1962,7 +1962,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);
@@ -1972,14 +1972,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,
@@ -1988,11 +1988,11 @@ void udp_v4_early_demux(struct sk_buff *
sk = __udp4_lib_demux_lookup(net, uh->dest, iph->daddr,
uh->source, iph->saddr, dif);
} else {
- return;
+ return 0;
}

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

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

int udp_rcv(struct sk_buff *skb)