vserver 1.9.5.x5
[linux-2.6.git] / net / ipv4 / netfilter / ipt_REJECT.c
index b2e0574..7593090 100644 (file)
@@ -22,6 +22,7 @@
 #include <net/ip.h>
 #include <net/tcp.h>
 #include <net/route.h>
+#include <net/dst.h>
 #include <linux/netfilter_ipv4/ip_tables.h>
 #include <linux/netfilter_ipv4/ipt_REJECT.h>
 #ifdef CONFIG_BRIDGE_NETFILTER
@@ -38,21 +39,8 @@ MODULE_DESCRIPTION("iptables REJECT target module");
 #define DEBUGP(format, args...)
 #endif
 
-/* If the original packet is part of a connection, but the connection
-   is not confirmed, our manufactured reply will not be associated
-   with it, so we need to do this manually. */
-static void connection_attach(struct sk_buff *new_skb, struct sk_buff *skb)
-{
-       void (*attach)(struct sk_buff *, struct sk_buff *);
-
-       /* Avoid module unload race with ip_ct_attach being NULLed out */
-       if (skb->nfct && (attach = ip_ct_attach) != NULL) {
-               mb(); /* Just to be sure: must be read before executing this */
-               attach(new_skb, skb);
-       }
-}
-
-static inline struct rtable *route_reverse(struct sk_buff *skb, int hook)
+static inline struct rtable *route_reverse(struct sk_buff *skb, 
+                                          struct tcphdr *tcph, int hook)
 {
        struct iphdr *iph = skb->nh.iph;
        struct dst_entry *odst;
@@ -89,9 +77,22 @@ static inline struct rtable *route_reverse(struct sk_buff *skb, int hook)
                dst_release(&rt->u.dst);
                rt = (struct rtable *)skb->dst;
                skb->dst = odst;
+
+               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 (rt->u.dst.error) {
+               dst_release(&rt->u.dst);
+               return NULL;
+       }
+
+       fl.proto = IPPROTO_TCP;
+       fl.fl_ip_sport = tcph->dest;
+       fl.fl_ip_dport = tcph->source;
+
+       if (xfrm_lookup((struct dst_entry **)&rt, &fl, NULL, 0)) {
                dst_release(&rt->u.dst);
                rt = NULL;
        }
@@ -124,7 +125,7 @@ static void send_reset(struct sk_buff *oldskb, int hook)
                return;
 
        /* FIXME: Check checksum --RR */
-       if ((rt = route_reverse(oldskb, hook)) == NULL)
+       if ((rt = route_reverse(oldskb, oth, hook)) == NULL)
                return;
 
        hh_len = LL_RESERVED_SPACE(rt->u.dst.dev);
@@ -209,7 +210,7 @@ static void send_reset(struct sk_buff *oldskb, int hook)
        if (nskb->len > dst_pmtu(nskb->dst))
                goto free_nskb;
 
-       connection_attach(nskb, oldskb);
+       nf_ct_attach(nskb, oldskb);
 
        NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, nskb, NULL, nskb->dst->dev,
                ip_finish_output);
@@ -222,7 +223,6 @@ static void send_reset(struct sk_buff *oldskb, int hook)
 static void send_unreach(struct sk_buff *skb_in, int code)
 {
        struct iphdr *iph;
-       struct udphdr *udph;
        struct icmphdr *icmph;
        struct sk_buff *nskb;
        u32 saddr;
@@ -256,26 +256,13 @@ static void send_unreach(struct sk_buff *skb_in, int code)
        if (skb_in->len < skb_in->nh.iph->ihl*4 + 8)
                return;
 
-       /* if UDP checksum is set, verify it's correct */
-       if (iph->protocol == IPPROTO_UDP
-           && skb_in->tail-(u8*)iph >= sizeof(struct udphdr)) {
-               int datalen = skb_in->len - (iph->ihl<<2);
-               udph = (struct udphdr *)((char *)iph + (iph->ihl<<2));
-               if (udph->check
-                   && csum_tcpudp_magic(iph->saddr, iph->daddr,
-                                        datalen, IPPROTO_UDP,
-                                        csum_partial((char *)udph, datalen,
-                                                     0)) != 0)
-                       return;
-       }
-
        /* If we send an ICMP error to an ICMP error a mess would result.. */
-       if (iph->protocol == IPPROTO_ICMP
-           && skb_in->tail-(u8*)iph >= sizeof(struct icmphdr)) {
-               icmph = (struct icmphdr *)((char *)iph + (iph->ihl<<2));
+       if (iph->protocol == IPPROTO_ICMP) {
+               struct icmphdr ihdr;
 
-               if (skb_copy_bits(skb_in, skb_in->nh.iph->ihl*4,
-                                 icmph, sizeof(*icmph)) < 0)
+               icmph = skb_header_pointer(skb_in, skb_in->nh.iph->ihl*4,
+                                          sizeof(ihdr), &ihdr);
+               if (!icmph)
                        return;
 
                /* Between echo-reply (0) and timestamp (13),
@@ -296,10 +283,23 @@ static void send_unreach(struct sk_buff *skb_in, int code)
        tos = (iph->tos & IPTOS_TOS_MASK) | IPTOS_PREC_INTERNETCONTROL;
 
        {
-               struct flowi fl = { .nl_u = { .ip4_u =
-                                             { .daddr = skb_in->nh.iph->saddr,
-                                               .saddr = saddr,
-                                               .tos = RT_TOS(tos) } } };
+               struct flowi fl = {
+                       .nl_u = {
+                               .ip4_u = {
+                                       .daddr = skb_in->nh.iph->saddr,
+                                       .saddr = saddr,
+                                       .tos = RT_TOS(tos)
+                               }
+                       },
+                       .proto = IPPROTO_ICMP,
+                       .uli_u = {
+                               .icmpt = {
+                                       .type = ICMP_DEST_UNREACH,
+                                       .code = code
+                               }
+                       }
+               };
+
                if (ip_route_output_key(&rt, &fl))
                        return;
        }
@@ -360,7 +360,7 @@ static void send_unreach(struct sk_buff *skb_in, int code)
        icmph->checksum = ip_compute_csum((unsigned char *)icmph,
                                          length - sizeof(struct iphdr));
 
-       connection_attach(nskb, skb_in);
+       nf_ct_attach(nskb, skb_in);
 
        NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, nskb, NULL, nskb->dst->dev,
                ip_finish_output);