diff options
author | Liping Zhang <zlpnobody@gmail.com> | 2017-01-07 07:49:18 -0500 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2017-01-16 08:23:01 -0500 |
commit | 6443ebc3fdd6f3c766d9442c18be274b3d736050 (patch) | |
tree | 3074af3b09ed120ad74198e669fad6464127d66e | |
parent | d7f5762c5e532dfe8247ce1bc60d97af27ff8d00 (diff) |
netfilter: rpfilter: fix incorrect loopback packet judgment
Currently, we check the existing rtable in PREROUTING hook, if RTCF_LOCAL
is set, we assume that the packet is loopback.
But this assumption is incorrect, for example, a packet encapsulated
in ipsec transport mode was received and routed to local, after
decapsulation, it would be delivered to local again, and the rtable
was not dropped, so RTCF_LOCAL check would trigger. But actually, the
packet was not loopback.
So for these normal loopback packets, we can check whether the in device
is IFF_LOOPBACK or not. For these locally generated broadcast/multicast,
we can check whether the skb->pkt_type is PACKET_LOOPBACK or not.
Finally, there's a subtle difference between nft fib expr and xtables
rpfilter extension, user can add the following nft rule to do strict
rpfilter check:
# nft add rule x y meta iif eth0 fib saddr . iif oif != eth0 drop
So when the packet is loopback, it's better to store the in device
instead of the LOOPBACK_IFINDEX, otherwise, after adding the above
nft rule, locally generated broad/multicast packets will be dropped
incorrectly.
Fixes: f83a7ea2075c ("netfilter: xt_rpfilter: skip locally generated broadcast/multicast, too")
Fixes: f6d0cbcf09c5 ("netfilter: nf_tables: add fib expression")
Signed-off-by: Liping Zhang <zlpnobody@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
-rw-r--r-- | include/net/netfilter/nft_fib.h | 6 | ||||
-rw-r--r-- | net/ipv4/netfilter/ipt_rpfilter.c | 8 | ||||
-rw-r--r-- | net/ipv4/netfilter/nft_fib_ipv4.c | 15 | ||||
-rw-r--r-- | net/ipv6/netfilter/ip6t_rpfilter.c | 8 | ||||
-rw-r--r-- | net/ipv6/netfilter/nft_fib_ipv6.c | 13 |
5 files changed, 23 insertions, 27 deletions
diff --git a/include/net/netfilter/nft_fib.h b/include/net/netfilter/nft_fib.h index cbedda077db2..5ceb2205e4e3 100644 --- a/include/net/netfilter/nft_fib.h +++ b/include/net/netfilter/nft_fib.h | |||
@@ -9,6 +9,12 @@ struct nft_fib { | |||
9 | 9 | ||
10 | extern const struct nla_policy nft_fib_policy[]; | 10 | extern const struct nla_policy nft_fib_policy[]; |
11 | 11 | ||
12 | static inline bool | ||
13 | nft_fib_is_loopback(const struct sk_buff *skb, const struct net_device *in) | ||
14 | { | ||
15 | return skb->pkt_type == PACKET_LOOPBACK || in->flags & IFF_LOOPBACK; | ||
16 | } | ||
17 | |||
12 | int nft_fib_dump(struct sk_buff *skb, const struct nft_expr *expr); | 18 | int nft_fib_dump(struct sk_buff *skb, const struct nft_expr *expr); |
13 | int nft_fib_init(const struct nft_ctx *ctx, const struct nft_expr *expr, | 19 | int nft_fib_init(const struct nft_ctx *ctx, const struct nft_expr *expr, |
14 | const struct nlattr * const tb[]); | 20 | const struct nlattr * const tb[]); |
diff --git a/net/ipv4/netfilter/ipt_rpfilter.c b/net/ipv4/netfilter/ipt_rpfilter.c index f273098e48fd..37fb9552e858 100644 --- a/net/ipv4/netfilter/ipt_rpfilter.c +++ b/net/ipv4/netfilter/ipt_rpfilter.c | |||
@@ -63,10 +63,10 @@ static bool rpfilter_lookup_reverse(struct net *net, struct flowi4 *fl4, | |||
63 | return dev_match || flags & XT_RPFILTER_LOOSE; | 63 | return dev_match || flags & XT_RPFILTER_LOOSE; |
64 | } | 64 | } |
65 | 65 | ||
66 | static bool rpfilter_is_local(const struct sk_buff *skb) | 66 | static bool |
67 | rpfilter_is_loopback(const struct sk_buff *skb, const struct net_device *in) | ||
67 | { | 68 | { |
68 | const struct rtable *rt = skb_rtable(skb); | 69 | return skb->pkt_type == PACKET_LOOPBACK || in->flags & IFF_LOOPBACK; |
69 | return rt && (rt->rt_flags & RTCF_LOCAL); | ||
70 | } | 70 | } |
71 | 71 | ||
72 | static bool rpfilter_mt(const struct sk_buff *skb, struct xt_action_param *par) | 72 | static bool rpfilter_mt(const struct sk_buff *skb, struct xt_action_param *par) |
@@ -79,7 +79,7 @@ static bool rpfilter_mt(const struct sk_buff *skb, struct xt_action_param *par) | |||
79 | info = par->matchinfo; | 79 | info = par->matchinfo; |
80 | invert = info->flags & XT_RPFILTER_INVERT; | 80 | invert = info->flags & XT_RPFILTER_INVERT; |
81 | 81 | ||
82 | if (rpfilter_is_local(skb)) | 82 | if (rpfilter_is_loopback(skb, xt_in(par))) |
83 | return true ^ invert; | 83 | return true ^ invert; |
84 | 84 | ||
85 | iph = ip_hdr(skb); | 85 | iph = ip_hdr(skb); |
diff --git a/net/ipv4/netfilter/nft_fib_ipv4.c b/net/ipv4/netfilter/nft_fib_ipv4.c index 965b1a161369..2981291910dd 100644 --- a/net/ipv4/netfilter/nft_fib_ipv4.c +++ b/net/ipv4/netfilter/nft_fib_ipv4.c | |||
@@ -26,13 +26,6 @@ static __be32 get_saddr(__be32 addr) | |||
26 | return addr; | 26 | return addr; |
27 | } | 27 | } |
28 | 28 | ||
29 | static bool fib4_is_local(const struct sk_buff *skb) | ||
30 | { | ||
31 | const struct rtable *rt = skb_rtable(skb); | ||
32 | |||
33 | return rt && (rt->rt_flags & RTCF_LOCAL); | ||
34 | } | ||
35 | |||
36 | #define DSCP_BITS 0xfc | 29 | #define DSCP_BITS 0xfc |
37 | 30 | ||
38 | void nft_fib4_eval_type(const struct nft_expr *expr, struct nft_regs *regs, | 31 | void nft_fib4_eval_type(const struct nft_expr *expr, struct nft_regs *regs, |
@@ -95,8 +88,10 @@ void nft_fib4_eval(const struct nft_expr *expr, struct nft_regs *regs, | |||
95 | else | 88 | else |
96 | oif = NULL; | 89 | oif = NULL; |
97 | 90 | ||
98 | if (nft_hook(pkt) == NF_INET_PRE_ROUTING && fib4_is_local(pkt->skb)) { | 91 | if (nft_hook(pkt) == NF_INET_PRE_ROUTING && |
99 | nft_fib_store_result(dest, priv->result, pkt, LOOPBACK_IFINDEX); | 92 | nft_fib_is_loopback(pkt->skb, nft_in(pkt))) { |
93 | nft_fib_store_result(dest, priv->result, pkt, | ||
94 | nft_in(pkt)->ifindex); | ||
100 | return; | 95 | return; |
101 | } | 96 | } |
102 | 97 | ||
@@ -131,7 +126,7 @@ void nft_fib4_eval(const struct nft_expr *expr, struct nft_regs *regs, | |||
131 | switch (res.type) { | 126 | switch (res.type) { |
132 | case RTN_UNICAST: | 127 | case RTN_UNICAST: |
133 | break; | 128 | break; |
134 | case RTN_LOCAL: /* should not appear here, see fib4_is_local() above */ | 129 | case RTN_LOCAL: /* Should not see RTN_LOCAL here */ |
135 | return; | 130 | return; |
136 | default: | 131 | default: |
137 | break; | 132 | break; |
diff --git a/net/ipv6/netfilter/ip6t_rpfilter.c b/net/ipv6/netfilter/ip6t_rpfilter.c index d5263dc364a9..b12e61b7b16c 100644 --- a/net/ipv6/netfilter/ip6t_rpfilter.c +++ b/net/ipv6/netfilter/ip6t_rpfilter.c | |||
@@ -72,10 +72,10 @@ static bool rpfilter_lookup_reverse6(struct net *net, const struct sk_buff *skb, | |||
72 | return ret; | 72 | return ret; |
73 | } | 73 | } |
74 | 74 | ||
75 | static bool rpfilter_is_local(const struct sk_buff *skb) | 75 | static bool |
76 | rpfilter_is_loopback(const struct sk_buff *skb, const struct net_device *in) | ||
76 | { | 77 | { |
77 | const struct rt6_info *rt = (const void *) skb_dst(skb); | 78 | return skb->pkt_type == PACKET_LOOPBACK || in->flags & IFF_LOOPBACK; |
78 | return rt && (rt->rt6i_flags & RTF_LOCAL); | ||
79 | } | 79 | } |
80 | 80 | ||
81 | static bool rpfilter_mt(const struct sk_buff *skb, struct xt_action_param *par) | 81 | static bool rpfilter_mt(const struct sk_buff *skb, struct xt_action_param *par) |
@@ -85,7 +85,7 @@ static bool rpfilter_mt(const struct sk_buff *skb, struct xt_action_param *par) | |||
85 | struct ipv6hdr *iph; | 85 | struct ipv6hdr *iph; |
86 | bool invert = info->flags & XT_RPFILTER_INVERT; | 86 | bool invert = info->flags & XT_RPFILTER_INVERT; |
87 | 87 | ||
88 | if (rpfilter_is_local(skb)) | 88 | if (rpfilter_is_loopback(skb, xt_in(par))) |
89 | return true ^ invert; | 89 | return true ^ invert; |
90 | 90 | ||
91 | iph = ipv6_hdr(skb); | 91 | iph = ipv6_hdr(skb); |
diff --git a/net/ipv6/netfilter/nft_fib_ipv6.c b/net/ipv6/netfilter/nft_fib_ipv6.c index c947aad8bcc6..765facf03d45 100644 --- a/net/ipv6/netfilter/nft_fib_ipv6.c +++ b/net/ipv6/netfilter/nft_fib_ipv6.c | |||
@@ -18,13 +18,6 @@ | |||
18 | #include <net/ip6_fib.h> | 18 | #include <net/ip6_fib.h> |
19 | #include <net/ip6_route.h> | 19 | #include <net/ip6_route.h> |
20 | 20 | ||
21 | static bool fib6_is_local(const struct sk_buff *skb) | ||
22 | { | ||
23 | const struct rt6_info *rt = (const void *)skb_dst(skb); | ||
24 | |||
25 | return rt && (rt->rt6i_flags & RTF_LOCAL); | ||
26 | } | ||
27 | |||
28 | static int get_ifindex(const struct net_device *dev) | 21 | static int get_ifindex(const struct net_device *dev) |
29 | { | 22 | { |
30 | return dev ? dev->ifindex : 0; | 23 | return dev ? dev->ifindex : 0; |
@@ -164,8 +157,10 @@ void nft_fib6_eval(const struct nft_expr *expr, struct nft_regs *regs, | |||
164 | 157 | ||
165 | lookup_flags = nft_fib6_flowi_init(&fl6, priv, pkt, oif); | 158 | lookup_flags = nft_fib6_flowi_init(&fl6, priv, pkt, oif); |
166 | 159 | ||
167 | if (nft_hook(pkt) == NF_INET_PRE_ROUTING && fib6_is_local(pkt->skb)) { | 160 | if (nft_hook(pkt) == NF_INET_PRE_ROUTING && |
168 | nft_fib_store_result(dest, priv->result, pkt, LOOPBACK_IFINDEX); | 161 | nft_fib_is_loopback(pkt->skb, nft_in(pkt))) { |
162 | nft_fib_store_result(dest, priv->result, pkt, | ||
163 | nft_in(pkt)->ifindex); | ||
169 | return; | 164 | return; |
170 | } | 165 | } |
171 | 166 | ||