Commit c9e1673a authored by Patrick McHardy's avatar Patrick McHardy Committed by Pablo Neira Ayuso

netfilter: ipv4: propagate routing errors from ip_route_me_harder()

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: default avatarPatrick McHardy <kaber@trash.net>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent 6b0ee8c0
...@@ -40,14 +40,14 @@ int ip_route_me_harder(struct sk_buff *skb, unsigned int addr_type) ...@@ -40,14 +40,14 @@ int ip_route_me_harder(struct sk_buff *skb, unsigned int addr_type)
fl4.flowi4_flags = flags; fl4.flowi4_flags = flags;
rt = ip_route_output_key(net, &fl4); rt = ip_route_output_key(net, &fl4);
if (IS_ERR(rt)) if (IS_ERR(rt))
return -1; return PTR_ERR(rt);
/* Drop old route. */ /* Drop old route. */
skb_dst_drop(skb); skb_dst_drop(skb);
skb_dst_set(skb, &rt->dst); skb_dst_set(skb, &rt->dst);
if (skb_dst(skb)->error) if (skb_dst(skb)->error)
return -1; return skb_dst(skb)->error;
#ifdef CONFIG_XFRM #ifdef CONFIG_XFRM
if (!(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && if (!(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) &&
...@@ -56,7 +56,7 @@ int ip_route_me_harder(struct sk_buff *skb, unsigned int addr_type) ...@@ -56,7 +56,7 @@ int ip_route_me_harder(struct sk_buff *skb, unsigned int addr_type)
skb_dst_set(skb, NULL); skb_dst_set(skb, NULL);
dst = xfrm_lookup(net, dst, flowi4_to_flowi(&fl4), skb->sk, 0); dst = xfrm_lookup(net, dst, flowi4_to_flowi(&fl4), skb->sk, 0);
if (IS_ERR(dst)) if (IS_ERR(dst))
return -1; return PTR_ERR(dst);;
skb_dst_set(skb, dst); skb_dst_set(skb, dst);
} }
#endif #endif
...@@ -66,7 +66,7 @@ int ip_route_me_harder(struct sk_buff *skb, unsigned int addr_type) ...@@ -66,7 +66,7 @@ int ip_route_me_harder(struct sk_buff *skb, unsigned int addr_type)
if (skb_headroom(skb) < hh_len && if (skb_headroom(skb) < hh_len &&
pskb_expand_head(skb, HH_DATA_ALIGN(hh_len - skb_headroom(skb)), pskb_expand_head(skb, HH_DATA_ALIGN(hh_len - skb_headroom(skb)),
0, GFP_ATOMIC)) 0, GFP_ATOMIC))
return -1; return -ENOMEM;
return 0; return 0;
} }
......
...@@ -44,6 +44,7 @@ ipt_mangle_out(struct sk_buff *skb, const struct net_device *out) ...@@ -44,6 +44,7 @@ ipt_mangle_out(struct sk_buff *skb, const struct net_device *out)
u_int8_t tos; u_int8_t tos;
__be32 saddr, daddr; __be32 saddr, daddr;
u_int32_t mark; u_int32_t mark;
int err;
/* root is playing with raw sockets. */ /* root is playing with raw sockets. */
if (skb->len < sizeof(struct iphdr) || if (skb->len < sizeof(struct iphdr) ||
...@@ -66,9 +67,11 @@ ipt_mangle_out(struct sk_buff *skb, const struct net_device *out) ...@@ -66,9 +67,11 @@ ipt_mangle_out(struct sk_buff *skb, const struct net_device *out)
if (iph->saddr != saddr || if (iph->saddr != saddr ||
iph->daddr != daddr || iph->daddr != daddr ||
skb->mark != mark || skb->mark != mark ||
iph->tos != tos) iph->tos != tos) {
if (ip_route_me_harder(skb, RTN_UNSPEC)) err = ip_route_me_harder(skb, RTN_UNSPEC);
ret = NF_DROP; if (err < 0)
ret = NF_DROP_ERR(err);
}
} }
return ret; return ret;
......
...@@ -213,6 +213,7 @@ nf_nat_ipv4_local_fn(unsigned int hooknum, ...@@ -213,6 +213,7 @@ nf_nat_ipv4_local_fn(unsigned int hooknum,
const struct nf_conn *ct; const struct nf_conn *ct;
enum ip_conntrack_info ctinfo; enum ip_conntrack_info ctinfo;
unsigned int ret; unsigned int ret;
int err;
/* root is playing with raw sockets. */ /* root is playing with raw sockets. */
if (skb->len < sizeof(struct iphdr) || if (skb->len < sizeof(struct iphdr) ||
...@@ -226,8 +227,9 @@ nf_nat_ipv4_local_fn(unsigned int hooknum, ...@@ -226,8 +227,9 @@ nf_nat_ipv4_local_fn(unsigned int hooknum,
if (ct->tuplehash[dir].tuple.dst.u3.ip != if (ct->tuplehash[dir].tuple.dst.u3.ip !=
ct->tuplehash[!dir].tuple.src.u3.ip) { ct->tuplehash[!dir].tuple.src.u3.ip) {
if (ip_route_me_harder(skb, RTN_UNSPEC)) err = ip_route_me_harder(skb, RTN_UNSPEC);
ret = NF_DROP; if (err < 0)
ret = NF_DROP_ERR(err);
} }
#ifdef CONFIG_XFRM #ifdef CONFIG_XFRM
else if (!(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && else if (!(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) &&
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment