struct net_device *dev;
struct net *net;
struct list_head next;
+ struct list_head up_next;
struct socket *sock;
__be16 dst_port;
};
/* per-network namespace private data for this module */
struct stt_net {
struct list_head stt_list;
+ struct list_head stt_up_list; /* Devices which are in IFF_UP state. */
int n_tunnels;
};
static void clean_percpu(struct work_struct *work);
static DECLARE_DELAYED_WORK(clean_percpu_wq, clean_percpu);
-static struct stt_dev *stt_find_sock(struct net *net, __be16 port)
+static struct stt_dev *stt_find_up_dev(struct net *net, __be16 port)
{
struct stt_net *sn = net_generic(net, stt_net_id);
struct stt_dev *stt_dev;
- list_for_each_entry_rcu(stt_dev, &sn->stt_list, next) {
+ list_for_each_entry_rcu(stt_dev, &sn->stt_up_list, up_next) {
if (stt_dev->dst_port == port)
return stt_dev;
}
skb_set_transport_header(skb, ip_hdr_len);
- stt_dev = stt_find_sock(dev_net(skb->dev), tcp_hdr(skb)->dest);
+ stt_dev = stt_find_up_dev(dev_net(skb->dev), tcp_hdr(skb)->dest);
if (!stt_dev)
return NF_ACCEPT;
sn->n_tunnels--;
if (sn->n_tunnels)
goto out;
-#ifdef HAVE_NF_REGISTER_NET_HOOK
- nf_unregister_net_hook(net, &nf_hook_ops);
-#else
- nf_unregister_hook(&nf_hook_ops);
-#endif
-
out:
n_tunnels--;
if (n_tunnels)
{
struct stt_dev *stt = netdev_priv(dev);
struct net *net = stt->net;
+ struct stt_net *sn = net_generic(net, stt_net_id);
int err;
err = stt_start(net);
err = tcp_sock_create4(net, stt->dst_port, &stt->sock);
if (err)
return err;
+ list_add_rcu(&stt->up_next, &sn->stt_up_list);
return 0;
}
struct stt_dev *stt_dev = netdev_priv(dev);
struct net *net = stt_dev->net;
+ list_del_rcu(&stt_dev->up_next);
+ synchronize_net();
tcp_sock_release(stt_dev->sock);
stt_dev->sock = NULL;
stt_cleanup(net);
if (err)
return err;
- list_add_rcu(&stt->next, &sn->stt_list);
+ list_add(&stt->next, &sn->stt_list);
return 0;
}
{
struct stt_dev *stt = netdev_priv(dev);
- list_del_rcu(&stt->next);
+ list_del(&stt->next);
unregister_netdevice_queue(dev, head);
}
struct stt_net *sn = net_generic(net, stt_net_id);
INIT_LIST_HEAD(&sn->stt_list);
+ INIT_LIST_HEAD(&sn->stt_up_list);
return 0;
}
struct net_device *dev, *aux;
LIST_HEAD(list);
+#ifdef HAVE_NF_REGISTER_NET_HOOK
+ /* Ideally this should be done from stt_stop(), But on some kernels
+ * nf-unreg operation needs RTNL-lock, which can cause deallock.
+ * So it is done from here. */
+ if (!list_empty(&nf_hook_ops.list))
+ nf_unregister_net_hook(net, &nf_hook_ops);
+#endif
+
rtnl_lock();
/* gather any stt devices that were moved into this ns */
if (rc)
goto out2;
+ INIT_LIST_HEAD(&nf_hook_ops.list);
pr_info("STT tunneling driver\n");
return 0;
out2:
void stt_cleanup_module(void)
{
+#ifndef HAVE_NF_REGISTER_NET_HOOK
+ if (!list_empty(&nf_hook_ops.list))
+ nf_unregister_hook(&nf_hook_ops);
+#endif
rtnl_link_unregister(&stt_link_ops);
unregister_pernet_subsys(&stt_net_ops);
}