patch-2.4.21 linux-2.4.21/net/ipv6/icmp.c

Next file: linux-2.4.21/net/ipv6/ip6_fib.c
Previous file: linux-2.4.21/net/ipv6/exthdrs.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.20/net/ipv6/icmp.c linux-2.4.21/net/ipv6/icmp.c
@@ -25,9 +25,9 @@
  *					add more length checks and other fixes.
  *	yoshfuji		:	ensure to sent parameter problem for
  *					fragments.
+ *	YOSHIFUJI Hideaki @USAGI:	added sysctl for icmp rate limit.
  */
 
-#define __NO_VERSION__
 #include <linux/module.h>
 #include <linux/errno.h>
 #include <linux/types.h>
@@ -40,6 +40,10 @@
 #include <linux/skbuff.h>
 #include <linux/init.h>
 
+#ifdef CONFIG_SYSCTL
+#include <linux/sysctl.h>
+#endif
+
 #include <linux/inet.h>
 #include <linux/netdevice.h>
 #include <linux/icmpv6.h>
@@ -63,10 +67,12 @@
 struct icmpv6_mib icmpv6_statistics[NR_CPUS*2];
 
 /*
- *	ICMP socket for flow control.
+ *	ICMP socket(s) for flow control.
  */
 
-struct socket *icmpv6_socket;
+static struct socket *__icmpv6_socket[NR_CPUS];
+#define icmpv6_socket	__icmpv6_socket[smp_processor_id()]
+#define icmpv6_socket_cpu(X) __icmpv6_socket[(X)]
 
 int icmpv6_rcv(struct sk_buff *skb);
 
@@ -91,43 +97,18 @@
 };
 
 
-static int icmpv6_xmit_holder = -1;
-
-static int icmpv6_xmit_lock_bh(void)
-{
-	if (!spin_trylock(&icmpv6_socket->sk->lock.slock)) {
-		if (icmpv6_xmit_holder == smp_processor_id())
-			return -EAGAIN;
-		spin_lock(&icmpv6_socket->sk->lock.slock);
-	}
-	icmpv6_xmit_holder = smp_processor_id();
-	return 0;
-}
-
-static __inline__ int icmpv6_xmit_lock(void)
+static void icmpv6_xmit_lock(void)
 {
-	int ret;
 	local_bh_disable();
-	ret = icmpv6_xmit_lock_bh();
-	if (ret)
-		local_bh_enable();
-	return ret;
+	if (unlikely(!spin_trylock(&icmpv6_socket->sk->lock.slock)))
+		BUG();
 }
 
-static void icmpv6_xmit_unlock_bh(void)
+static void icmpv6_xmit_unlock(void)
 {
-	icmpv6_xmit_holder = -1;
-	spin_unlock(&icmpv6_socket->sk->lock.slock);
+	spin_unlock_bh(&icmpv6_socket->sk->lock.slock);
 }
 
-static __inline__ void icmpv6_xmit_unlock(void)
-{
-	icmpv6_xmit_unlock_bh();
-	local_bh_enable();
-}
-
-
-
 /*
  *	getfrag callback
  */
@@ -344,8 +325,7 @@
 	fl.uli_u.icmpt.type = type;
 	fl.uli_u.icmpt.code = code;
 
-	if (icmpv6_xmit_lock())
-		return;
+	icmpv6_xmit_lock();
 
 	if (!icmpv6_xrlim_allow(sk, type, &fl))
 		goto out;
@@ -395,7 +375,8 @@
 
 	saddr = &skb->nh.ipv6h->daddr;
 
-	if (ipv6_addr_type(saddr) & IPV6_ADDR_MULTICAST)
+	if (ipv6_addr_type(saddr) & IPV6_ADDR_MULTICAST ||
+	    ipv6_chk_acast_addr(0, saddr)) 
 		saddr = NULL;
 
 	msg.icmph.icmp6_type = ICMPV6_ECHO_REPLY;
@@ -418,15 +399,14 @@
 	fl.uli_u.icmpt.type = ICMPV6_ECHO_REPLY;
 	fl.uli_u.icmpt.code = 0;
 
-	if (icmpv6_xmit_lock_bh())
-		return;
+	icmpv6_xmit_lock();
 
 	ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, msg.len, NULL, -1,
 		       MSG_DONTWAIT);
 	ICMP6_INC_STATS_BH(Icmp6OutEchoReplies);
 	ICMP6_INC_STATS_BH(Icmp6OutMsgs);
 
-	icmpv6_xmit_unlock_bh();
+	icmpv6_xmit_unlock();
 }
 
 static void icmpv6_notify(struct sk_buff *skb, int type, int code, u32 info)
@@ -635,41 +615,53 @@
 int __init icmpv6_init(struct net_proto_family *ops)
 {
 	struct sock *sk;
-	int err;
+	int err, i, j;
 
-	icmpv6_socket = sock_alloc();
-	if (icmpv6_socket == NULL) {
-		printk(KERN_ERR
-		       "Failed to create the ICMP6 control socket.\n");
-		return -1;
-	}
-	icmpv6_socket->inode->i_uid = 0;
-	icmpv6_socket->inode->i_gid = 0;
-	icmpv6_socket->type = SOCK_RAW;
-
-	if ((err = ops->create(icmpv6_socket, IPPROTO_ICMPV6)) < 0) {
-		printk(KERN_ERR
-		       "Failed to initialize the ICMP6 control socket (err %d).\n",
-		       err);
-		sock_release(icmpv6_socket);
-		icmpv6_socket = NULL; /* for safety */
-		return err;
-	}
-
-	sk = icmpv6_socket->sk;
-	sk->allocation = GFP_ATOMIC;
-	sk->sndbuf = SK_WMEM_MAX*2;
-	sk->prot->unhash(sk);
+	for (i = 0; i < NR_CPUS; i++) {
+		icmpv6_socket_cpu(i) = sock_alloc();
+		if (icmpv6_socket_cpu(i) == NULL) {
+			printk(KERN_ERR
+			       "Failed to create the ICMP6 control socket.\n");
+			err = -1;
+			goto fail;
+		}
+		icmpv6_socket_cpu(i)->inode->i_uid = 0;
+		icmpv6_socket_cpu(i)->inode->i_gid = 0;
+		icmpv6_socket_cpu(i)->type = SOCK_RAW;
+
+		if ((err = ops->create(icmpv6_socket_cpu(i), IPPROTO_ICMPV6)) < 0) {
+			printk(KERN_ERR
+			       "Failed to initialize the ICMP6 control socket "
+			       "(err %d).\n",
+			       err);
+			goto fail;
+		}
+
+		sk = icmpv6_socket_cpu(i)->sk;
+		sk->allocation = GFP_ATOMIC;
+		sk->sndbuf = SK_WMEM_MAX*2;
+		sk->prot->unhash(sk);
+	}
 
 	inet6_add_protocol(&icmpv6_protocol);
 
 	return 0;
+fail:
+	for (j = 0; j < i; j++) {
+		sock_release(icmpv6_socket_cpu(j));
+		icmpv6_socket_cpu(j) = NULL;
+	}
+	return err;
 }
 
 void icmpv6_cleanup(void)
 {
-	sock_release(icmpv6_socket);
-	icmpv6_socket = NULL; /* For safety. */
+	int i;
+
+	for (i = 0; i < NR_CPUS; i++) {
+		sock_release(icmpv6_socket_cpu(i));
+		icmpv6_socket_cpu(i) = NULL;
+	}
 	inet6_del_protocol(&icmpv6_protocol);
 }
 
@@ -715,3 +707,12 @@
 
 	return fatal;
 }
+
+#ifdef CONFIG_SYSCTL
+ctl_table ipv6_icmp_table[] = {
+	{NET_IPV6_ICMP_RATELIMIT, "ratelimit",
+	&sysctl_icmpv6_time, sizeof(int), 0644, NULL, &proc_dointvec},
+	{0},
+};
+#endif
+

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