Merge branch 'net-ip-add-drop-reasons-to-input-route'

Menglong Dong says:

====================
net: ip: add drop reasons to input route

In this series, we mainly add some skb drop reasons to the input path of
ip routing, and we make the following functions return drop reasons:

  fib_validate_source()
  ip_route_input_mc()
  ip_mc_validate_source()
  ip_route_input_slow()
  ip_route_input_rcu()
  ip_route_input_noref()
  ip_route_input()
  ip_mkroute_input()
  __mkroute_input()
  ip_route_use_hint()

And following new skb drop reasons are added:

  SKB_DROP_REASON_IP_LOCAL_SOURCE
  SKB_DROP_REASON_IP_INVALID_SOURCE
  SKB_DROP_REASON_IP_LOCALNET
  SKB_DROP_REASON_IP_INVALID_DEST

Changes since v4:
- in the 6th patch: remove the unneeded "else" in ip_expire()
- in the 8th patch: delete the unneeded comment in __mkroute_input()
- in the 9th patch: replace "return 0" with "return SKB_NOT_DROPPED_YET"
  in ip_route_use_hint()

Changes since v3:
- don't refactor fib_validate_source/__fib_validate_source, and introduce
  a wrapper for fib_validate_source() instead in the 1st patch.
- some small adjustment in the 4-7 patches

Changes since v2:
- refactor fib_validate_source and __fib_validate_source to make
  fib_validate_source return drop reasons
- add the 9th and 10th patches to make this series cover the input route
  code path

Changes since v1:
- make ip_route_input_noref/ip_route_input_rcu/ip_route_input_slow return
  drop reasons, instead of passing a local variable to their function
  arguments.
====================

Link: https://patch.msgid.link/20241107125601.1076814-1-dongml2@chinatelecom.cn
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni
2024-11-12 11:24:53 +01:00
12 changed files with 224 additions and 140 deletions

View File

@@ -76,6 +76,10 @@
FN(INVALID_PROTO) \
FN(IP_INADDRERRORS) \
FN(IP_INNOROUTES) \
FN(IP_LOCAL_SOURCE) \
FN(IP_INVALID_SOURCE) \
FN(IP_LOCALNET) \
FN(IP_INVALID_DEST) \
FN(PKT_TOO_BIG) \
FN(DUP_FRAG) \
FN(FRAG_REASM_TIMEOUT) \
@@ -100,6 +104,7 @@
FN(IP_TUNNEL_ECN) \
FN(TUNNEL_TXINFO) \
FN(LOCAL_MAC) \
FN(ARP_PVLAN_DISABLE) \
FNe(MAX)
/**
@@ -373,6 +378,21 @@ enum skb_drop_reason {
* IPSTATS_MIB_INADDRERRORS
*/
SKB_DROP_REASON_IP_INNOROUTES,
/** @SKB_DROP_REASON_IP_LOCAL_SOURCE: the source ip is local */
SKB_DROP_REASON_IP_LOCAL_SOURCE,
/**
* @SKB_DROP_REASON_IP_INVALID_SOURCE: the source ip is invalid:
* 1) source ip is multicast or limited broadcast
* 2) source ip is zero and not IGMP
*/
SKB_DROP_REASON_IP_INVALID_SOURCE,
/** @SKB_DROP_REASON_IP_LOCALNET: source or dest ip is local net */
SKB_DROP_REASON_IP_LOCALNET,
/**
* @SKB_DROP_REASON_IP_INVALID_DEST: the dest ip is invalid:
* 1) dest ip is 0
*/
SKB_DROP_REASON_IP_INVALID_DEST,
/**
* @SKB_DROP_REASON_PKT_TOO_BIG: packet size is too big (maybe exceed the
* MTU)
@@ -458,6 +478,12 @@ enum skb_drop_reason {
* the MAC address of the local netdev.
*/
SKB_DROP_REASON_LOCAL_MAC,
/**
* @SKB_DROP_REASON_ARP_PVLAN_DISABLE: packet which is not IP is
* forwarded to the in_dev, and the proxy_arp_pvlan is not
* enabled.
*/
SKB_DROP_REASON_ARP_PVLAN_DISABLE,
/**
* @SKB_DROP_REASON_MAX: the maximum of core drop reasons, which
* shouldn't be used as a real 'reason' - only for tracing code gen

View File

@@ -452,6 +452,18 @@ int fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst,
dscp_t dscp, int oif, struct net_device *dev,
struct in_device *idev, u32 *itag);
static inline enum skb_drop_reason
fib_validate_source_reason(struct sk_buff *skb, __be32 src, __be32 dst,
dscp_t dscp, int oif, struct net_device *dev,
struct in_device *idev, u32 *itag)
{
int err = fib_validate_source(skb, src, dst, dscp, oif, dev, idev,
itag);
if (err < 0)
return -err;
return SKB_NOT_DROPPED_YET;
}
#ifdef CONFIG_IP_ROUTE_CLASSID
static inline int fib_num_tclassid_users(struct net *net)
{

View File

@@ -199,30 +199,34 @@ static inline struct rtable *ip_route_output_gre(struct net *net, struct flowi4
return ip_route_output_key(net, fl4);
}
int ip_mc_validate_source(struct sk_buff *skb, __be32 daddr, __be32 saddr,
dscp_t dscp, struct net_device *dev,
struct in_device *in_dev, u32 *itag);
int ip_route_input_noref(struct sk_buff *skb, __be32 daddr, __be32 saddr,
dscp_t dscp, struct net_device *dev);
int ip_route_use_hint(struct sk_buff *skb, __be32 daddr, __be32 saddr,
enum skb_drop_reason
ip_mc_validate_source(struct sk_buff *skb, __be32 daddr, __be32 saddr,
dscp_t dscp, struct net_device *dev,
const struct sk_buff *hint);
struct in_device *in_dev, u32 *itag);
enum skb_drop_reason
ip_route_input_noref(struct sk_buff *skb, __be32 daddr, __be32 saddr,
dscp_t dscp, struct net_device *dev);
enum skb_drop_reason
ip_route_use_hint(struct sk_buff *skb, __be32 daddr, __be32 saddr,
dscp_t dscp, struct net_device *dev,
const struct sk_buff *hint);
static inline int ip_route_input(struct sk_buff *skb, __be32 dst, __be32 src,
dscp_t dscp, struct net_device *devin)
static inline enum skb_drop_reason
ip_route_input(struct sk_buff *skb, __be32 dst, __be32 src, dscp_t dscp,
struct net_device *devin)
{
int err;
enum skb_drop_reason reason;
rcu_read_lock();
err = ip_route_input_noref(skb, dst, src, dscp, devin);
if (!err) {
reason = ip_route_input_noref(skb, dst, src, dscp, devin);
if (!reason) {
skb_dst_force(skb);
if (!skb_dst(skb))
err = -EINVAL;
reason = SKB_DROP_REASON_NOT_SPECIFIED;
}
rcu_read_unlock();
return err;
return reason;
}
void ipv4_update_pmtu(struct sk_buff *skb, struct net *net, u32 mtu, int oif,

View File

@@ -373,8 +373,8 @@ static int br_nf_pre_routing_finish(struct net *net, struct sock *sk, struct sk_
struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
struct net_device *dev = skb->dev, *br_indev;
const struct iphdr *iph = ip_hdr(skb);
enum skb_drop_reason reason;
struct rtable *rt;
int err;
br_indev = nf_bridge_get_physindev(skb, net);
if (!br_indev) {
@@ -390,9 +390,9 @@ static int br_nf_pre_routing_finish(struct net *net, struct sock *sk, struct sk_
}
nf_bridge->in_prerouting = 0;
if (br_nf_ipv4_daddr_was_changed(skb, nf_bridge)) {
err = ip_route_input(skb, iph->daddr, iph->saddr,
ip4h_dscp(iph), dev);
if (err) {
reason = ip_route_input(skb, iph->daddr, iph->saddr,
ip4h_dscp(iph), dev);
if (reason) {
struct in_device *in_dev = __in_dev_get_rcu(dev);
/* If err equals -EHOSTUNREACH the error is due to a
@@ -402,7 +402,8 @@ static int br_nf_pre_routing_finish(struct net *net, struct sock *sk, struct sk_
* martian destinations: loopback destinations and destination
* 0.0.0.0. In both cases the packet will be dropped because the
* destination is the loopback device and not the bridge. */
if (err != -EHOSTUNREACH || !in_dev || IN_DEV_FORWARD(in_dev))
if (reason != SKB_DROP_REASON_IP_INADDRERRORS || !in_dev ||
IN_DEV_FORWARD(in_dev))
goto free_skb;
rt = ip_route_output(net, iph->daddr, 0,

View File

@@ -88,6 +88,7 @@ static int run_lwt_bpf(struct sk_buff *skb, struct bpf_lwt_prog *lwt,
static int bpf_lwt_input_reroute(struct sk_buff *skb)
{
enum skb_drop_reason reason;
int err = -EINVAL;
if (skb->protocol == htons(ETH_P_IP)) {
@@ -96,8 +97,9 @@ static int bpf_lwt_input_reroute(struct sk_buff *skb)
dev_hold(dev);
skb_dst_drop(skb);
err = ip_route_input_noref(skb, iph->daddr, iph->saddr,
ip4h_dscp(iph), dev);
reason = ip_route_input_noref(skb, iph->daddr, iph->saddr,
ip4h_dscp(iph), dev);
err = reason ? -EINVAL : 0;
dev_put(dev);
} else if (skb->protocol == htons(ETH_P_IPV6)) {
skb_dst_drop(skb);

View File

@@ -346,6 +346,7 @@ static int __fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst,
int rpf, struct in_device *idev, u32 *itag)
{
struct net *net = dev_net(dev);
enum skb_drop_reason reason;
struct flow_keys flkeys;
int ret, no_addr;
struct fib_result res;
@@ -377,9 +378,15 @@ static int __fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst,
if (fib_lookup(net, &fl4, &res, 0))
goto last_resort;
if (res.type != RTN_UNICAST &&
(res.type != RTN_LOCAL || !IN_DEV_ACCEPT_LOCAL(idev)))
goto e_inval;
if (res.type != RTN_UNICAST) {
if (res.type != RTN_LOCAL) {
reason = SKB_DROP_REASON_IP_INVALID_SOURCE;
goto e_inval;
} else if (!IN_DEV_ACCEPT_LOCAL(idev)) {
reason = SKB_DROP_REASON_IP_LOCAL_SOURCE;
goto e_inval;
}
}
fib_combine_itag(itag, &res);
dev_match = fib_info_nh_uses_dev(res.fi, dev);
@@ -412,9 +419,9 @@ static int __fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst,
return 0;
e_inval:
return -EINVAL;
return -reason;
e_rpf:
return -EXDEV;
return -SKB_DROP_REASON_IP_RPFILTER;
}
/* Ignore rp_filter for packets protected by IPsec. */

View File

@@ -545,7 +545,7 @@ static struct rtable *icmp_route_lookup(struct net *net, struct flowi4 *fl4,
orefdst = skb_in->_skb_refdst; /* save old refdst */
skb_dst_set(skb_in, NULL);
err = ip_route_input(skb_in, fl4_dec.daddr, fl4_dec.saddr,
dscp, rt2->dst.dev);
dscp, rt2->dst.dev) ? -EINVAL : 0;
dst_release(&rt2->dst);
rt2 = skb_rtable(skb_in);

View File

@@ -132,12 +132,12 @@ static bool frag_expire_skip_icmp(u32 user)
*/
static void ip_expire(struct timer_list *t)
{
enum skb_drop_reason reason = SKB_DROP_REASON_FRAG_REASM_TIMEOUT;
struct inet_frag_queue *frag = from_timer(frag, t, timer);
const struct iphdr *iph;
struct sk_buff *head = NULL;
struct net *net;
struct ipq *qp;
int err;
qp = container_of(frag, struct ipq, q);
net = qp->q.fqdir->net;
@@ -175,14 +175,15 @@ static void ip_expire(struct timer_list *t)
/* skb has no dst, perform route lookup again */
iph = ip_hdr(head);
err = ip_route_input_noref(head, iph->daddr, iph->saddr, ip4h_dscp(iph),
head->dev);
if (err)
reason = ip_route_input_noref(head, iph->daddr, iph->saddr,
ip4h_dscp(iph), head->dev);
if (reason)
goto out;
/* Only an end host needs to send an ICMP
* "Fragment Reassembly Timeout" message, per RFC792.
*/
reason = SKB_DROP_REASON_FRAG_REASM_TIMEOUT;
if (frag_expire_skip_icmp(qp->q.key.v4.user) &&
(skb_rtable(head)->rt_type != RTN_LOCAL))
goto out;
@@ -195,7 +196,7 @@ static void ip_expire(struct timer_list *t)
spin_unlock(&qp->q.lock);
out_rcu_unlock:
rcu_read_unlock();
kfree_skb_reason(head, SKB_DROP_REASON_FRAG_REASM_TIMEOUT);
kfree_skb_reason(head, reason);
ipq_put(qp);
}

View File

@@ -322,15 +322,14 @@ static int ip_rcv_finish_core(struct net *net, struct sock *sk,
int err, drop_reason;
struct rtable *rt;
drop_reason = SKB_DROP_REASON_NOT_SPECIFIED;
if (ip_can_use_hint(skb, iph, hint)) {
err = ip_route_use_hint(skb, iph->daddr, iph->saddr,
ip4h_dscp(iph), dev, hint);
if (unlikely(err))
drop_reason = ip_route_use_hint(skb, iph->daddr, iph->saddr,
ip4h_dscp(iph), dev, hint);
if (unlikely(drop_reason))
goto drop_error;
}
drop_reason = SKB_DROP_REASON_NOT_SPECIFIED;
if (READ_ONCE(net->ipv4.sysctl_ip_early_demux) &&
!skb_dst(skb) &&
!skb->sk &&
@@ -362,10 +361,11 @@ static int ip_rcv_finish_core(struct net *net, struct sock *sk,
* how the packet travels inside Linux networking.
*/
if (!skb_valid_dst(skb)) {
err = ip_route_input_noref(skb, iph->daddr, iph->saddr,
ip4h_dscp(iph), dev);
if (unlikely(err))
drop_reason = ip_route_input_noref(skb, iph->daddr, iph->saddr,
ip4h_dscp(iph), dev);
if (unlikely(drop_reason))
goto drop_error;
drop_reason = SKB_DROP_REASON_NOT_SPECIFIED;
} else {
struct in_device *in_dev = __in_dev_get_rcu(dev);
@@ -425,10 +425,8 @@ static int ip_rcv_finish_core(struct net *net, struct sock *sk,
return NET_RX_DROP;
drop_error:
if (err == -EXDEV) {
drop_reason = SKB_DROP_REASON_IP_RPFILTER;
if (drop_reason == SKB_DROP_REASON_IP_RPFILTER)
__NET_INC_STATS(net, LINUX_MIB_IPRPFILTER);
}
goto drop;
}

View File

@@ -618,7 +618,7 @@ int ip_options_rcv_srr(struct sk_buff *skb, struct net_device *dev)
orefdst = skb->_skb_refdst;
skb_dst_set(skb, NULL);
err = ip_route_input(skb, nexthop, iph->saddr, ip4h_dscp(iph),
dev);
dev) ? -EINVAL : 0;
rt2 = skb_rtable(skb);
if (err || (rt2->rt_type != RTN_UNICAST && rt2->rt_type != RTN_LOCAL)) {
skb_dst_drop(skb);

View File

@@ -1678,50 +1678,54 @@ struct rtable *rt_dst_clone(struct net_device *dev, struct rtable *rt)
EXPORT_SYMBOL(rt_dst_clone);
/* called in rcu_read_lock() section */
int ip_mc_validate_source(struct sk_buff *skb, __be32 daddr, __be32 saddr,
dscp_t dscp, struct net_device *dev,
struct in_device *in_dev, u32 *itag)
enum skb_drop_reason
ip_mc_validate_source(struct sk_buff *skb, __be32 daddr, __be32 saddr,
dscp_t dscp, struct net_device *dev,
struct in_device *in_dev, u32 *itag)
{
int err;
enum skb_drop_reason reason;
/* Primary sanity checks. */
if (!in_dev)
return -EINVAL;
return SKB_DROP_REASON_NOT_SPECIFIED;
if (ipv4_is_multicast(saddr) || ipv4_is_lbcast(saddr) ||
skb->protocol != htons(ETH_P_IP))
return -EINVAL;
if (ipv4_is_multicast(saddr) || ipv4_is_lbcast(saddr))
return SKB_DROP_REASON_IP_INVALID_SOURCE;
if (skb->protocol != htons(ETH_P_IP))
return SKB_DROP_REASON_INVALID_PROTO;
if (ipv4_is_loopback(saddr) && !IN_DEV_ROUTE_LOCALNET(in_dev))
return -EINVAL;
return SKB_DROP_REASON_IP_LOCALNET;
if (ipv4_is_zeronet(saddr)) {
if (!ipv4_is_local_multicast(daddr) &&
ip_hdr(skb)->protocol != IPPROTO_IGMP)
return -EINVAL;
return SKB_DROP_REASON_IP_INVALID_SOURCE;
} else {
err = fib_validate_source(skb, saddr, 0, dscp, 0, dev, in_dev,
itag);
if (err < 0)
return err;
reason = fib_validate_source_reason(skb, saddr, 0, dscp, 0,
dev, in_dev, itag);
if (reason)
return reason;
}
return 0;
return SKB_NOT_DROPPED_YET;
}
/* called in rcu_read_lock() section */
static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr,
dscp_t dscp, struct net_device *dev, int our)
static enum skb_drop_reason
ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr,
dscp_t dscp, struct net_device *dev, int our)
{
struct in_device *in_dev = __in_dev_get_rcu(dev);
unsigned int flags = RTCF_MULTICAST;
enum skb_drop_reason reason;
struct rtable *rth;
u32 itag = 0;
int err;
err = ip_mc_validate_source(skb, daddr, saddr, dscp, dev, in_dev,
&itag);
if (err)
return err;
reason = ip_mc_validate_source(skb, daddr, saddr, dscp, dev, in_dev,
&itag);
if (reason)
return reason;
if (our)
flags |= RTCF_LOCAL;
@@ -1732,7 +1736,7 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr,
rth = rt_dst_alloc(dev_net(dev)->loopback_dev, flags, RTN_MULTICAST,
false);
if (!rth)
return -ENOBUFS;
return SKB_DROP_REASON_NOMEM;
#ifdef CONFIG_IP_ROUTE_CLASSID
rth->dst.tclassid = itag;
@@ -1748,7 +1752,7 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr,
skb_dst_drop(skb);
skb_dst_set(skb, &rth->dst);
return 0;
return SKB_NOT_DROPPED_YET;
}
@@ -1778,10 +1782,12 @@ static void ip_handle_martian_source(struct net_device *dev,
}
/* called in rcu_read_lock() section */
static int __mkroute_input(struct sk_buff *skb, const struct fib_result *res,
struct in_device *in_dev, __be32 daddr,
__be32 saddr, dscp_t dscp)
static enum skb_drop_reason
__mkroute_input(struct sk_buff *skb, const struct fib_result *res,
struct in_device *in_dev, __be32 daddr,
__be32 saddr, dscp_t dscp)
{
enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
struct fib_nh_common *nhc = FIB_RES_NHC(*res);
struct net_device *dev = nhc->nhc_dev;
struct fib_nh_exception *fnhe;
@@ -1795,12 +1801,13 @@ static int __mkroute_input(struct sk_buff *skb, const struct fib_result *res,
out_dev = __in_dev_get_rcu(dev);
if (!out_dev) {
net_crit_ratelimited("Bug in ip_route_input_slow(). Please report.\n");
return -EINVAL;
return reason;
}
err = fib_validate_source(skb, saddr, daddr, dscp, FIB_RES_OIF(*res),
in_dev->dev, in_dev, &itag);
if (err < 0) {
reason = -err;
ip_handle_martian_source(in_dev->dev, in_dev, skb, daddr,
saddr);
@@ -1828,7 +1835,7 @@ static int __mkroute_input(struct sk_buff *skb, const struct fib_result *res,
*/
if (out_dev == in_dev &&
IN_DEV_PROXY_ARP_PVLAN(in_dev) == 0) {
err = -EINVAL;
reason = SKB_DROP_REASON_ARP_PVLAN_DISABLE;
goto cleanup;
}
}
@@ -1851,7 +1858,7 @@ static int __mkroute_input(struct sk_buff *skb, const struct fib_result *res,
rth = rt_dst_alloc(out_dev->dev, 0, res->type,
IN_DEV_ORCONF(out_dev, NOXFRM));
if (!rth) {
err = -ENOBUFS;
reason = SKB_DROP_REASON_NOMEM;
goto cleanup;
}
@@ -1865,9 +1872,9 @@ static int __mkroute_input(struct sk_buff *skb, const struct fib_result *res,
lwtunnel_set_redirect(&rth->dst);
skb_dst_set(skb, &rth->dst);
out:
err = 0;
cleanup:
return err;
reason = SKB_NOT_DROPPED_YET;
cleanup:
return reason;
}
#ifdef CONFIG_IP_ROUTE_MULTIPATH
@@ -2125,9 +2132,10 @@ int fib_multipath_hash(const struct net *net, const struct flowi4 *fl4,
}
#endif /* CONFIG_IP_ROUTE_MULTIPATH */
static int ip_mkroute_input(struct sk_buff *skb, struct fib_result *res,
struct in_device *in_dev, __be32 daddr,
__be32 saddr, dscp_t dscp, struct flow_keys *hkeys)
static enum skb_drop_reason
ip_mkroute_input(struct sk_buff *skb, struct fib_result *res,
struct in_device *in_dev, __be32 daddr,
__be32 saddr, dscp_t dscp, struct flow_keys *hkeys)
{
#ifdef CONFIG_IP_ROUTE_MULTIPATH
if (res->fi && fib_info_num_path(res->fi) > 1) {
@@ -2146,43 +2154,50 @@ static int ip_mkroute_input(struct sk_buff *skb, struct fib_result *res,
* assuming daddr is valid and the destination is not a local broadcast one.
* Uses the provided hint instead of performing a route lookup.
*/
int ip_route_use_hint(struct sk_buff *skb, __be32 daddr, __be32 saddr,
dscp_t dscp, struct net_device *dev,
const struct sk_buff *hint)
enum skb_drop_reason
ip_route_use_hint(struct sk_buff *skb, __be32 daddr, __be32 saddr,
dscp_t dscp, struct net_device *dev,
const struct sk_buff *hint)
{
enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
struct in_device *in_dev = __in_dev_get_rcu(dev);
struct rtable *rt = skb_rtable(hint);
struct net *net = dev_net(dev);
int err = -EINVAL;
u32 tag = 0;
if (!in_dev)
return -EINVAL;
return reason;
if (ipv4_is_multicast(saddr) || ipv4_is_lbcast(saddr))
if (ipv4_is_multicast(saddr) || ipv4_is_lbcast(saddr)) {
reason = SKB_DROP_REASON_IP_INVALID_SOURCE;
goto martian_source;
}
if (ipv4_is_zeronet(saddr))
if (ipv4_is_zeronet(saddr)) {
reason = SKB_DROP_REASON_IP_INVALID_SOURCE;
goto martian_source;
}
if (ipv4_is_loopback(saddr) && !IN_DEV_NET_ROUTE_LOCALNET(in_dev, net))
if (ipv4_is_loopback(saddr) && !IN_DEV_NET_ROUTE_LOCALNET(in_dev, net)) {
reason = SKB_DROP_REASON_IP_LOCALNET;
goto martian_source;
}
if (rt->rt_type != RTN_LOCAL)
goto skip_validate_source;
err = fib_validate_source(skb, saddr, daddr, dscp, 0, dev, in_dev,
&tag);
if (err < 0)
reason = fib_validate_source_reason(skb, saddr, daddr, dscp, 0, dev,
in_dev, &tag);
if (reason)
goto martian_source;
skip_validate_source:
skb_dst_copy(skb, hint);
return 0;
return SKB_NOT_DROPPED_YET;
martian_source:
ip_handle_martian_source(dev, in_dev, skb, daddr, saddr);
return err;
return reason;
}
/* get device for dst_alloc with local routes */
@@ -2211,10 +2226,12 @@ static struct net_device *ip_rt_get_dev(struct net *net,
* called with rcu_read_lock()
*/
static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,
dscp_t dscp, struct net_device *dev,
struct fib_result *res)
static enum skb_drop_reason
ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,
dscp_t dscp, struct net_device *dev,
struct fib_result *res)
{
enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
struct in_device *in_dev = __in_dev_get_rcu(dev);
struct flow_keys *flkeys = NULL, _flkeys;
struct net *net = dev_net(dev);
@@ -2242,8 +2259,10 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,
fl4.flowi4_tun_key.tun_id = 0;
skb_dst_drop(skb);
if (ipv4_is_multicast(saddr) || ipv4_is_lbcast(saddr))
if (ipv4_is_multicast(saddr) || ipv4_is_lbcast(saddr)) {
reason = SKB_DROP_REASON_IP_INVALID_SOURCE;
goto martian_source;
}
res->fi = NULL;
res->table = NULL;
@@ -2253,21 +2272,29 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,
/* Accept zero addresses only to limited broadcast;
* I even do not know to fix it or not. Waiting for complains :-)
*/
if (ipv4_is_zeronet(saddr))
if (ipv4_is_zeronet(saddr)) {
reason = SKB_DROP_REASON_IP_INVALID_SOURCE;
goto martian_source;
}
if (ipv4_is_zeronet(daddr))
if (ipv4_is_zeronet(daddr)) {
reason = SKB_DROP_REASON_IP_INVALID_DEST;
goto martian_destination;
}
/* Following code try to avoid calling IN_DEV_NET_ROUTE_LOCALNET(),
* and call it once if daddr or/and saddr are loopback addresses
*/
if (ipv4_is_loopback(daddr)) {
if (!IN_DEV_NET_ROUTE_LOCALNET(in_dev, net))
if (!IN_DEV_NET_ROUTE_LOCALNET(in_dev, net)) {
reason = SKB_DROP_REASON_IP_LOCALNET;
goto martian_destination;
}
} else if (ipv4_is_loopback(saddr)) {
if (!IN_DEV_NET_ROUTE_LOCALNET(in_dev, net))
if (!IN_DEV_NET_ROUTE_LOCALNET(in_dev, net)) {
reason = SKB_DROP_REASON_IP_LOCALNET;
goto martian_source;
}
}
/*
@@ -2309,10 +2336,11 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,
goto brd_input;
}
err = -EINVAL;
if (res->type == RTN_LOCAL) {
err = fib_validate_source(skb, saddr, daddr, dscp, 0, dev,
in_dev, &itag);
if (err < 0)
reason = fib_validate_source_reason(skb, saddr, daddr, dscp,
0, dev, in_dev, &itag);
if (reason)
goto martian_source;
goto local_input;
}
@@ -2321,21 +2349,28 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,
err = -EHOSTUNREACH;
goto no_route;
}
if (res->type != RTN_UNICAST)
if (res->type != RTN_UNICAST) {
reason = SKB_DROP_REASON_IP_INVALID_DEST;
goto martian_destination;
}
make_route:
err = ip_mkroute_input(skb, res, in_dev, daddr, saddr, dscp, flkeys);
out: return err;
reason = ip_mkroute_input(skb, res, in_dev, daddr, saddr, dscp,
flkeys);
out:
return reason;
brd_input:
if (skb->protocol != htons(ETH_P_IP))
goto e_inval;
if (skb->protocol != htons(ETH_P_IP)) {
reason = SKB_DROP_REASON_INVALID_PROTO;
goto out;
}
if (!ipv4_is_zeronet(saddr)) {
err = fib_validate_source(skb, saddr, 0, dscp, 0, dev, in_dev,
&itag);
if (err < 0)
reason = fib_validate_source_reason(skb, saddr, 0, dscp, 0,
dev, in_dev, &itag);
if (reason)
goto martian_source;
}
flags |= RTCF_BROADCAST;
@@ -2353,7 +2388,7 @@ out: return err;
rth = rcu_dereference(nhc->nhc_rth_input);
if (rt_cache_valid(rth)) {
skb_dst_set_noref(skb, &rth->dst);
err = 0;
reason = SKB_NOT_DROPPED_YET;
goto out;
}
}
@@ -2390,7 +2425,7 @@ out: return err;
rt_add_uncached_list(rth);
}
skb_dst_set(skb, &rth->dst);
err = 0;
reason = SKB_NOT_DROPPED_YET;
goto out;
no_route:
@@ -2411,12 +2446,8 @@ out: return err;
&daddr, &saddr, dev->name);
#endif
e_inval:
err = -EINVAL;
goto out;
e_nobufs:
err = -ENOBUFS;
reason = SKB_DROP_REASON_NOMEM;
goto out;
martian_source:
@@ -2425,9 +2456,10 @@ out: return err;
}
/* called with rcu_read_lock held */
static int ip_route_input_rcu(struct sk_buff *skb, __be32 daddr, __be32 saddr,
dscp_t dscp, struct net_device *dev,
struct fib_result *res)
static enum skb_drop_reason
ip_route_input_rcu(struct sk_buff *skb, __be32 daddr, __be32 saddr,
dscp_t dscp, struct net_device *dev,
struct fib_result *res)
{
/* Multicast recognition logic is moved from route cache to here.
* The problem was that too many Ethernet cards have broken/missing
@@ -2441,12 +2473,12 @@ static int ip_route_input_rcu(struct sk_buff *skb, __be32 daddr, __be32 saddr,
* route cache entry is created eventually.
*/
if (ipv4_is_multicast(daddr)) {
enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
struct in_device *in_dev = __in_dev_get_rcu(dev);
int our = 0;
int err = -EINVAL;
if (!in_dev)
return err;
return -EINVAL;
our = ip_check_mc_rcu(in_dev, daddr, saddr,
ip_hdr(skb)->protocol);
@@ -2467,26 +2499,27 @@ static int ip_route_input_rcu(struct sk_buff *skb, __be32 daddr, __be32 saddr,
IN_DEV_MFORWARD(in_dev))
#endif
) {
err = ip_route_input_mc(skb, daddr, saddr, dscp, dev,
our);
reason = ip_route_input_mc(skb, daddr, saddr, dscp,
dev, our);
}
return err;
return reason;
}
return ip_route_input_slow(skb, daddr, saddr, dscp, dev, res);
}
int ip_route_input_noref(struct sk_buff *skb, __be32 daddr, __be32 saddr,
dscp_t dscp, struct net_device *dev)
enum skb_drop_reason ip_route_input_noref(struct sk_buff *skb, __be32 daddr,
__be32 saddr, dscp_t dscp,
struct net_device *dev)
{
enum skb_drop_reason reason;
struct fib_result res;
int err;
rcu_read_lock();
err = ip_route_input_rcu(skb, daddr, saddr, dscp, dev, &res);
reason = ip_route_input_rcu(skb, daddr, saddr, dscp, dev, &res);
rcu_read_unlock();
return err;
return reason;
}
EXPORT_SYMBOL(ip_route_input_noref);
@@ -3298,7 +3331,7 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
skb->mark = mark;
err = ip_route_input_rcu(skb, dst, src,
inet_dsfield_to_dscp(rtm->rtm_tos),
dev, &res);
dev, &res) ? -EINVAL : 0;
rt = skb_rtable(skb);
if (err == 0 && rt->dst.error)

View File

@@ -954,10 +954,10 @@ static int input_action_end_dx4_finish(struct net *net, struct sock *sk,
struct sk_buff *skb)
{
struct dst_entry *orig_dst = skb_dst(skb);
enum skb_drop_reason reason;
struct seg6_local_lwt *slwt;
struct iphdr *iph;
__be32 nhaddr;
int err;
slwt = seg6_local_lwtunnel(orig_dst->lwtstate);
@@ -967,9 +967,9 @@ static int input_action_end_dx4_finish(struct net *net, struct sock *sk,
skb_dst_drop(skb);
err = ip_route_input(skb, nhaddr, iph->saddr, 0, skb->dev);
if (err) {
kfree_skb(skb);
reason = ip_route_input(skb, nhaddr, iph->saddr, 0, skb->dev);
if (reason) {
kfree_skb_reason(skb, reason);
return -EINVAL;
}
@@ -1174,8 +1174,8 @@ static struct sk_buff *end_dt_vrf_core(struct sk_buff *skb,
static int input_action_end_dt4(struct sk_buff *skb,
struct seg6_local_lwt *slwt)
{
enum skb_drop_reason reason;
struct iphdr *iph;
int err;
if (!decap_and_validate(skb, IPPROTO_IPIP))
goto drop;
@@ -1193,8 +1193,8 @@ static int input_action_end_dt4(struct sk_buff *skb,
iph = ip_hdr(skb);
err = ip_route_input(skb, iph->daddr, iph->saddr, 0, skb->dev);
if (unlikely(err))
reason = ip_route_input(skb, iph->daddr, iph->saddr, 0, skb->dev);
if (unlikely(reason))
goto drop;
return dst_input(skb);