netfilter: ipv6: propagate routing errors from ip6_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.

# ip -6 r a unreachable default table 100
# ip -6 ru add fwmark 0x1 lookup 100
# ip6tables -t mangle -A OUTPUT -d 2001:4860:4860::8888 -j MARK --set-mark 0x1

Old behaviour:

PING 2001:4860:4860::8888(2001:4860:4860::8888) 56 data bytes
ping: sendmsg: Operation not permitted
ping: sendmsg: Operation not permitted
ping: sendmsg: Operation not permitted

New behaviour:

PING 2001:4860:4860::8888(2001:4860:4860::8888) 56 data bytes
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>
This commit is contained in:
Patrick McHardy 2013-04-05 06:41:11 +00:00 коммит произвёл Pablo Neira Ayuso
Родитель c9e1673a0a
Коммит 58e35d1471
3 изменённых файлов: 13 добавлений и 8 удалений

Просмотреть файл

@ -29,7 +29,7 @@ int ip6_route_me_harder(struct sk_buff *skb)
IP6_INC_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES); IP6_INC_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES);
LIMIT_NETDEBUG(KERN_DEBUG "ip6_route_me_harder: No more route.\n"); LIMIT_NETDEBUG(KERN_DEBUG "ip6_route_me_harder: No more route.\n");
dst_release(dst); dst_release(dst);
return -EINVAL; return dst->error;
} }
/* Drop old route. */ /* Drop old route. */
@ -43,7 +43,7 @@ int ip6_route_me_harder(struct sk_buff *skb)
skb_dst_set(skb, NULL); skb_dst_set(skb, NULL);
dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), skb->sk, 0); dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), 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
@ -53,7 +53,7 @@ int ip6_route_me_harder(struct sk_buff *skb)
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;
} }

Просмотреть файл

@ -38,7 +38,7 @@ ip6t_mangle_out(struct sk_buff *skb, const struct net_device *out)
struct in6_addr saddr, daddr; struct in6_addr saddr, daddr;
u_int8_t hop_limit; u_int8_t hop_limit;
u_int32_t flowlabel, mark; u_int32_t flowlabel, mark;
int err;
#if 0 #if 0
/* root is playing with raw sockets. */ /* root is playing with raw sockets. */
if (skb->len < sizeof(struct iphdr) || if (skb->len < sizeof(struct iphdr) ||
@ -65,8 +65,11 @@ ip6t_mangle_out(struct sk_buff *skb, const struct net_device *out)
!ipv6_addr_equal(&ipv6_hdr(skb)->daddr, &daddr) || !ipv6_addr_equal(&ipv6_hdr(skb)->daddr, &daddr) ||
skb->mark != mark || skb->mark != mark ||
ipv6_hdr(skb)->hop_limit != hop_limit || ipv6_hdr(skb)->hop_limit != hop_limit ||
flowlabel != *((u_int32_t *)ipv6_hdr(skb)))) flowlabel != *((u_int32_t *)ipv6_hdr(skb)))) {
return ip6_route_me_harder(skb) == 0 ? ret : NF_DROP; err = ip6_route_me_harder(skb);
if (err < 0)
ret = NF_DROP_ERR(err);
}
return ret; return ret;
} }

Просмотреть файл

@ -215,6 +215,7 @@ nf_nat_ipv6_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 ipv6hdr)) if (skb->len < sizeof(struct ipv6hdr))
@ -227,8 +228,9 @@ nf_nat_ipv6_local_fn(unsigned int hooknum,
if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3, if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3,
&ct->tuplehash[!dir].tuple.src.u3)) { &ct->tuplehash[!dir].tuple.src.u3)) {
if (ip6_route_me_harder(skb)) err = ip6_route_me_harder(skb);
ret = NF_DROP; if (err < 0)
ret = NF_DROP_ERR(err);
} }
#ifdef CONFIG_XFRM #ifdef CONFIG_XFRM
else if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) && else if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&