From b60998c141fa0bda7d1aa940f6e9b49e03a6e8ea Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Thu, 28 Sep 2017 15:51:36 +0200 Subject: [PATCH] IPv4: early demux can return an error code 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 Signed-off-by: David S. Miller [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 --- include/net/protocol.h | 2 +- include/net/tcp.h | 2 +- include/net/udp.h | 2 +- net/ipv4/ip_input.c | 22 +++++++++++++--------- net/ipv4/tcp_ipv4.c | 9 +++++---- net/ipv4/udp.c | 13 +++++++------ 6 files changed, 28 insertions(+), 22 deletions(-) diff --git a/include/net/protocol.h b/include/net/protocol.h index 047c0476c0a0..ad34826b3e22 100644 --- 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, diff --git a/include/net/tcp.h b/include/net/tcp.h index d5fb063a4bf4..cdef98f089fc 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -371,7 +371,7 @@ extern void tcp_v4_err(struct sk_buff *skb, u32); extern void tcp_shutdown (struct sock *sk, int how); -extern void tcp_v4_early_demux(struct sk_buff *skb); +extern int tcp_v4_early_demux(struct sk_buff *skb); extern int tcp_v4_rcv(struct sk_buff *skb); extern int tcp_v4_tw_remember_stamp(struct inet_timewait_sock *tw); diff --git a/include/net/udp.h b/include/net/udp.h index 2c4e8ecda56a..e30a9d7d51f8 100644 --- a/include/net/udp.h +++ b/include/net/udp.h @@ -175,7 +175,7 @@ extern int udp_lib_get_port(struct sock *sk, unsigned short snum, unsigned int hash2_nulladdr); /* net/ipv4/udp.c */ -extern void udp_v4_early_demux(struct sk_buff *skb); +extern int udp_v4_early_demux(struct sk_buff *skb); extern int udp_get_port(struct sock *sk, unsigned short snum, int (*saddr_cmp)(const struct sock *, const struct sock *)); diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index 0a22bb0ce1a8..43f70dcce9b2 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -312,6 +312,7 @@ static int ip_rcv_finish(struct sk_buff *skb) { 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; @@ -319,7 +320,9 @@ static int ip_rcv_finish(struct sk_buff *skb) 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); } @@ -330,14 +333,10 @@ static int ip_rcv_finish(struct sk_buff *skb) * 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 @@ -367,6 +366,11 @@ static int ip_rcv_finish(struct sk_buff *skb) 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; } /* diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index f52a63eb94c3..e880facf022b 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1889,23 +1889,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, @@ -1924,6 +1924,7 @@ void tcp_v4_early_demux(struct sk_buff *skb) skb_dst_set_noref(skb, dst); } } + return 0; } /* Packet is added to VJ-style prequeue for processing in process diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index f6877b09edfd..5055b240e241 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1912,7 +1912,7 @@ static struct sock *__udp4_lib_demux_lookup(struct net *net, 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; @@ -1924,7 +1924,7 @@ void udp_v4_early_demux(struct sk_buff *skb) /* 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); @@ -1934,14 +1934,14 @@ void udp_v4_early_demux(struct sk_buff *skb) 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, @@ -1950,11 +1950,11 @@ void udp_v4_early_demux(struct sk_buff *skb) 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; @@ -1971,6 +1971,7 @@ void udp_v4_early_demux(struct sk_buff *skb) skb_dst_set_noref(skb, dst); } } + return 0; } int udp_rcv(struct sk_buff *skb)