From 3fcfa12904e83cc291cf2b7b05ff2530068920a4 Mon Sep 17 00:00:00 2001
From: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Date: Sat, 22 Mar 2008 17:42:57 +0900
Subject: [IPV6] SIT: Fix locking issues in PRL management.

To protect PRL list, use ipip6_lock.

Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
---
 net/ipv6/sit.c | 49 ++++++++++++++++++++++++++++++++-----------------
 1 file changed, 32 insertions(+), 17 deletions(-)

(limited to 'net/ipv6/sit.c')

diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
index 4786419..ee0cc28 100644
--- a/net/ipv6/sit.c
+++ b/net/ipv6/sit.c
@@ -198,7 +198,7 @@ failed:
 }
 
 static struct ip_tunnel_prl_entry *
-ipip6_tunnel_locate_prl(struct ip_tunnel *t, __be32 addr)
+__ipip6_tunnel_locate_prl(struct ip_tunnel *t, __be32 addr)
 {
 	struct ip_tunnel_prl_entry *p = (struct ip_tunnel_prl_entry *)NULL;
 
@@ -213,34 +213,46 @@ static int
 ipip6_tunnel_add_prl(struct ip_tunnel *t, struct ip_tunnel_prl *a, int chg)
 {
 	struct ip_tunnel_prl_entry *p;
+	int err = 0;
+
+	write_lock(&ipip6_lock);
 
 	for (p = t->prl; p; p = p->next) {
 		if (p->entry.addr == a->addr) {
-			if (chg) {
-				p->entry = *a;
-				return 0;
-			}
-			return -EEXIST;
+			if (chg)
+				goto update;
+			err = -EEXIST;
+			goto out;
 		}
 	}
 
-	if (chg)
-		return -ENXIO;
+	if (chg) {
+		err = -ENXIO;
+		goto out;
+	}
 
 	p = kzalloc(sizeof(struct ip_tunnel_prl_entry), GFP_KERNEL);
-	if (!p)
-		return -ENOBUFS;
+	if (!p) {
+		err = -ENOBUFS;
+		goto out;
+	}
 
-	p->entry = *a;
 	p->next = t->prl;
 	t->prl = p;
-	return 0;
+update:
+	p->entry = *a;
+out:
+	write_unlock(&ipip6_lock);
+	return err;
 }
 
 static int
 ipip6_tunnel_del_prl(struct ip_tunnel *t, struct ip_tunnel_prl *a)
 {
 	struct ip_tunnel_prl_entry *x, **p;
+	int err = 0;
+
+	write_lock(&ipip6_lock);
 
 	if (a) {
 		for (p = &t->prl; *p; p = &(*p)->next) {
@@ -248,10 +260,10 @@ ipip6_tunnel_del_prl(struct ip_tunnel *t, struct ip_tunnel_prl *a)
 				x = *p;
 				*p = x->next;
 				kfree(x);
-				return 0;
+				goto out;
 			}
 		}
-		return -ENXIO;
+		err = -ENXIO;
 	} else {
 		while (t->prl) {
 			x = t->prl;
@@ -259,6 +271,8 @@ ipip6_tunnel_del_prl(struct ip_tunnel *t, struct ip_tunnel_prl *a)
 			kfree(x);
 		}
 	}
+out:
+	write_unlock(&ipip6_lock);
 	return 0;
 }
 
@@ -290,9 +304,11 @@ ipip6_onlink(struct in6_addr *addr, struct net_device *dev)
 static int
 isatap_chksrc(struct sk_buff *skb, struct iphdr *iph, struct ip_tunnel *t)
 {
-	struct ip_tunnel_prl_entry *p = ipip6_tunnel_locate_prl(t, iph->saddr);
+	struct ip_tunnel_prl_entry *p;
 	int ok = 1;
 
+	read_lock(&ipip6_lock);
+	p = __ipip6_tunnel_locate_prl(t, iph->saddr);
 	if (p) {
 		if (p->entry.flags & PRL_DEFAULT)
 			skb->ndisc_nodetype = NDISC_NODETYPE_DEFAULT;
@@ -307,6 +323,7 @@ isatap_chksrc(struct sk_buff *skb, struct iphdr *iph, struct ip_tunnel *t)
 		else
 			ok = 0;
 	}
+	read_unlock(&ipip6_lock);
 	return ok;
 }
 
@@ -895,12 +912,10 @@ ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
 		if (!(t = netdev_priv(dev)))
 			goto done;
 
-		ipip6_tunnel_unlink(t);
 		if (cmd == SIOCDELPRL)
 			err = ipip6_tunnel_del_prl(t, &prl);
 		else
 			err = ipip6_tunnel_add_prl(t, &prl, cmd == SIOCCHGPRL);
-		ipip6_tunnel_link(t);
 		netdev_state_change(dev);
 		break;
 
-- 
cgit v1.1