From: Pravin Shelar <pshe...@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 <shemmin...@vyatta.com> Signed-off-by: Pravin B Shelar <pshe...@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 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev