[ovs-dev] [PATCH 5/5] IP_TUNNEL: follow state of lower device

Pravin B Shelar pshelar at nicira.com
Tue Jan 8 02:31:53 UTC 2013


From: Pravin Shelar <pshelar at nicira.com>

I reference patch posted for IPGRE:
http://permalink.gmane.org/gmane.linux.network/226946

--8<--------------------------cut here-------------------------->8--

IP tunnels like other layered devices should propogate
carrier and state from lower device to tunnel.
Following patch would propogate link status to IPIP and
GRE devices.

Suggested-by: Stephen Hemminger <shemminger at vyatta.com>
Signed-off-by: Pravin B Shelar <pshelar at nicira.com>
---
 include/net/ip_tunnel.h |    1 +
 net/ipv4/ip_tunnel.c    |  112 ++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 111 insertions(+), 2 deletions(-)

diff --git a/include/net/ip_tunnel.h b/include/net/ip_tunnel.h
index 7328871..5490a7d 100644
--- a/include/net/ip_tunnel.h
+++ b/include/net/ip_tunnel.h
@@ -21,6 +21,7 @@ struct ip_tunnel_6rd_parm {
 struct ip_tunnel {
 	struct ip_tunnel __rcu	*next;
 	struct hlist_node hash_node;
+	struct hlist_node link_node;
 	struct net_device	*dev;
 
 	int			err_count;	/* Number of arrived ICMP errors */
diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c
index 5e0e46e..f47345c 100644
--- a/net/ipv4/ip_tunnel.c
+++ b/net/ipv4/ip_tunnel.c
@@ -51,6 +51,11 @@
 #include <net/ip6_route.h>
 #endif
 
+struct tunnels_net {
+	struct hlist_head *link_map;
+};
+static int tunnels_net_id;
+
 static unsigned int ip_tunnel_hash(struct ip_tunnel_net *itn,
 				   __be32 key, __be32 remote)
 {
@@ -235,8 +240,58 @@ static void ip_tunnel_add(struct ip_tunnel_net *itn, struct ip_tunnel *t)
 static void ip_tunnel_del(struct ip_tunnel_net *itn, struct ip_tunnel *t)
 {
 	hlist_del_init_rcu(&t->hash_node);
+	hlist_del_init_rcu(&t->link_node);
+}
+
+static void ip_tunnel_add_link(struct net *net, struct ip_tunnel *t, int iflink)
+{
+	struct tunnels_net *tn = net_generic(net, tunnels_net_id);
+	int hash = hash_32(iflink, IP_TNL_HASH_BITS);
+	struct hlist_head *head = &tn->link_map[hash];
+
+	hlist_add_head_rcu(&t->link_node, head);
+}
+
+static int ip_tunnel_notify(struct notifier_block *unused,
+		unsigned long event, void *ptr)
+{
+	struct net_device *rootdev = ptr;
+	struct tunnels_net *tn = net_generic(dev_net(rootdev), tunnels_net_id);
+	int hash = hash_32(rootdev->iflink, IP_TNL_HASH_BITS);
+	struct hlist_head *head = &tn->link_map[hash];
+	struct hlist_node *node, *n;
+	struct ip_tunnel *t;
+
+	hlist_for_each_entry_safe(t, node, n, head, link_node) {
+		int flags;
+
+		if (rootdev->ifindex != t->dev->iflink)
+			continue;
+		switch (event) {
+		case NETDEV_DOWN:
+			flags = t->dev->flags;
+			if (!(flags & IFF_UP))
+				break;
+			dev_change_flags(t->dev, flags & ~IFF_UP);
+			netif_stacked_transfer_operstate(rootdev, t->dev);
+			break;
+		case NETDEV_UP:
+			flags = t->dev->flags;
+			if (flags & IFF_UP)
+				break;
+			dev_change_flags(t->dev, flags | IFF_UP);
+			netif_stacked_transfer_operstate(rootdev, t->dev);
+			break;
+		}
+
+	}
+	return NOTIFY_DONE;
 }
 
+static struct notifier_block ip_tunnel_notifier = {
+	.notifier_call = ip_tunnel_notify,
+};
+
 static struct ip_tunnel *ip_tunnel_find(struct ip_tunnel_net *itn,
 				 struct ip_tunnel_parm *parms,
 				 int type)
@@ -356,8 +411,12 @@ static int ip_tunnel_bind_dev(struct net_device *dev)
 	if (tdev) {
 		hlen = tdev->hard_header_len + tdev->needed_headroom;
 		mtu = tdev->mtu;
+		netif_stacked_transfer_operstate(tdev, dev);
+		ip_tunnel_add_link(dev_net(dev), tunnel, tdev->ifindex);
+		dev->iflink = tdev->ifindex;
+	} else {
+		dev->iflink = tunnel->parms.link;
 	}
-	dev->iflink = tunnel->parms.link;
 
 	dev->needed_headroom = t_hlen + hlen;
 	mtu -= (dev->hard_header_len + t_hlen);
@@ -935,7 +994,7 @@ int ip_tunnel_newlink(struct net *src_net, struct net_device *dev,
 		dev->mtu = mtu;
 
 	ip_tunnel_add(itn, nt);
-
+	linkwatch_fire_event(dev);
 out:
 	return err;
 }
@@ -1060,4 +1119,53 @@ void ip_tunnel_setup(struct net_device *dev, int net_id)
 }
 EXPORT_SYMBOL(ip_tunnel_setup);
 
+static int __net_init tunnels_init_net(struct net *net)
+{
+	struct tunnels_net *tn = net_generic(net, tunnels_net_id);
+
+	tn->link_map = kzalloc(IP_TNL_HASH_SIZE * sizeof(struct hlist_head), GFP_KERNEL);
+	if (!tn->link_map)
+		return -ENOMEM;
+	return 0;
+}
+
+static void __net_exit tunnels_exit_net(struct net *net)
+{
+	struct tunnels_net *tn = net_generic(net, tunnels_net_id);
+	kfree(tn->link_map);
+}
+
+static struct pernet_operations tunnels_net_ops = {
+	.init = tunnels_init_net,
+	.exit = tunnels_exit_net,
+	.id   = &tunnels_net_id,
+	.size = sizeof(struct tunnels_net),
+};
+
+static int __init ip_tunnel_mod_init(void)
+{
+	int err;
+
+	pr_info("IP_Tunnel init\n");
+	err = register_pernet_device(&tunnels_net_ops);
+	if (err < 0)
+		return err;
+
+	err = register_netdevice_notifier(&ip_tunnel_notifier);
+	if (err < 0) {
+		unregister_pernet_device(&tunnels_net_ops);
+		return err;
+	}
+
+	return 0;
+}
+
+static void __exit ip_tunnel_mod_fini(void)
+{
+	unregister_netdevice_notifier(&ip_tunnel_notifier);
+	unregister_pernet_device(&tunnels_net_ops);
+}
+
+module_init(ip_tunnel_mod_init);
+module_exit(ip_tunnel_mod_fini);
 MODULE_LICENSE("GPL");
-- 
1.7.1




More information about the dev mailing list