[NETFILTER]: bridge netfilter: deal with martians correctly

The attached patch resolves an issue where a IP DNATed packet with a
martian source is forwarded while it's better to drop it. It also
resolves messages complaining about ip forwarding being disabled while
it's actually enabled. Thanks to lepton <ytht.net@gmail.com> for
reporting this problem.

This is probably a candidate for the -stable release.

Signed-off-by: Bart De Schuymer <bdschuym@pandora.be>
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Bart De Schuymer 2006-12-05 13:45:21 -08:00 коммит произвёл David S. Miller
Родитель ece006416d
Коммит f216f082b2
1 изменённых файлов: 28 добавлений и 8 удалений

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

@ -34,6 +34,7 @@
#include <linux/netfilter_ipv6.h> #include <linux/netfilter_ipv6.h>
#include <linux/netfilter_arp.h> #include <linux/netfilter_arp.h>
#include <linux/in_route.h> #include <linux/in_route.h>
#include <linux/inetdevice.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/ipv6.h> #include <net/ipv6.h>
@ -221,10 +222,14 @@ static void __br_dnat_complain(void)
* *
* Otherwise, the packet is considered to be routed and we just * Otherwise, the packet is considered to be routed and we just
* change the destination MAC address so that the packet will * change the destination MAC address so that the packet will
* later be passed up to the IP stack to be routed. * later be passed up to the IP stack to be routed. For a redirected
* packet, ip_route_input() will give back the localhost as output device,
* which differs from the bridge device.
* *
* Let us now consider the case that ip_route_input() fails: * Let us now consider the case that ip_route_input() fails:
* *
* This can be because the destination address is martian, in which case
* the packet will be dropped.
* After a "echo '0' > /proc/sys/net/ipv4/ip_forward" ip_route_input() * After a "echo '0' > /proc/sys/net/ipv4/ip_forward" ip_route_input()
* will fail, while __ip_route_output_key() will return success. The source * will fail, while __ip_route_output_key() will return success. The source
* address for __ip_route_output_key() is set to zero, so __ip_route_output_key * address for __ip_route_output_key() is set to zero, so __ip_route_output_key
@ -237,7 +242,8 @@ static void __br_dnat_complain(void)
* *
* --Lennert, 20020411 * --Lennert, 20020411
* --Bart, 20020416 (updated) * --Bart, 20020416 (updated)
* --Bart, 20021007 (updated) */ * --Bart, 20021007 (updated)
* --Bart, 20062711 (updated) */
static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb) static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
{ {
if (skb->pkt_type == PACKET_OTHERHOST) { if (skb->pkt_type == PACKET_OTHERHOST) {
@ -264,15 +270,15 @@ static int br_nf_pre_routing_finish(struct sk_buff *skb)
struct net_device *dev = skb->dev; struct net_device *dev = skb->dev;
struct iphdr *iph = skb->nh.iph; struct iphdr *iph = skb->nh.iph;
struct nf_bridge_info *nf_bridge = skb->nf_bridge; struct nf_bridge_info *nf_bridge = skb->nf_bridge;
int err;
if (nf_bridge->mask & BRNF_PKT_TYPE) { if (nf_bridge->mask & BRNF_PKT_TYPE) {
skb->pkt_type = PACKET_OTHERHOST; skb->pkt_type = PACKET_OTHERHOST;
nf_bridge->mask ^= BRNF_PKT_TYPE; nf_bridge->mask ^= BRNF_PKT_TYPE;
} }
nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING; nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING;
if (dnat_took_place(skb)) { if (dnat_took_place(skb)) {
if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, dev)) { if ((err = ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, dev))) {
struct rtable *rt; struct rtable *rt;
struct flowi fl = { struct flowi fl = {
.nl_u = { .nl_u = {
@ -283,19 +289,33 @@ static int br_nf_pre_routing_finish(struct sk_buff *skb)
}, },
.proto = 0, .proto = 0,
}; };
struct in_device *in_dev = in_dev_get(dev);
/* If err equals -EHOSTUNREACH the error is due to a
* martian destination or due to the fact that
* forwarding is disabled. For most martian packets,
* ip_route_output_key() will fail. It won't fail for 2 types of
* martian destinations: loopback destinations and destination
* 0.0.0.0. In both cases the packet will be dropped because the
* destination is the loopback device and not the bridge. */
if (err != -EHOSTUNREACH || !in_dev || IN_DEV_FORWARD(in_dev))
goto free_skb;
if (!ip_route_output_key(&rt, &fl)) { if (!ip_route_output_key(&rt, &fl)) {
/* - Bridged-and-DNAT'ed traffic doesn't /* - Bridged-and-DNAT'ed traffic doesn't
* require ip_forwarding. * require ip_forwarding. */
* - Deal with redirected traffic. */ if (((struct dst_entry *)rt)->dev == dev) {
if (((struct dst_entry *)rt)->dev == dev ||
rt->rt_type == RTN_LOCAL) {
skb->dst = (struct dst_entry *)rt; skb->dst = (struct dst_entry *)rt;
goto bridged_dnat; goto bridged_dnat;
} }
/* we are sure that forwarding is disabled, so printing
* this message is no problem. Note that the packet could
* still have a martian destination address, in which case
* the packet could be dropped even if forwarding were enabled */
__br_dnat_complain(); __br_dnat_complain();
dst_release((struct dst_entry *)rt); dst_release((struct dst_entry *)rt);
} }
free_skb:
kfree_skb(skb); kfree_skb(skb);
return 0; return 0;
} else { } else {