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

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

diff -u --recursive --new-file v2.2.3/linux/net/ipv4/ip_gre.c linux/net/ipv4/ip_gre.c
@@ -189,6 +189,48 @@
 	return NULL;
 }
 
+static struct ip_tunnel **ipgre_bucket(struct ip_tunnel *t)
+{
+	u32 remote = t->parms.iph.daddr;
+	u32 local = t->parms.iph.saddr;
+	u32 key = t->parms.i_key;
+	unsigned h = HASH(key);
+	int prio = 0;
+
+	if (local)
+		prio |= 1;
+	if (remote && !MULTICAST(remote)) {
+		prio |= 2;
+		h ^= HASH(remote);
+	}
+
+	return &tunnels[prio][h];
+}
+
+static void ipgre_tunnel_link(struct ip_tunnel *t)
+{
+	struct ip_tunnel **tp = ipgre_bucket(t);
+
+	net_serialize_enter();
+	t->next = *tp;
+	*tp = t;
+	net_serialize_leave();
+}
+
+static void ipgre_tunnel_unlink(struct ip_tunnel *t)
+{
+	struct ip_tunnel **tp;
+
+	for (tp = ipgre_bucket(t); *tp; tp = &(*tp)->next) {
+		if (t == *tp) {
+			net_serialize_enter();
+			*tp = t->next;
+			net_serialize_leave();
+			break;
+		}
+	}
+}
+
 static struct ip_tunnel * ipgre_tunnel_locate(struct ip_tunnel_parm *parms, int create)
 {
 	u32 remote = parms->iph.daddr;
@@ -241,10 +283,7 @@
 	if (register_netdevice(dev) < 0)
 		goto failed;
 
-	start_bh_atomic();
-	nt->next = t;
-	*tp = nt;
-	end_bh_atomic();
+	ipgre_tunnel_link(nt);
 	/* Do not decrement MOD_USE_COUNT here. */
 	return nt;
 
@@ -256,28 +295,11 @@
 
 static void ipgre_tunnel_destroy(struct device *dev)
 {
-	struct ip_tunnel *t, **tp;
-	struct ip_tunnel *t0 = (struct ip_tunnel*)dev->priv;
-	u32 remote = t0->parms.iph.daddr;
-	u32 local = t0->parms.iph.saddr;
-	unsigned h = HASH(t0->parms.i_key);
-	int prio = 0;
+	ipgre_tunnel_unlink((struct ip_tunnel*)dev->priv);
 
-	if (local)
-		prio |= 1;
-	if (remote && !MULTICAST(remote)) {
-		prio |= 2;
-		h ^= HASH(remote);
-	}
-	for (tp = &tunnels[prio][h]; (t = *tp) != NULL; tp = &t->next) {
-		if (t == t0) {
-			*tp = t->next;
-			if (dev != &ipgre_fb_tunnel_dev) {
-				kfree(dev);
-				MOD_DEC_USE_COUNT;
-			}
-			break;
-		}
+	if (dev != &ipgre_fb_tunnel_dev) {
+		kfree(dev);
+		MOD_DEC_USE_COUNT;
 	}
 }
 
@@ -848,6 +870,41 @@
 			p.o_key = 0;
 
 		t = ipgre_tunnel_locate(&p, cmd == SIOCADDTUNNEL);
+
+		if (dev != &ipgre_fb_tunnel_dev && cmd == SIOCCHGTUNNEL &&
+		    t != &ipgre_fb_tunnel) {
+			if (t != NULL) {
+				if (t->dev != dev) {
+					err = -EEXIST;
+					break;
+				}
+			} else {
+				unsigned nflags=0;
+
+				t = (struct ip_tunnel*)dev->priv;
+
+				if (MULTICAST(p.iph.daddr))
+					nflags = IFF_BROADCAST;
+				else if (p.iph.daddr)
+					nflags = IFF_POINTOPOINT;
+
+				if ((dev->flags^nflags)&(IFF_POINTOPOINT|IFF_BROADCAST)) {
+					err = -EINVAL;
+					break;
+				}
+				start_bh_atomic();
+				ipgre_tunnel_unlink(t);
+				t->parms.iph.saddr = p.iph.saddr;
+				t->parms.iph.daddr = p.iph.daddr;
+				t->parms.i_key = p.i_key;
+				t->parms.o_key = p.o_key;
+				memcpy(dev->dev_addr, &p.iph.saddr, 4);
+				memcpy(dev->broadcast, &p.iph.daddr, 4);
+				ipgre_tunnel_link(t);
+				end_bh_atomic();
+				netdev_state_change(dev);
+			}
+		}
 
 		if (t) {
 			err = 0;

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