patch-2.2.4 linux/net/ipv4/ip_output.c

Next file: linux/net/ipv4/ip_sockglue.c
Previous file: linux/net/ipv4/ip_options.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.2.3/linux/net/ipv4/ip_output.c linux/net/ipv4/ip_output.c
@@ -5,7 +5,7 @@
  *
  *		The Internet Protocol (IP) output module.
  *
- * Version:	$Id: ip_output.c,v 1.65 1999/01/21 13:37:34 davem Exp $
+ * Version:	$Id: ip_output.c,v 1.66 1999/03/21 05:22:41 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -36,8 +36,7 @@
  *					for decreased register pressure on x86 
  *					and more readibility. 
  *		Marc Boucher	:	When call_out_firewall returns FW_QUEUE,
- *					silently abort send instead of failing
- *					with -EPERM.
+ *					silently drop skb instead of failing with -EPERM.
  */
 
 #include <asm/uaccess.h>
@@ -132,8 +131,16 @@
 	dev = rt->u.dst.dev;
 
 #ifdef CONFIG_FIREWALL
-	if (call_out_firewall(PF_INET, dev, iph, NULL, &skb) < FW_ACCEPT)
-		goto drop;
+	/* Now we have no better mechanism to notify about error. */
+	switch (call_out_firewall(PF_INET, dev, iph, NULL, &skb)) {
+	case FW_REJECT:
+		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
+		/* Fall thru... */
+	case FW_BLOCK:
+	case FW_QUEUE:
+		kfree_skb(skb);
+		return;
+	}
 #endif
 
 	ip_send_check(iph);
@@ -141,11 +148,6 @@
 	/* Send it out. */
 	skb->dst->output(skb);
 	return;
-
-#ifdef CONFIG_FIREWALL
-drop:
-	kfree_skb(skb);
-#endif
 }
 
 int __ip_finish_output(struct sk_buff *skb)
@@ -292,8 +294,17 @@
 	dev = rt->u.dst.dev;
 
 #ifdef CONFIG_FIREWALL
-	if (call_out_firewall(PF_INET, dev, iph, NULL, &skb) < FW_ACCEPT) 
-		goto drop;
+	/* Now we have no better mechanism to notify about error. */
+	switch (call_out_firewall(PF_INET, dev, iph, NULL, &skb)) {
+	case FW_REJECT:
+		start_bh_atomic();
+		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
+		end_bh_atomic();
+		/* Fall thru... */
+	case FW_BLOCK:
+	case FW_QUEUE:
+ 		goto drop;
+	}
 #endif
 
 	/* This can happen when the transport layer has segments queued
@@ -340,8 +351,12 @@
 		 */
 		iph->frag_off |= __constant_htons(IP_DF);
 		printk(KERN_DEBUG "sending pkt_too_big to self\n");
+
+		/* icmp_send is not reenterable, so that bh_atomic... --ANK */
+		start_bh_atomic();
 		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
 			  htonl(rt->u.dst.pmtu));
+		end_bh_atomic();
 		goto drop;
 	}
 	ip_fragment(skb, skb->dst->output);
@@ -402,14 +417,13 @@
 	if (ip_dont_fragment(sk, &rt->u.dst))
 		df = htons(IP_DF);
   
-	if (!sk->ip_hdrincl)
-		length -= sizeof(struct iphdr);
+	length -= sizeof(struct iphdr);
 
 	if (opt) {
 		fragheaderlen = sizeof(struct iphdr) + opt->optlen;
 		maxfraglen = ((mtu-sizeof(struct iphdr)-opt->optlen) & ~7) + fragheaderlen;
 	} else {
-		fragheaderlen = sk->ip_hdrincl ? 0 : sizeof(struct iphdr);
+		fragheaderlen = sizeof(struct iphdr);
 		
 		/*
 		 *	Fragheaderlen is the size of 'overhead' on each buffer. Now work
@@ -474,7 +488,6 @@
 	 */
 	 
 	do {
-		int error;
 		char *data;
 		struct sk_buff * skb;
 
@@ -482,15 +495,10 @@
 		 *	Get the memory we require with some space left for alignment.
 		 */
 
-		skb = sock_alloc_send_skb(sk, fraglen+hh_len+15, 0, flags&MSG_DONTWAIT, &error);
-		if (skb == NULL) {
-			ip_statistics.IpOutDiscards++;
-			if(nfrags>1)
-				ip_statistics.IpFragCreates++;			
-			dev_unlock_list();
-			return(error);
-		}
-		
+		skb = sock_alloc_send_skb(sk, fraglen+hh_len+15, 0, flags&MSG_DONTWAIT, &err);
+		if (skb == NULL)
+			goto error;
+
 		/*
 		 *	Fill in the control structures
 		 */
@@ -510,7 +518,7 @@
 		 *	Only write IP header onto non-raw packets 
 		 */
 		 
-		if(!sk->ip_hdrincl) {
+		{
 			struct iphdr *iph = (struct iphdr *)data;
 
 			iph->version = 4;
@@ -547,53 +555,46 @@
 		 *	User data callback
 		 */
 
-		err = 0;
-		if (getfrag(frag, data, offset, fraglen-fragheaderlen))
+		if (getfrag(frag, data, offset, fraglen-fragheaderlen)) {
 			err = -EFAULT;
-		
-		/*
-		 *	Account for the fragment.
-		 */
-
-#ifdef CONFIG_FIREWALL
-		if(!err) {
-			int fw_res;
-
-			fw_res = call_out_firewall(PF_INET, rt->u.dst.dev, skb->nh.iph, NULL, &skb);
-			if(fw_res == FW_QUEUE) {
-				kfree_skb(skb);
-				skb = NULL;
-			} else if(fw_res < FW_ACCEPT) {
-				err = -EPERM;
-			}
-		}
-#endif
-
-		if (err) { 
-			ip_statistics.IpOutDiscards++;
 			kfree_skb(skb);
-			dev_unlock_list();
-			return err; 
+			goto error;
 		}
-			
 
 		offset -= (maxfraglen-fragheaderlen);
 		fraglen = maxfraglen;
 
 		nfrags++;
 
-		err = 0; 
-		if (skb && rt->u.dst.output(skb)) {
-			err = -ENETDOWN;
-			ip_statistics.IpOutDiscards++;	
-			break;
+#ifdef CONFIG_FIREWALL
+		switch (call_out_firewall(PF_INET, rt->u.dst.dev, skb->nh.iph, NULL, &skb)) {
+		case FW_QUEUE:
+			kfree_skb(skb);
+			continue;
+		case FW_BLOCK:
+		case FW_REJECT:
+			kfree_skb(skb);
+			err = -EPERM;
+			goto error;
 		}
+#endif
+
+		err = -ENETDOWN;
+		if (rt->u.dst.output(skb))
+			goto error;
 	} while (offset >= 0);
 
 	if (nfrags>1)
 		ip_statistics.IpFragCreates += nfrags;
 	dev_unlock_list();
-	return err;
+	return 0;
+
+error:
+	ip_statistics.IpOutDiscards++;
+	if (nfrags>1)
+		ip_statistics.IpFragCreates += nfrags;
+	dev_unlock_list();
+	return err; 
 }
 
 
@@ -621,14 +622,20 @@
 	 *	choice RAW frames within 20 bytes of maximum size(rare) to the long path
 	 */
 
-	if (!sk->ip_hdrincl)
+	if (!sk->ip_hdrincl) {
 		length += sizeof(struct iphdr);
 
-	/*
-	 * 	Check for slow path.
-	 */
-	if (length > rt->u.dst.pmtu || ipc->opt != NULL)  
-		return ip_build_xmit_slow(sk,getfrag,frag,length,ipc,rt,flags); 
+		/*
+		 * 	Check for slow path.
+		 */
+		if (length > rt->u.dst.pmtu || ipc->opt != NULL)  
+			return ip_build_xmit_slow(sk,getfrag,frag,length,ipc,rt,flags); 
+	} else {
+		if (length > rt->u.dst.dev->mtu) {
+			ip_local_error(sk, EMSGSIZE, rt->rt_dst, sk->dport, rt->u.dst.dev->mtu);
+			return -EMSGSIZE;
+		}
+	}
 
 	/*
 	 *	Do path mtu discovery if needed.
@@ -636,7 +643,7 @@
 	df = 0;
 	if (ip_dont_fragment(sk, &rt->u.dst))
 		df = htons(IP_DF);
-	 	
+
 	/* 
 	 *	Fast path for unfragmented frames without options. 
 	 */ 
@@ -679,31 +686,27 @@
 
 	dev_unlock_list();
 
-	if (err) 
-		err = -EFAULT;
+	if (err)
+		goto error_fault;
 
 #ifdef CONFIG_FIREWALL
-	if(!err) {
-		int fw_res;
-
-		fw_res = call_out_firewall(PF_INET, rt->u.dst.dev, iph, NULL, &skb);
-		if(fw_res == FW_QUEUE) {
-			/* re-queued elsewhere; silently abort this send */
-			kfree_skb(skb);
-			return 0;
-		}
-		if(fw_res < FW_ACCEPT)
-			err = -EPERM;
-	}
-#endif
-
-	if (err) { 
+	switch (call_out_firewall(PF_INET, rt->u.dst.dev, iph, NULL, &skb)) {
+	case FW_QUEUE:
 		kfree_skb(skb);
+		return 0;
+	case FW_BLOCK:
+	case FW_REJECT:
+		kfree_skb(skb);
+		err = -EPERM;
 		goto error;
 	}
-	
+#endif
+
 	return rt->u.dst.output(skb);
 
+error_fault:
+	err = -EFAULT;
+	kfree_skb(skb);
 error:
 	ip_statistics.IpOutDiscards++;
 	return err; 

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)