]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
net: ip: make fib_validate_source() support drop reasons
authorMenglong Dong <menglong8.dong@gmail.com>
Thu, 7 Nov 2024 12:55:53 +0000 (20:55 +0800)
committerPaolo Abeni <pabeni@redhat.com>
Tue, 12 Nov 2024 10:24:50 +0000 (11:24 +0100)
In this commit, we make fib_validate_source() and __fib_validate_source()
return -reason instead of errno on error.

The return value of fib_validate_source can be -errno, 0, and 1. It's hard
to make fib_validate_source() return drop reasons directly.

The fib_validate_source() will return 1 if the scope of the source(revert)
route is HOST. And the __mkroute_input() will mark the skb with
IPSKB_DOREDIRECT in this case (combine with some other conditions). And
then, a REDIRECT ICMP will be sent in ip_forward() if this flag exists. We
can't pass this information to __mkroute_input if we make
fib_validate_source() return drop reasons.

Therefore, we introduce the wrapper fib_validate_source_reason() for
fib_validate_source(), which will return the drop reasons on error.

In the origin logic, LINUX_MIB_IPRPFILTER will be counted if
fib_validate_source() return -EXDEV. And now, we need to adjust it by
checking "reason == SKB_DROP_REASON_IP_RPFILTER". However, this will take
effect only after the patch "net: ip: make ip_route_input_noref() return
drop reasons", as we can't pass the drop reasons from
fib_validate_source() to ip_rcv_finish_core() in this patch.

Following new drop reasons are added in this patch:

  SKB_DROP_REASON_IP_LOCAL_SOURCE
  SKB_DROP_REASON_IP_INVALID_SOURCE

Signed-off-by: Menglong Dong <dongml2@chinatelecom.cn>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
include/net/dropreason-core.h
include/net/ip_fib.h
net/ipv4/fib_frontend.c
net/ipv4/ip_input.c
net/ipv4/route.c

index d59bb96c5a02c63c20cf24e6174e82dd787f4b7e..62a60be1db840646b5ecd3d1da3fd40624432804 100644 (file)
@@ -76,6 +76,8 @@
        FN(INVALID_PROTO)               \
        FN(IP_INADDRERRORS)             \
        FN(IP_INNOROUTES)               \
+       FN(IP_LOCAL_SOURCE)             \
+       FN(IP_INVALID_SOURCE)           \
        FN(PKT_TOO_BIG)                 \
        FN(DUP_FRAG)                    \
        FN(FRAG_REASM_TIMEOUT)          \
@@ -373,6 +375,14 @@ 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_PKT_TOO_BIG: packet size is too big (maybe exceed the
         * MTU)
index b6e44f4eaa4c7c46a4103ebd668d192a73fd8e8a..a113c11ab56b5f6e1659f7a3a454822932892f02 100644 (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)
 {
index 0c9ce934b4904763ef6d6918ea1f9666fa139b39..87bb36a5bdece1a33b4c63777714a5ffde0b269b 100644 (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 @@ last_resort:
        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. */
index 89bb63da6852164e498bb391a0ebcdac590612b0..c40a26972884b4f6a3fad6087435690da815cf39 100644 (file)
@@ -425,10 +425,8 @@ drop:
        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;
 }
 
index ccdbe9c701320ee1f117d0680412affc6a113f78..c38e95d9da9e7218f2a0b480fc115dc67a82cd1e 100644 (file)
@@ -1682,7 +1682,7 @@ 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 err;
+       enum skb_drop_reason reason;
 
        /* Primary sanity checks. */
        if (!in_dev)
@@ -1700,10 +1700,10 @@ int ip_mc_validate_source(struct sk_buff *skb, __be32 daddr, __be32 saddr,
                    ip_hdr(skb)->protocol != IPPROTO_IGMP)
                        return -EINVAL;
        } 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 -EINVAL;
        }
        return 0;
 }
@@ -1801,6 +1801,7 @@ static int __mkroute_input(struct sk_buff *skb, const struct fib_result *res,
        err = fib_validate_source(skb, saddr, daddr, dscp, FIB_RES_OIF(*res),
                                  in_dev->dev, in_dev, &itag);
        if (err < 0) {
+               err = -EINVAL;
                ip_handle_martian_source(in_dev->dev, in_dev, skb, daddr,
                                         saddr);
 
@@ -2153,6 +2154,7 @@ int ip_route_use_hint(struct sk_buff *skb, __be32 daddr, __be32 saddr,
        struct in_device *in_dev = __in_dev_get_rcu(dev);
        struct rtable *rt = skb_rtable(hint);
        struct net *net = dev_net(dev);
+       enum skb_drop_reason reason;
        int err = -EINVAL;
        u32 tag = 0;
 
@@ -2171,9 +2173,9 @@ int ip_route_use_hint(struct sk_buff *skb, __be32 daddr, __be32 saddr,
        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:
@@ -2215,6 +2217,7 @@ 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)
 {
+       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);
@@ -2309,10 +2312,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;
        }
@@ -2333,9 +2337,10 @@ brd_input:
                goto e_inval;
 
        if (!ipv4_is_zeronet(saddr)) {
-               err = fib_validate_source(skb, saddr, 0, dscp, 0, dev, in_dev,
-                                         &itag);
-               if (err < 0)
+               err = -EINVAL;
+               reason = fib_validate_source_reason(skb, saddr, 0, dscp, 0,
+                                                   dev, in_dev, &itag);
+               if (reason)
                        goto martian_source;
        }
        flags |= RTCF_BROADCAST;