1
0
Fork 0

IPv4: early demux can return an error code

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@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
zero-colors
Paolo Abeni 2017-09-28 15:51:36 +02:00 committed by David S. Miller
parent d41bb33ba3
commit 7487449c86
6 changed files with 30 additions and 23 deletions

View File

@ -39,8 +39,8 @@
/* This is used to register protocols. */ /* This is used to register protocols. */
struct net_protocol { struct net_protocol {
void (*early_demux)(struct sk_buff *skb); int (*early_demux)(struct sk_buff *skb);
void (*early_demux_handler)(struct sk_buff *skb); int (*early_demux_handler)(struct sk_buff *skb);
int (*handler)(struct sk_buff *skb); int (*handler)(struct sk_buff *skb);
void (*err_handler)(struct sk_buff *skb, u32 info); void (*err_handler)(struct sk_buff *skb, u32 info);
unsigned int no_policy:1, unsigned int no_policy:1,

View File

@ -345,7 +345,7 @@ void tcp_v4_err(struct sk_buff *skb, u32);
void tcp_shutdown(struct sock *sk, int how); 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_rcv(struct sk_buff *skb);
int tcp_v4_tw_remember_stamp(struct inet_timewait_sock *tw); int tcp_v4_tw_remember_stamp(struct inet_timewait_sock *tw);

View File

@ -259,7 +259,7 @@ static inline struct sk_buff *skb_recv_udp(struct sock *sk, unsigned int flags,
return __skb_recv_udp(sk, flags, noblock, &peeked, &off, err); 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); bool udp_sk_rx_dst_set(struct sock *sk, struct dst_entry *dst);
int udp_get_port(struct sock *sk, unsigned short snum, int udp_get_port(struct sock *sk, unsigned short snum,
int (*saddr_cmp)(const struct sock *, int (*saddr_cmp)(const struct sock *,

View File

@ -311,9 +311,10 @@ drop:
static int ip_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb) static int ip_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
{ {
const struct iphdr *iph = ip_hdr(skb); const struct iphdr *iph = ip_hdr(skb);
struct rtable *rt; int (*edemux)(struct sk_buff *skb);
struct net_device *dev = skb->dev; 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 /* if ingress device is enslaved to an L3 master device pass the
* skb to its handler for processing * skb to its handler for processing
@ -331,7 +332,9 @@ static int ip_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
ipprot = rcu_dereference(inet_protos[protocol]); ipprot = rcu_dereference(inet_protos[protocol]);
if (ipprot && (edemux = READ_ONCE(ipprot->early_demux))) { 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 */ /* must reload iph, skb->head might have changed */
iph = ip_hdr(skb); iph = ip_hdr(skb);
} }
@ -342,13 +345,10 @@ static int ip_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
* how the packet travels inside Linux networking. * how the packet travels inside Linux networking.
*/ */
if (!skb_valid_dst(skb)) { if (!skb_valid_dst(skb)) {
int err = ip_route_input_noref(skb, iph->daddr, iph->saddr, err = ip_route_input_noref(skb, iph->daddr, iph->saddr,
iph->tos, dev); iph->tos, dev);
if (unlikely(err)) { if (unlikely(err))
if (err == -EXDEV) goto drop_error;
__NET_INC_STATS(net, LINUX_MIB_IPRPFILTER);
goto drop;
}
} }
#ifdef CONFIG_IP_ROUTE_CLASSID #ifdef CONFIG_IP_ROUTE_CLASSID
@ -399,6 +399,11 @@ static int ip_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
drop: drop:
kfree_skb(skb); kfree_skb(skb);
return NET_RX_DROP; return NET_RX_DROP;
drop_error:
if (err == -EXDEV)
__NET_INC_STATS(net, LINUX_MIB_IPRPFILTER);
goto drop;
} }
/* /*

View File

@ -1503,23 +1503,23 @@ csum_err:
} }
EXPORT_SYMBOL(tcp_v4_do_rcv); 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 iphdr *iph;
const struct tcphdr *th; const struct tcphdr *th;
struct sock *sk; struct sock *sk;
if (skb->pkt_type != PACKET_HOST) if (skb->pkt_type != PACKET_HOST)
return; return 0;
if (!pskb_may_pull(skb, skb_transport_offset(skb) + sizeof(struct tcphdr))) if (!pskb_may_pull(skb, skb_transport_offset(skb) + sizeof(struct tcphdr)))
return; return 0;
iph = ip_hdr(skb); iph = ip_hdr(skb);
th = tcp_hdr(skb); th = tcp_hdr(skb);
if (th->doff < sizeof(struct tcphdr) / 4) if (th->doff < sizeof(struct tcphdr) / 4)
return; return 0;
sk = __inet_lookup_established(dev_net(skb->dev), &tcp_hashinfo, sk = __inet_lookup_established(dev_net(skb->dev), &tcp_hashinfo,
iph->saddr, th->source, iph->saddr, th->source,
@ -1538,6 +1538,7 @@ void tcp_v4_early_demux(struct sk_buff *skb)
skb_dst_set_noref(skb, dst); skb_dst_set_noref(skb, dst);
} }
} }
return 0;
} }
bool tcp_add_backlog(struct sock *sk, struct sk_buff *skb) bool tcp_add_backlog(struct sock *sk, struct sk_buff *skb)

View File

@ -2221,7 +2221,7 @@ static struct sock *__udp4_lib_demux_lookup(struct net *net,
return NULL; 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); struct net *net = dev_net(skb->dev);
const struct iphdr *iph; const struct iphdr *iph;
@ -2234,7 +2234,7 @@ void udp_v4_early_demux(struct sk_buff *skb)
/* validate the packet */ /* validate the packet */
if (!pskb_may_pull(skb, skb_transport_offset(skb) + sizeof(struct udphdr))) if (!pskb_may_pull(skb, skb_transport_offset(skb) + sizeof(struct udphdr)))
return; return 0;
iph = ip_hdr(skb); iph = ip_hdr(skb);
uh = udp_hdr(skb); uh = udp_hdr(skb);
@ -2244,14 +2244,14 @@ void udp_v4_early_demux(struct sk_buff *skb)
struct in_device *in_dev = __in_dev_get_rcu(skb->dev); struct in_device *in_dev = __in_dev_get_rcu(skb->dev);
if (!in_dev) if (!in_dev)
return; return 0;
/* we are supposed to accept bcast packets */ /* we are supposed to accept bcast packets */
if (skb->pkt_type == PACKET_MULTICAST) { if (skb->pkt_type == PACKET_MULTICAST) {
ours = ip_check_mc_rcu(in_dev, iph->daddr, iph->saddr, ours = ip_check_mc_rcu(in_dev, iph->daddr, iph->saddr,
iph->protocol); iph->protocol);
if (!ours) if (!ours)
return; return 0;
} }
sk = __udp4_lib_mcast_demux_lookup(net, uh->dest, iph->daddr, sk = __udp4_lib_mcast_demux_lookup(net, uh->dest, iph->daddr,
@ -2263,7 +2263,7 @@ void udp_v4_early_demux(struct sk_buff *skb)
} }
if (!sk || !refcount_inc_not_zero(&sk->sk_refcnt)) if (!sk || !refcount_inc_not_zero(&sk->sk_refcnt))
return; return 0;
skb->sk = sk; skb->sk = sk;
skb->destructor = sock_efree; skb->destructor = sock_efree;
@ -2278,6 +2278,7 @@ void udp_v4_early_demux(struct sk_buff *skb)
*/ */
skb_dst_set_noref(skb, dst); skb_dst_set_noref(skb, dst);
} }
return 0;
} }
int udp_rcv(struct sk_buff *skb) int udp_rcv(struct sk_buff *skb)