X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=net%2Fipv4%2Fnetfilter%2Fipt_REJECT.c;h=6714bd19a86ee7fbd9c6b9077f68b6ae920357e8;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=f6183f968b2b93d7a5d4f9099f92c48f33c0f7da;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/net/ipv4/netfilter/ipt_REJECT.c b/net/ipv4/netfilter/ipt_REJECT.c index f6183f968..6714bd19a 100644 --- a/net/ipv4/netfilter/ipt_REJECT.c +++ b/net/ipv4/netfilter/ipt_REJECT.c @@ -12,7 +12,6 @@ * published by the Free Software Foundation. */ -#include #include #include #include @@ -22,6 +21,7 @@ #include #include #include +#include #include #include #ifdef CONFIG_BRIDGE_NETFILTER @@ -38,121 +38,50 @@ 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 nf_ct_info *nfct) -{ - void (*attach)(struct sk_buff *, struct nf_ct_info *); - - /* Avoid module unload race with ip_ct_attach being NULLed out */ - if (nfct && (attach = ip_ct_attach) != NULL) { - mb(); /* Just to be sure: must be read before executing this */ - attach(new_skb, nfct); - } -} - -static inline struct rtable *route_reverse(struct sk_buff *skb, int hook) -{ - struct iphdr *iph = skb->nh.iph; - struct dst_entry *odst; - struct flowi fl = {}; - struct rtable *rt; - - /* We don't require ip forwarding to be enabled to be able to - * send a RST reply for bridged traffic. */ - if (hook != NF_IP_FORWARD -#ifdef CONFIG_BRIDGE_NETFILTER - || (skb->nf_bridge && skb->nf_bridge->mask & BRNF_BRIDGED) -#endif - ) { - fl.nl_u.ip4_u.daddr = iph->saddr; - if (hook == NF_IP_LOCAL_IN) - 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 hook) { struct sk_buff *nskb; - struct tcphdr otcph, *tcph; - struct rtable *rt; - u_int16_t tmp_port; - u_int32_t tmp_addr; + struct iphdr *iph = oldskb->nh.iph; + struct tcphdr _otcph, *oth, *tcph; + __be16 tmp_port; + __be32 tmp_addr; int needs_ack; - int hh_len; + unsigned int addr_type; /* IP header checks: fragment. */ if (oldskb->nh.iph->frag_off & htons(IP_OFFSET)) return; - if (skb_copy_bits(oldskb, oldskb->nh.iph->ihl*4, - &otcph, sizeof(otcph)) < 0) + oth = skb_header_pointer(oldskb, oldskb->nh.iph->ihl * 4, + sizeof(_otcph), &_otcph); + if (oth == NULL) return; /* No RST for RST. */ - if (otcph.rst) + if (oth->rst) return; - /* FIXME: Check checksum --RR */ - if ((rt = route_reverse(oldskb, hook)) == NULL) + /* Check checksum */ + if (nf_ip_checksum(oldskb, hook, iph->ihl * 4, IPPROTO_TCP)) return; - hh_len = LL_RESERVED_SPACE(rt->u.dst.dev); - /* We need a linear, writeable skb. 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), + nskb = skb_copy_expand(oldskb, LL_MAX_HEADER, skb_tailroom(oldskb), GFP_ATOMIC); - if (!nskb) { - dst_release(&rt->u.dst); + if (!nskb) return; - } - - dst_release(nskb->dst); - nskb->dst = &rt->u.dst; /* This packet will not be the same as the other: clear nf fields */ - nf_conntrack_put(nskb->nfct); - nskb->nfct = NULL; - nskb->nfcache = 0; -#ifdef CONFIG_NETFILTER_DEBUG - nskb->nf_debug = 0; -#endif - nskb->nfmark = 0; -#ifdef CONFIG_BRIDGE_NETFILTER - nf_bridge_put(nskb->nf_bridge); - nskb->nf_bridge = NULL; -#endif + nf_reset(nskb); + nskb->mark = 0; + skb_init_secmark(nskb); + + skb_shinfo(nskb)->gso_size = 0; + skb_shinfo(nskb)->gso_segs = 0; + skb_shinfo(nskb)->gso_type = 0; tcph = (struct tcphdr *)((u_int32_t*)nskb->nh.iph + nskb->nh.iph->ihl); @@ -171,13 +100,13 @@ static void send_reset(struct sk_buff *oldskb, int hook) if (tcph->ack) { needs_ack = 0; - tcph->seq = otcph.ack_seq; + tcph->seq = oth->ack_seq; tcph->ack_seq = 0; } else { needs_ack = 1; - tcph->ack_seq = htonl(ntohl(otcph.seq) + otcph.syn + otcph.fin + tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + oth->fin + oldskb->len - oldskb->nh.iph->ihl*4 - - (otcph.doff<<2)); + - (oth->doff<<2)); tcph->seq = 0; } @@ -197,184 +126,56 @@ static void send_reset(struct sk_buff *oldskb, int hook) csum_partial((char *)tcph, sizeof(struct tcphdr), 0)); - /* Adjust IP TTL, DF */ - nskb->nh.iph->ttl = MAXTTL; /* Set DF, id = 0 */ nskb->nh.iph->frag_off = htons(IP_DF); nskb->nh.iph->id = 0; + addr_type = RTN_UNSPEC; + if (hook != NF_IP_FORWARD +#ifdef CONFIG_BRIDGE_NETFILTER + || (nskb->nf_bridge && nskb->nf_bridge->mask & BRNF_BRIDGED) +#endif + ) + addr_type = RTN_LOCAL; + + if (ip_route_me_harder(&nskb, addr_type)) + goto free_nskb; + + nskb->ip_summed = CHECKSUM_NONE; + + /* Adjust IP TTL */ + nskb->nh.iph->ttl = dst_metric(nskb->dst, RTAX_HOPLIMIT); + /* Adjust IP checksum */ nskb->nh.iph->check = 0; nskb->nh.iph->check = ip_fast_csum((unsigned char *)nskb->nh.iph, nskb->nh.iph->ihl); /* "Never happens" */ - if (nskb->len > dst_pmtu(nskb->dst)) + if (nskb->len > dst_mtu(nskb->dst)) goto free_nskb; - connection_attach(nskb, oldskb->nfct); + nf_ct_attach(nskb, oldskb); NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, nskb, NULL, nskb->dst->dev, - ip_finish_output); + dst_output); return; free_nskb: kfree_skb(nskb); } -static void send_unreach(struct sk_buff *skb_in, int code) +static inline 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; - int hh_len, length; - struct rtable *rt = (struct rtable*)skb_in->dst; - unsigned char *data; - - if (!rt) - return; - - /* FIXME: Use sysctl number. --RR */ - 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; - - /* Now check at the protocol level */ - if (rt->rt_flags&(RTCF_BROADCAST|RTCF_MULTICAST)) - return; - - /* Only reply to fragment 0. */ - 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 (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) - 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) - return; - } - - saddr = iph->daddr; - if (!(rt->rt_flags & RTCF_LOCAL)) - saddr = 0; - - 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) } } }; - if (ip_route_output_key(&rt, &fl)) - return; - } - /* RFC says return as much as we can without exceeding 576 bytes. */ - length = skb_in->len + sizeof(struct iphdr) + sizeof(struct icmphdr); - - if (length > dst_pmtu(&rt->u.dst)) - length = dst_pmtu(&rt->u.dst); - if (length > 576) - length = 576; - - hh_len = LL_RESERVED_SPACE(rt->u.dst.dev); - - nskb = alloc_skb(hh_len + length, GFP_ATOMIC); - if (!nskb) { - ip_rt_put(rt); - return; - } - - nskb->priority = 0; - nskb->dst = &rt->u.dst; - skb_reserve(nskb, hh_len); - - /* Set up IP header */ - 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. */ - iph->frag_off = 0; - - 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. */ - 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)); - - icmph->checksum = ip_compute_csum((unsigned char *)icmph, - length - sizeof(struct iphdr)); - - connection_attach(nskb, skb_in->nfct); - - NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, nskb, NULL, nskb->dst->dev, - ip_finish_output); + icmp_send(skb_in, ICMP_DEST_UNREACH, code, 0); } static unsigned int reject(struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, unsigned int hooknum, - const void *targinfo, - void *userinfo) + const struct xt_target *target, + const void *targinfo) { const struct ipt_reject_info *reject = targinfo; @@ -419,29 +220,13 @@ static unsigned int reject(struct sk_buff **pskb, } static int check(const char *tablename, - const struct ipt_entry *e, + const void *e_void, + const struct xt_target *target, void *targinfo, - unsigned int targinfosize, unsigned int hook_mask) { const struct ipt_reject_info *rejinfo = targinfo; - - if (targinfosize != IPT_ALIGN(sizeof(struct ipt_reject_info))) { - DEBUGP("REJECT: targinfosize %u != 0\n", targinfosize); - return 0; - } - - /* Only allow these for packet filtering. */ - if (strcmp(tablename, "filter") != 0) { - DEBUGP("REJECT: bad table `%s'.\n", tablename); - return 0; - } - if ((hook_mask & ~((1 << NF_IP_LOCAL_IN) - | (1 << NF_IP_FORWARD) - | (1 << NF_IP_LOCAL_OUT))) != 0) { - DEBUGP("REJECT: bad hook mask %X\n", hook_mask); - return 0; - } + const struct ipt_entry *e = e_void; if (rejinfo->with == IPT_ICMP_ECHOREPLY) { printk("REJECT: ECHOREPLY no longer supported.\n"); @@ -454,26 +239,29 @@ static int check(const char *tablename, return 0; } } - return 1; } static struct ipt_target ipt_reject_reg = { .name = "REJECT", .target = reject, + .targetsize = sizeof(struct ipt_reject_info), + .table = "filter", + .hooks = (1 << NF_IP_LOCAL_IN) | (1 << NF_IP_FORWARD) | + (1 << NF_IP_LOCAL_OUT), .checkentry = check, .me = THIS_MODULE, }; -static int __init init(void) +static int __init ipt_reject_init(void) { return ipt_register_target(&ipt_reject_reg); } -static void __exit fini(void) +static void __exit ipt_reject_fini(void) { ipt_unregister_target(&ipt_reject_reg); } -module_init(init); -module_exit(fini); +module_init(ipt_reject_init); +module_exit(ipt_reject_fini);