patch-2.4.4 linux/net/ipv4/netfilter/ipt_REJECT.c
Next file: linux/net/ipv4/raw.c
Previous file: linux/net/ipv4/netfilter/ipt_MASQUERADE.c
Back to the patch index
Back to the overall index
- Lines: 241
- Date:
Fri Apr 27 14:15:01 2001
- Orig file:
v2.4.3/linux/net/ipv4/netfilter/ipt_REJECT.c
- Orig date:
Mon Jan 22 13:30:21 2001
diff -u --recursive --new-file v2.4.3/linux/net/ipv4/netfilter/ipt_REJECT.c linux/net/ipv4/netfilter/ipt_REJECT.c
@@ -20,6 +20,18 @@
#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)
+ attach(new_skb, nfct);
+}
+
/* Send RST reply */
static void send_reset(struct sk_buff *oldskb, int local)
{
@@ -128,6 +140,8 @@
if (nskb->len > nskb->dst->pmtu)
goto free_nskb;
+ connection_attach(nskb, oldskb->nfct);
+
NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, nskb, NULL, nskb->dst->dev,
ip_finish_output);
return;
@@ -136,6 +150,127 @@
kfree_skb(nskb);
}
+static void send_unreach(struct sk_buff *skb_in, int code)
+{
+ struct iphdr *iph;
+ 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;
+
+ /* 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));
+ /* 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;
+
+ if (ip_route_output(&rt, iph->saddr, saddr, RT_TOS(tos), 0))
+ 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 > rt->u.dst.pmtu)
+ length = rt->u.dst.pmtu;
+ if (length > 576)
+ length = 576;
+
+ hh_len = (rt->u.dst.dev->hard_header_len + 15)&~15;
+
+ nskb = alloc_skb(hh_len+15+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);
+
+ /* This abbreviates icmp->send->ip_build_xmit->ip_dont_fragment */
+ if (!ipv4_config.no_pmtu_disc
+ && !(rt->u.dst.mxlock&(1<<RTAX_MTU)))
+ iph->frag_off = htons(IP_DF);
+ else 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));
+ /* FIXME: won't work with nonlinear skbs --RR */
+ memcpy(data, skb_in->nh.iph,
+ 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);
+}
+
static unsigned int reject(struct sk_buff **pskb,
unsigned int hooknum,
const struct net_device *in,
@@ -145,51 +280,43 @@
{
const struct ipt_reject_info *reject = targinfo;
+ /* Our naive response construction doesn't deal with IP
+ options, and probably shouldn't try. */
+ if ((*pskb)->nh.iph->ihl<<2 != sizeof(struct iphdr))
+ return NF_DROP;
+
/* WARNING: This code causes reentry within iptables.
This means that the iptables jump stack is now crap. We
must return an absolute verdict. --RR */
switch (reject->with) {
case IPT_ICMP_NET_UNREACHABLE:
- icmp_send(*pskb, ICMP_DEST_UNREACH, ICMP_NET_UNREACH, 0);
+ send_unreach(*pskb, ICMP_NET_UNREACH);
break;
case IPT_ICMP_HOST_UNREACHABLE:
- icmp_send(*pskb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0);
+ send_unreach(*pskb, ICMP_HOST_UNREACH);
break;
case IPT_ICMP_PROT_UNREACHABLE:
- icmp_send(*pskb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0);
+ send_unreach(*pskb, ICMP_PROT_UNREACH);
break;
case IPT_ICMP_PORT_UNREACHABLE:
- icmp_send(*pskb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
+ send_unreach(*pskb, ICMP_PORT_UNREACH);
break;
case IPT_ICMP_NET_PROHIBITED:
- icmp_send(*pskb, ICMP_DEST_UNREACH, ICMP_NET_ANO, 0);
+ send_unreach(*pskb, ICMP_NET_ANO);
break;
case IPT_ICMP_HOST_PROHIBITED:
- icmp_send(*pskb, ICMP_DEST_UNREACH, ICMP_HOST_ANO, 0);
+ send_unreach(*pskb, ICMP_HOST_ANO);
break;
- case IPT_ICMP_ECHOREPLY:
- printk("REJECT: ECHOREPLY no longer supported.\n");
- break;
case IPT_TCP_RESET:
send_reset(*pskb, hooknum == NF_IP_LOCAL_IN);
break;
+ case IPT_ICMP_ECHOREPLY:
+ /* Doesn't happen. */
}
return NF_DROP;
}
-static inline int find_ping_match(const struct ipt_entry_match *m)
-{
- const struct ipt_icmp *icmpinfo = (const struct ipt_icmp *)m->data;
-
- if (strcmp(m->u.kernel.match->name, "icmp") == 0
- && icmpinfo->type == ICMP_ECHO
- && !(icmpinfo->invflags & IPT_ICMP_INV))
- return 1;
-
- return 0;
-}
-
static int check(const char *tablename,
const struct ipt_entry *e,
void *targinfo,
@@ -216,17 +343,8 @@
}
if (rejinfo->with == IPT_ICMP_ECHOREPLY) {
- /* Must specify that it's an ICMP ping packet. */
- if (e->ip.proto != IPPROTO_ICMP
- || (e->ip.invflags & IPT_INV_PROTO)) {
- DEBUGP("REJECT: ECHOREPLY illegal for non-icmp\n");
- return 0;
- }
- /* Must contain ICMP match. */
- if (IPT_MATCH_ITERATE(e, find_ping_match) == 0) {
- DEBUGP("REJECT: ECHOREPLY illegal for non-ping\n");
- return 0;
- }
+ printk("REJECT: ECHOREPLY no longer supported.\n");
+ return 0;
} else if (rejinfo->with == IPT_TCP_RESET) {
/* Must specify that it's a TCP packet */
if (e->ip.proto != IPPROTO_TCP
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)