Hi Dave! This is the 2nd of my 2.6 merge of the recent bugfixes (all tested against 2.4.22-pre7). You might need to apply them incrementally (didn't test it in a different order). Author: Patrick McHardy This patch fixes some issues with iptables REJECT and MIRROR targets when combined with policy routing / rp_filter. Please apply, diff -Nru --exclude .depend --exclude '*.o' --exclude '*.ko' --exclude '*.ver' --exclude '.*.flags' --exclude '*.orig' --exclude '*.rej' --exclude '*.cmd' --exclude '*.mod.c' --exclude '*~' linux-2.6.0-test1-nftest2/net/ipv4/netfilter/ipt_MIRROR.c linux-2.6.0-test1-nftest3/net/ipv4/netfilter/ipt_MIRROR.c --- linux-2.6.0-test1-nftest2/net/ipv4/netfilter/ipt_MIRROR.c 2003-07-19 16:05:13.000000000 +0200 +++ linux-2.6.0-test1-nftest3/net/ipv4/netfilter/ipt_MIRROR.c 2003-07-19 16:13:56.000000000 +0200 @@ -12,6 +12,9 @@ 18 Jul 2003 Harald Welte - merge Patrick McHardy's mirror fixes from 2.4.22 to 2.6.0-test1 + 19 Jul 2003 Harald Welte + - merge Patrick McHardy's rp_filter fixes from 2.4.22 to + 2.6.0-test1 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the @@ -43,17 +46,42 @@ #define DEBUGP(format, args...) #endif -static inline struct rtable *route_mirror(struct sk_buff *skb) +static inline struct rtable *route_mirror(struct sk_buff *skb, int local) { struct iphdr *iph = skb->nh.iph; - struct flowi fl = { .nl_u = { .ip4_u = { .daddr = iph->saddr, - .saddr = iph->daddr, - .tos = RT_TOS(iph->tos) } } }; + struct dst_entry *odst; + struct flowi fl = {}; struct rtable *rt; - /* Backwards */ - if (ip_route_output_key(&rt, &fl)) - return NULL; + if (local) { + fl.nl_u.ip4_u.daddr = iph->saddr; + fl.nl_u.ip4_u.saddr = iph->daddr; + fl.nl_u.ip4_u.tos = RT_TOS(iph->tos); + + if (ip_route_output_key(&rt, &fl) != 0) + return NULL; + } else { + /* non-local src, find valid iif to satisfy + * rp-filter when calling ip_route_input(). */ + fl.nl_u.ip4_u.daddr = iph->daddr; + if (ip_route_output_key(&rt, &fl) != 0) + return NULL; + + odst = skb->dst; + if (ip_route_input(skb, iph->saddr, iph->daddr, + RT_TOS(iph->tos), rt->u.dst.dev) != 0) { + dst_release(&rt->u.dst); + return NULL; + } + dst_release(&rt->u.dst); + rt = (struct rtable *)skb->dst; + skb->dst = odst; + } + + if (rt->u.dst.error) { + dst_release(&rt->u.dst); + rt = NULL; + } return rt; } @@ -123,7 +151,7 @@ ip_decrease_ttl((*pskb)->nh.iph); } - if ((rt = route_mirror(*pskb)) == NULL) + if ((rt = route_mirror(*pskb, hooknum == NF_IP_LOCAL_IN)) == NULL) return NF_DROP; hh_len = (rt->u.dst.dev->hard_header_len + 15) & ~15; diff -Nru --exclude .depend --exclude '*.o' --exclude '*.ko' --exclude '*.ver' --exclude '.*.flags' --exclude '*.orig' --exclude '*.rej' --exclude '*.cmd' --exclude '*.mod.c' --exclude '*~' linux-2.6.0-test1-nftest2/net/ipv4/netfilter/ipt_REJECT.c linux-2.6.0-test1-nftest3/net/ipv4/netfilter/ipt_REJECT.c --- linux-2.6.0-test1-nftest2/net/ipv4/netfilter/ipt_REJECT.c 2003-07-19 14:14:23.000000000 +0200 +++ linux-2.6.0-test1-nftest3/net/ipv4/netfilter/ipt_REJECT.c 2003-07-19 16:13:32.000000000 +0200 @@ -35,6 +35,46 @@ } } +static inline struct rtable *route_reverse(struct sk_buff *skb, int local) +{ + struct iphdr *iph = skb->nh.iph; + struct dst_entry *odst; + struct flowi fl = {}; + struct rtable *rt; + + if (local) { + fl.nl_u.ip4_u.daddr = iph->saddr; + fl.nl_u.ip4_u.saddr = iph->daddr; + fl.nl_u.ip4_u.tos = RT_TOS(iph->tos); + + if (ip_route_output_key(&rt, &fl) != 0) + return NULL; + } else { + /* non-local src, find valid iif to satisfy + * rp-filter when calling ip_route_input. */ + fl.nl_u.ip4_u.daddr = iph->daddr; + if (ip_route_output_key(&rt, &fl) != 0) + return NULL; + + odst = skb->dst; + if (ip_route_input(skb, iph->saddr, iph->daddr, + RT_TOS(iph->tos), rt->u.dst.dev) != 0) { + dst_release(&rt->u.dst); + return NULL; + } + dst_release(&rt->u.dst); + rt = (struct rtable *)skb->dst; + skb->dst = odst; + } + + if (rt->u.dst.error) { + dst_release(&rt->u.dst); + rt = NULL; + } + + return rt; +} + /* Send RST reply */ static void send_reset(struct sk_buff *oldskb, int local) { @@ -69,18 +109,9 @@ csum_partial((char *)otcph, otcplen, 0)) != 0) return; - { - struct flowi fl = { .nl_u = { .ip4_u = - { .daddr = oldskb->nh.iph->saddr, - .saddr = (local ? - oldskb->nh.iph->daddr : - 0), - .tos = RT_TOS(oldskb->nh.iph->tos) } } }; - - /* Routing: if not headed for us, route won't like source */ - if (ip_route_output_key(&rt, &fl)) + if ((rt = route_reverse(oldskb, local)) == NULL) return; - } + hh_len = (rt->u.dst.dev->hard_header_len + 15)&~15; /* Copy skb (even if skb is about to be dropped, we can't just --- linux-2.6.0-test1-nftest2/net/core/netfilter.c 2003-07-14 05:33:59.000000000 +0200 +++ linux-2.6.0-test1-nftest3/net/core/netfilter.c 2003-07-19 16:21:26.000000000 +0200 @@ -625,66 +625,62 @@ { struct iphdr *iph = (*pskb)->nh.iph; struct rtable *rt; - struct flowi fl = { .nl_u = { .ip4_u = - { .daddr = iph->daddr, - .saddr = iph->saddr, - .tos = RT_TOS(iph->tos)|RTO_CONN, + struct flowi fl = {}; + struct dst_entry *odst; + unsigned int hh_len; + + /* some non-standard hacks like ipt_REJECT.c:send_reset() can cause + * packets with foreign saddr to appear on the NF_IP_LOCAL_OUT hook. + */ + if (inet_addr_type(iph->saddr) == RTN_LOCAL) { + fl.nl_u.ip4_u.daddr = iph->daddr; + fl.nl_u.ip4_u.saddr = iph->saddr; + fl.nl_u.ip4_u.tos = RT_TOS(iph->tos); + fl.oif = (*pskb)->sk ? (*pskb)->sk->sk_bound_dev_if : 0; #ifdef CONFIG_IP_ROUTE_FWMARK - .fwmark = (*pskb)->nfmark + fl.nl_u.ip4_u.fwmark = (*pskb)->nfmark; #endif - } }, - .oif = (*pskb)->sk ? (*pskb)->sk->sk_bound_dev_if : 0, - }; - struct net_device *dev_src = NULL; - int err; - - /* accommodate ip_route_output_slow(), which expects the key src to be - 0 or a local address; however some non-standard hacks like - ipt_REJECT.c:send_reset() can cause packets with foreign - saddr to be appear on the NF_IP_LOCAL_OUT hook -MB */ - if(fl.fl4_src && !(dev_src = ip_dev_find(fl.fl4_src))) - fl.fl4_src = 0; - - if ((err=ip_route_output_key(&rt, &fl)) != 0) { - printk("route_me_harder: ip_route_output_key(dst=%u.%u.%u.%u, src=%u.%u.%u.%u, oif=%d, tos=0x%x, fwmark=0x%lx) error %d\n", - NIPQUAD(iph->daddr), NIPQUAD(iph->saddr), - (*pskb)->sk ? (*pskb)->sk->sk_bound_dev_if : 0, - RT_TOS(iph->tos)|RTO_CONN, -#ifdef CONFIG_IP_ROUTE_FWMARK - (*pskb)->nfmark, -#else - 0UL, -#endif - err); - goto out; - } - - /* Drop old route. */ - dst_release((*pskb)->dst); + if (ip_route_output_key(&rt, &fl) != 0) + return -1; - (*pskb)->dst = &rt->u.dst; + /* Drop old route. */ + dst_release((*pskb)->dst); + (*pskb)->dst = &rt->u.dst; + } else { + /* non-local src, find valid iif to satisfy + * rp-filter when calling ip_route_input. */ + fl.nl_u.ip4_u.daddr = iph->saddr; + if (ip_route_output_key(&rt, &fl) != 0) + return -1; + + odst = (*pskb)->dst; + if (ip_route_input(*pskb, iph->daddr, iph->saddr, + RT_TOS(iph->tos), rt->u.dst.dev) != 0) { + dst_release(&rt->u.dst); + return -1; + } + dst_release(&rt->u.dst); + dst_release(odst); + } + + if ((*pskb)->dst->error) + return -1; /* Change in oif may mean change in hh_len. */ - if (skb_headroom(*pskb) < (*pskb)->dst->dev->hard_header_len) { + hh_len = (*pskb)->dst->dev->hard_header_len; + if (skb_headroom(*pskb) < hh_len) { struct sk_buff *nskb; - nskb = skb_realloc_headroom(*pskb, - (*pskb)->dst->dev->hard_header_len); - if (!nskb) { - err = -ENOMEM; - goto out; - } + nskb = skb_realloc_headroom(*pskb, hh_len); + if (!nskb) + return -1; if ((*pskb)->sk) skb_set_owner_w(nskb, (*pskb)->sk); kfree_skb(*pskb); *pskb = nskb; } -out: - if (dev_src) - dev_put(dev_src); - - return err; + return 0; } int skb_ip_make_writable(struct sk_buff **pskb, unsigned int writable_len) -- - Harald Welte http://www.netfilter.org/ ============================================================================ "Fragmentation is like classful addressing -- an interesting early architectural error that shows how much experimentation was going on while IP was being designed." -- Paul Vixie