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

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)