]> git.hungrycats.org Git - linux/commitdiff
[NETFILTER]: Re-sync ipt_REJECT with 2.4.x
authorHarald Welte <laforge@netfilter.org>
Fri, 25 Jul 2003 08:14:44 +0000 (01:14 -0700)
committerDavid S. Miller <davem@nuts.ninka.net>
Fri, 25 Jul 2003 08:14:44 +0000 (01:14 -0700)
net/ipv4/netfilter/ipt_REJECT.c

index 2582a825f49979912ac736e2e7b73119f09b6182..b0b236642ab78c58fc9e10bfd654ecd53e472fc4 100644 (file)
@@ -36,133 +36,148 @@ static void connection_attach(struct sk_buff *new_skb, struct nf_ct_info *nfct)
 }
 
 /* Send RST reply */
-static unsigned int send_reset(struct sk_buff **pskb, int local)
+static void send_reset(struct sk_buff *oldskb, int local)
 {
-       struct tcphdr tcph;
+       struct sk_buff *nskb;
+       struct tcphdr *otcph, *tcph;
        struct rtable *rt;
+       unsigned int otcplen;
        u_int16_t tmp_port;
        u_int32_t tmp_addr;
-       int needs_ack, hh_len, datalen;
-       struct nf_ct_info *oldnfct;
+       int needs_ack;
+       int hh_len;
 
-       /* No RSTs for fragments. */
-       if ((*pskb)->nh.iph->frag_off & htons(IP_OFFSET))
-               return NF_DROP;
+       /* IP header checks: fragment, too short. */
+       if (oldskb->nh.iph->frag_off & htons(IP_OFFSET)
+           || oldskb->len < (oldskb->nh.iph->ihl<<2) + sizeof(struct tcphdr))
+               return;
 
-       if (skb_copy_bits(*pskb, (*pskb)->nh.iph->ihl*4,
-                         &tcph, sizeof(tcph)) < 0)
-               return NF_DROP;
+       otcph = (struct tcphdr *)((u_int32_t*)oldskb->nh.iph + oldskb->nh.iph->ihl);
+       otcplen = oldskb->len - oldskb->nh.iph->ihl*4;
+
+       if (skb_copy_bits(oldskb, oldskb->nh.iph->ihl*4,
+                         otcph, sizeof(*otcph)) < 0)
+               return;
 
        /* No RST for RST. */
-       if (tcph.rst)
-               return NF_DROP;
+       if (otcph->rst)
+               return;
+
+       /* Check checksum. */
+       if (tcp_v4_check(otcph, otcplen, oldskb->nh.iph->saddr,
+                        oldskb->nh.iph->daddr,
+                        csum_partial((char *)otcph, otcplen, 0)) != 0)
+               return;
 
-       /* FIXME: Check checksum. */
        {
                struct flowi fl = { .nl_u = { .ip4_u =
-                                             { .daddr = (*pskb)->nh.iph->saddr,
+                                             { .daddr = oldskb->nh.iph->saddr,
                                                .saddr = (local ?
-                                                         (*pskb)->nh.iph->daddr :
+                                                         oldskb->nh.iph->daddr :
                                                          0),
-                                               .tos = RT_TOS((*pskb)->nh.iph->tos) } } };
-
-               /* Routing: if not headed for us, route won't like source */
-               if (ip_route_output_key(&rt, &fl))
-                       return NF_DROP;
+                                               .tos = RT_TOS(oldskb->nh.iph->tos) } } };
 
-               hh_len = (rt->u.dst.dev->hard_header_len + 15)&~15;
-       }
+       /* Routing: if not headed for us, route won't like source */
+       if (ip_route_output_key(&rt, &fl))
+               return;
+       }               
+       hh_len = (rt->u.dst.dev->hard_header_len + 15)&~15;
 
-       /* We're going to flip the header around, drop options and data. */
-       if (!skb_ip_make_writable(pskb, (*pskb)->nh.iph->ihl*4+sizeof(tcph))) {
-               ip_rt_put(rt);
-               return NF_DROP;
+       /* Copy skb (even if skb is about to be dropped, we can't just
+           clone it because there may be other things, such as tcpdump,
+           interested in it). We also need to expand headroom in case
+          hh_len of incoming interface < hh_len of outgoing interface */
+       nskb = skb_copy_expand(oldskb, hh_len, skb_tailroom(oldskb),
+                              GFP_ATOMIC);
+       if (!nskb) {
+               dst_release(&rt->u.dst);
+               return;
        }
 
-       (*pskb)->h.th = (void *)(*pskb)->nh.iph + sizeof(tcph);
-       datalen = (*pskb)->len - (*pskb)->nh.iph->ihl*4 - tcph.doff*4;
-
-       /* Change over route. */
-       dst_release((*pskb)->dst);
-       (*pskb)->dst = &rt->u.dst;
+       dst_release(nskb->dst);
+       nskb->dst = &rt->u.dst;
 
        /* This packet will not be the same as the other: clear nf fields */
-       (*pskb)->nfcache = 0;
+       nf_conntrack_put(nskb->nfct);
+       nskb->nfct = NULL;
+       nskb->nfcache = 0;
 #ifdef CONFIG_NETFILTER_DEBUG
-       (*pskb)->nf_debug = 0;
+       nskb->nf_debug = 0;
 #endif
-       (*pskb)->nfmark = 0;
+       nskb->nfmark = 0;
+
+       tcph = (struct tcphdr *)((u_int32_t*)nskb->nh.iph + nskb->nh.iph->ihl);
 
        /* Swap source and dest */
-       tmp_addr = (*pskb)->nh.iph->saddr;
-       (*pskb)->nh.iph->saddr = (*pskb)->nh.iph->daddr;
-       (*pskb)->nh.iph->daddr = tmp_addr;
-       tmp_port = (*pskb)->h.th->source;
-       (*pskb)->h.th->source = (*pskb)->h.th->dest;
-       (*pskb)->h.th->dest = tmp_port;
+       tmp_addr = nskb->nh.iph->saddr;
+       nskb->nh.iph->saddr = nskb->nh.iph->daddr;
+       nskb->nh.iph->daddr = tmp_addr;
+       tmp_port = tcph->source;
+       tcph->source = tcph->dest;
+       tcph->dest = tmp_port;
 
        /* Truncate to length (no data) */
-       (*pskb)->h.th->doff = sizeof(struct tcphdr)/4;
-       skb_trim(*pskb, (*pskb)->nh.iph->ihl*4 + sizeof(struct tcphdr));
-       (*pskb)->nh.iph->tot_len = htons((*pskb)->len);
+       tcph->doff = sizeof(struct tcphdr)/4;
+       skb_trim(nskb, nskb->nh.iph->ihl*4 + sizeof(struct tcphdr));
+       nskb->nh.iph->tot_len = htons(nskb->len);
 
-       if ((*pskb)->h.th->ack) {
+       if (tcph->ack) {
                needs_ack = 0;
-               (*pskb)->h.th->seq = tcph.ack_seq;
-               (*pskb)->h.th->ack_seq = 0;
+               tcph->seq = otcph->ack_seq;
+               tcph->ack_seq = 0;
        } else {
                needs_ack = 1;
-               (*pskb)->h.th->ack_seq = htonl(ntohl(tcph.seq)
-                                                + tcph.syn + tcph.fin
-                                                + datalen);
-               (*pskb)->h.th->seq = 0;
+               tcph->ack_seq = htonl(ntohl(otcph->seq) + otcph->syn + otcph->fin
+                                     + otcplen - (otcph->doff<<2));
+               tcph->seq = 0;
        }
 
        /* Reset flags */
-       memset((*pskb)->h.raw + 13, 0, 1);
-       (*pskb)->h.th->rst = 1;
-       (*pskb)->h.th->ack = needs_ack;
+       ((u_int8_t *)tcph)[13] = 0;
+       tcph->rst = 1;
+       tcph->ack = needs_ack;
 
-       (*pskb)->h.th->window = 0;
-       (*pskb)->h.th->urg_ptr = 0;
+       tcph->window = 0;
+       tcph->urg_ptr = 0;
 
        /* Adjust TCP checksum */
-       (*pskb)->h.th->check = 0;
-       (*pskb)->h.th->check
-               = tcp_v4_check((*pskb)->h.th,
-                              sizeof(struct tcphdr),
-                              (*pskb)->nh.iph->saddr,
-                              (*pskb)->nh.iph->daddr,
-                              csum_partial((*pskb)->h.raw,
-                                           sizeof(struct tcphdr), 0));
+       tcph->check = 0;
+       tcph->check = tcp_v4_check(tcph, sizeof(struct tcphdr),
+                                  nskb->nh.iph->saddr,
+                                  nskb->nh.iph->daddr,
+                                  csum_partial((char *)tcph,
+                                               sizeof(struct tcphdr), 0));
 
        /* Adjust IP TTL, DF */
-       (*pskb)->nh.iph->ttl = MAXTTL;
+       nskb->nh.iph->ttl = MAXTTL;
        /* Set DF, id = 0 */
-       (*pskb)->nh.iph->frag_off = htons(IP_DF);
-       (*pskb)->nh.iph->id = 0;
+       nskb->nh.iph->frag_off = htons(IP_DF);
+       nskb->nh.iph->id = 0;
 
        /* Adjust IP checksum */
-       (*pskb)->nh.iph->check = 0;
-       (*pskb)->nh.iph->check = ip_fast_csum((*pskb)->nh.raw, 
-                                             (*pskb)->nh.iph->ihl);
+       nskb->nh.iph->check = 0;
+       nskb->nh.iph->check = ip_fast_csum((unsigned char *)nskb->nh.iph, 
+                                          nskb->nh.iph->ihl);
 
        /* "Never happens" */
-       if ((*pskb)->len > dst_pmtu((*pskb)->dst))
-               return NF_DROP;
+       if (nskb->len > dst_pmtu(nskb->dst))
+               goto free_nskb;
 
-       /* Related to old connection. */
-       oldnfct = (*pskb)->nfct;
-       connection_attach(*pskb, oldnfct);
-       nf_conntrack_put(oldnfct);
+       connection_attach(nskb, oldskb->nfct);
 
-       NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, *pskb, NULL, (*pskb)->dst->dev,
+       NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, nskb, NULL, nskb->dst->dev,
                ip_finish_output);
-       return NF_STOLEN;
+       return;
+
+ free_nskb:
+       kfree_skb(nskb);
 }
 
-static void send_unreach(const struct sk_buff *skb_in, int code)
+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;
        u8 tos;
@@ -177,6 +192,8 @@ static void send_unreach(const struct sk_buff *skb_in, int code)
        if (!xrlim_allow(&rt->u.dst, 1*HZ))
                return;
 
+       iph = skb_in->nh.iph;
+
        /* No replies to physical multicast/broadcast */
        if (skb_in->pkt_type!=PACKET_HOST)
                return;
@@ -186,38 +203,52 @@ static void send_unreach(const struct sk_buff *skb_in, int code)
                return;
 
        /* Only reply to fragment 0. */
-       if (skb_in->nh.iph->frag_off&htons(IP_OFFSET))
+       if (iph->frag_off&htons(IP_OFFSET))
                return;
 
        /* Ensure we have at least 8 bytes of proto header. */
        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 (skb_in->nh.iph->protocol == IPPROTO_ICMP) {
-               struct icmphdr icmph;
+       if (iph->protocol == IPPROTO_ICMP
+           && skb_in->tail-(u8*)iph >= sizeof(struct icmphdr)) {
+               icmph = (struct icmphdr *)((char *)iph + (iph->ihl<<2));
 
                if (skb_copy_bits(skb_in, skb_in->nh.iph->ihl*4,
-                                 &icmph, sizeof(icmph)) < 0)
+                                 icmph, sizeof(*icmph)) < 0)
                        return;
 
                /* Between echo-reply (0) and timestamp (13),
                   everything except echo-request (8) is an error.
                   Also, anything greater than NR_ICMP_TYPES is
                   unknown, and hence should be treated as an error... */
-               if ((icmph.type < ICMP_TIMESTAMP
-                    && icmph.type != ICMP_ECHOREPLY
-                    && icmph.type != ICMP_ECHO)
-                   || icmph.type > NR_ICMP_TYPES)
+               if ((icmph->type < ICMP_TIMESTAMP
+                    && icmph->type != ICMP_ECHOREPLY
+                    && icmph->type != ICMP_ECHO)
+                   || icmph->type > NR_ICMP_TYPES)
                        return;
        }
 
-       saddr = skb_in->nh.iph->daddr;
+       saddr = iph->daddr;
        if (!(rt->rt_flags & RTCF_LOCAL))
                saddr = 0;
 
-       tos = (skb_in->nh.iph->tos & IPTOS_TOS_MASK)
-               | IPTOS_PREC_INTERNETCONTROL;
+       tos = (iph->tos & IPTOS_TOS_MASK) | IPTOS_PREC_INTERNETCONTROL;
+
        {
                struct flowi fl = { .nl_u = { .ip4_u =
                                              { .daddr = skb_in->nh.iph->saddr,
@@ -247,38 +278,41 @@ static void send_unreach(const struct sk_buff *skb_in, int code)
        skb_reserve(nskb, hh_len);
 
        /* Set up IP header */
-       nskb->nh.iph = (struct iphdr *)skb_put(nskb, sizeof(struct iphdr));
-       nskb->nh.iph->version=4;
-       nskb->nh.iph->ihl=5;
-       nskb->nh.iph->tos=tos;
-       nskb->nh.iph->tot_len = htons(length);
+       iph = nskb->nh.iph
+               = (struct iphdr *)skb_put(nskb, sizeof(struct iphdr));
+       iph->version=4;
+       iph->ihl=5;
+       iph->tos=tos;
+       iph->tot_len = htons(length);
 
        /* PMTU discovery never applies to ICMP packets. */
-       nskb->nh.iph->frag_off = 0;
+       iph->frag_off = 0;
 
-       nskb->nh.iph->ttl = MAXTTL;
-       ip_select_ident(nskb->nh.iph, &rt->u.dst, NULL);
-       nskb->nh.iph->protocol=IPPROTO_ICMP;
-       nskb->nh.iph->saddr=rt->rt_src;
-       nskb->nh.iph->daddr=rt->rt_dst;
-       nskb->nh.iph->check=0;
-       nskb->nh.iph->check = ip_fast_csum(nskb->nh.raw,
-                                          nskb->nh.iph->ihl);
+       iph->ttl = MAXTTL;
+       ip_select_ident(iph, &rt->u.dst, NULL);
+       iph->protocol=IPPROTO_ICMP;
+       iph->saddr=rt->rt_src;
+       iph->daddr=rt->rt_dst;
+       iph->check=0;
+       iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
 
        /* Set up ICMP header. */
-       nskb->h.icmph = (struct icmphdr *)skb_put(nskb,sizeof(struct icmphdr));
-       nskb->h.icmph->type = ICMP_DEST_UNREACH;
-       nskb->h.icmph->code = code;     
-       nskb->h.icmph->un.gateway = 0;
-       nskb->h.icmph->checksum = 0;
+       icmph = nskb->h.icmph
+               = (struct icmphdr *)skb_put(nskb, sizeof(struct icmphdr));
+       icmph->type = ICMP_DEST_UNREACH;
+       icmph->code = code;     
+       icmph->un.gateway = 0;
+       icmph->checksum = 0;
        
        /* Copy as much of original packet as will fit */
        data = skb_put(nskb,
                       length - sizeof(struct iphdr) - sizeof(struct icmphdr));
+
        skb_copy_bits(skb_in, 0, data,
                      length - sizeof(struct iphdr) - sizeof(struct icmphdr));
-       nskb->h.icmph->checksum = ip_compute_csum(nskb->h.raw,
-                                                 length-sizeof(struct iphdr));
+
+       icmph->checksum = ip_compute_csum((unsigned char *)icmph,
+                                         length - sizeof(struct iphdr));
 
        connection_attach(nskb, skb_in->nfct);
 
@@ -323,7 +357,7 @@ static unsigned int reject(struct sk_buff **pskb,
                send_unreach(*pskb, ICMP_HOST_ANO);
                break;
        case IPT_TCP_RESET:
-               return send_reset(pskb, hooknum == NF_IP_LOCAL_IN);
+               send_reset(*pskb, hooknum == NF_IP_LOCAL_IN);
        case IPT_ICMP_ECHOREPLY:
                /* Doesn't happen. */
                break;