netfilter: ipv4: propagate routing errors from ip_route_me_harder()
authorPatrick McHardy <kaber@trash.net>
Fri, 5 Apr 2013 06:41:10 +0000 (06:41 +0000)
committerPablo Neira Ayuso <pablo@netfilter.org>
Mon, 8 Apr 2013 10:34:00 +0000 (12:34 +0200)
Propagate routing errors from ip_route_me_harder() when dropping a packet
using NF_DROP_ERR(). This makes userspace get the proper error instead of
EPERM for everything.

Example:

# ip r a unreachable default table 100
# ip ru add fwmark 0x1 lookup 100
# iptables -t mangle -A OUTPUT -d 8.8.8.8 -j MARK --set-mark 0x1

Current behaviour:

PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
ping: sendmsg: Operation not permitted
ping: sendmsg: Operation not permitted
ping: sendmsg: Operation not permitted

New behaviour:

PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
ping: sendmsg: Network is unreachable
ping: sendmsg: Network is unreachable
ping: sendmsg: Network is unreachable
ping: sendmsg: Network is unreachable

Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
net/ipv4/netfilter.c
net/ipv4/netfilter/iptable_mangle.c
net/ipv4/netfilter/iptable_nat.c

index 4c0cf63dd92e9ab94bf155c8e0d497c2029cebde..8b201e8095cda6a23965b7ed4b0a651719303b29 100644 (file)
@@ -40,14 +40,14 @@ int ip_route_me_harder(struct sk_buff *skb, unsigned int addr_type)
        fl4.flowi4_flags = flags;
        rt = ip_route_output_key(net, &fl4);
        if (IS_ERR(rt))
-               return -1;
+               return PTR_ERR(rt);
 
        /* Drop old route. */
        skb_dst_drop(skb);
        skb_dst_set(skb, &rt->dst);
 
        if (skb_dst(skb)->error)
-               return -1;
+               return skb_dst(skb)->error;
 
 #ifdef CONFIG_XFRM
        if (!(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) &&
@@ -56,7 +56,7 @@ int ip_route_me_harder(struct sk_buff *skb, unsigned int addr_type)
                skb_dst_set(skb, NULL);
                dst = xfrm_lookup(net, dst, flowi4_to_flowi(&fl4), skb->sk, 0);
                if (IS_ERR(dst))
-                       return -1;
+                       return PTR_ERR(dst);;
                skb_dst_set(skb, dst);
        }
 #endif
@@ -66,7 +66,7 @@ int ip_route_me_harder(struct sk_buff *skb, unsigned int addr_type)
        if (skb_headroom(skb) < hh_len &&
            pskb_expand_head(skb, HH_DATA_ALIGN(hh_len - skb_headroom(skb)),
                                0, GFP_ATOMIC))
-               return -1;
+               return -ENOMEM;
 
        return 0;
 }
index 85d88f20644701f63ad9b8250d9fd391c108ee55..cba5658ec82c671b92e086ba18c4871dda5d385b 100644 (file)
@@ -44,6 +44,7 @@ ipt_mangle_out(struct sk_buff *skb, const struct net_device *out)
        u_int8_t tos;
        __be32 saddr, daddr;
        u_int32_t mark;
+       int err;
 
        /* root is playing with raw sockets. */
        if (skb->len < sizeof(struct iphdr) ||
@@ -66,9 +67,11 @@ ipt_mangle_out(struct sk_buff *skb, const struct net_device *out)
                if (iph->saddr != saddr ||
                    iph->daddr != daddr ||
                    skb->mark != mark ||
-                   iph->tos != tos)
-                       if (ip_route_me_harder(skb, RTN_UNSPEC))
-                               ret = NF_DROP;
+                   iph->tos != tos) {
+                       err = ip_route_me_harder(skb, RTN_UNSPEC);
+                       if (err < 0)
+                               ret = NF_DROP_ERR(err);
+               }
        }
 
        return ret;
index eeaff7e4acb5c8da1c042769ac4c805579260bef..c2937c81bb679bc4782a369d58980dd7215d2c3c 100644 (file)
@@ -213,6 +213,7 @@ nf_nat_ipv4_local_fn(unsigned int hooknum,
        const struct nf_conn *ct;
        enum ip_conntrack_info ctinfo;
        unsigned int ret;
+       int err;
 
        /* root is playing with raw sockets. */
        if (skb->len < sizeof(struct iphdr) ||
@@ -226,8 +227,9 @@ nf_nat_ipv4_local_fn(unsigned int hooknum,
 
                if (ct->tuplehash[dir].tuple.dst.u3.ip !=
                    ct->tuplehash[!dir].tuple.src.u3.ip) {
-                       if (ip_route_me_harder(skb, RTN_UNSPEC))
-                               ret = NF_DROP;
+                       err = ip_route_me_harder(skb, RTN_UNSPEC);
+                       if (err < 0)
+                               ret = NF_DROP_ERR(err);
                }
 #ifdef CONFIG_XFRM
                else if (!(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) &&