Skip to content

Commit

Permalink
netfilter: ipv4: propagate routing errors from ip_route_me_harder()
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
kaber authored and ummakynes committed Apr 8, 2013
1 parent 6b0ee8c commit c9e1673
Show file tree
Hide file tree
Showing 3 changed files with 14 additions and 9 deletions.
8 changes: 4 additions & 4 deletions net/ipv4/netfilter.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) &&
Expand All @@ -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
Expand All @@ -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;
}
Expand Down
9 changes: 6 additions & 3 deletions net/ipv4/netfilter/iptable_mangle.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) ||
Expand All @@ -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;
Expand Down
6 changes: 4 additions & 2 deletions net/ipv4/netfilter/iptable_nat.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) ||
Expand All @@ -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) &&
Expand Down

0 comments on commit c9e1673

Please sign in to comment.