|
@@ -264,6 +264,16 @@ static void __net_exit ipmr_rules_exit(struct net *net)
|
|
|
fib_rules_unregister(net->ipv4.mr_rules_ops);
|
|
|
rtnl_unlock();
|
|
|
}
|
|
|
+
|
|
|
+static int ipmr_rules_dump(struct net *net, struct notifier_block *nb)
|
|
|
+{
|
|
|
+ return fib_rules_dump(net, nb, RTNL_FAMILY_IPMR);
|
|
|
+}
|
|
|
+
|
|
|
+static unsigned int ipmr_rules_seq_read(struct net *net)
|
|
|
+{
|
|
|
+ return fib_rules_seq_read(net, RTNL_FAMILY_IPMR);
|
|
|
+}
|
|
|
#else
|
|
|
#define ipmr_for_each_table(mrt, net) \
|
|
|
for (mrt = net->ipv4.mrt; mrt; mrt = NULL)
|
|
@@ -298,6 +308,16 @@ static void __net_exit ipmr_rules_exit(struct net *net)
|
|
|
net->ipv4.mrt = NULL;
|
|
|
rtnl_unlock();
|
|
|
}
|
|
|
+
|
|
|
+static int ipmr_rules_dump(struct net *net, struct notifier_block *nb)
|
|
|
+{
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static unsigned int ipmr_rules_seq_read(struct net *net)
|
|
|
+{
|
|
|
+ return 0;
|
|
|
+}
|
|
|
#endif
|
|
|
|
|
|
static inline int ipmr_hash_cmp(struct rhashtable_compare_arg *arg,
|
|
@@ -587,6 +607,43 @@ static struct net_device *ipmr_reg_vif(struct net *net, struct mr_table *mrt)
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
+static int call_ipmr_vif_entry_notifier(struct notifier_block *nb,
|
|
|
+ struct net *net,
|
|
|
+ enum fib_event_type event_type,
|
|
|
+ struct vif_device *vif,
|
|
|
+ vifi_t vif_index, u32 tb_id)
|
|
|
+{
|
|
|
+ struct vif_entry_notifier_info info = {
|
|
|
+ .info = {
|
|
|
+ .family = RTNL_FAMILY_IPMR,
|
|
|
+ .net = net,
|
|
|
+ },
|
|
|
+ .dev = vif->dev,
|
|
|
+ .vif_index = vif_index,
|
|
|
+ .vif_flags = vif->flags,
|
|
|
+ .tb_id = tb_id,
|
|
|
+ };
|
|
|
+
|
|
|
+ return call_fib_notifier(nb, net, event_type, &info.info);
|
|
|
+}
|
|
|
+
|
|
|
+static int call_ipmr_mfc_entry_notifier(struct notifier_block *nb,
|
|
|
+ struct net *net,
|
|
|
+ enum fib_event_type event_type,
|
|
|
+ struct mfc_cache *mfc, u32 tb_id)
|
|
|
+{
|
|
|
+ struct mfc_entry_notifier_info info = {
|
|
|
+ .info = {
|
|
|
+ .family = RTNL_FAMILY_IPMR,
|
|
|
+ .net = net,
|
|
|
+ },
|
|
|
+ .mfc = mfc,
|
|
|
+ .tb_id = tb_id
|
|
|
+ };
|
|
|
+
|
|
|
+ return call_fib_notifier(nb, net, event_type, &info.info);
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* vif_delete - Delete a VIF entry
|
|
|
* @notify: Set to 1, if the caller is a notifier_call
|
|
@@ -3050,14 +3107,87 @@ static const struct net_protocol pim_protocol = {
|
|
|
};
|
|
|
#endif
|
|
|
|
|
|
+static unsigned int ipmr_seq_read(struct net *net)
|
|
|
+{
|
|
|
+ ASSERT_RTNL();
|
|
|
+
|
|
|
+ return net->ipv4.ipmr_seq + ipmr_rules_seq_read(net);
|
|
|
+}
|
|
|
+
|
|
|
+static int ipmr_dump(struct net *net, struct notifier_block *nb)
|
|
|
+{
|
|
|
+ struct mr_table *mrt;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ err = ipmr_rules_dump(net, nb);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ ipmr_for_each_table(mrt, net) {
|
|
|
+ struct vif_device *v = &mrt->vif_table[0];
|
|
|
+ struct mfc_cache *mfc;
|
|
|
+ int vifi;
|
|
|
+
|
|
|
+ /* Notifiy on table VIF entries */
|
|
|
+ read_lock(&mrt_lock);
|
|
|
+ for (vifi = 0; vifi < mrt->maxvif; vifi++, v++) {
|
|
|
+ if (!v->dev)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ call_ipmr_vif_entry_notifier(nb, net, FIB_EVENT_VIF_ADD,
|
|
|
+ v, vifi, mrt->id);
|
|
|
+ }
|
|
|
+ read_unlock(&mrt_lock);
|
|
|
+
|
|
|
+ /* Notify on table MFC entries */
|
|
|
+ list_for_each_entry_rcu(mfc, &mrt->mfc_cache_list, list)
|
|
|
+ call_ipmr_mfc_entry_notifier(nb, net,
|
|
|
+ FIB_EVENT_ENTRY_ADD, mfc,
|
|
|
+ mrt->id);
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static const struct fib_notifier_ops ipmr_notifier_ops_template = {
|
|
|
+ .family = RTNL_FAMILY_IPMR,
|
|
|
+ .fib_seq_read = ipmr_seq_read,
|
|
|
+ .fib_dump = ipmr_dump,
|
|
|
+ .owner = THIS_MODULE,
|
|
|
+};
|
|
|
+
|
|
|
+int __net_init ipmr_notifier_init(struct net *net)
|
|
|
+{
|
|
|
+ struct fib_notifier_ops *ops;
|
|
|
+
|
|
|
+ net->ipv4.ipmr_seq = 0;
|
|
|
+
|
|
|
+ ops = fib_notifier_ops_register(&ipmr_notifier_ops_template, net);
|
|
|
+ if (IS_ERR(ops))
|
|
|
+ return PTR_ERR(ops);
|
|
|
+ net->ipv4.ipmr_notifier_ops = ops;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void __net_exit ipmr_notifier_exit(struct net *net)
|
|
|
+{
|
|
|
+ fib_notifier_ops_unregister(net->ipv4.ipmr_notifier_ops);
|
|
|
+ net->ipv4.ipmr_notifier_ops = NULL;
|
|
|
+}
|
|
|
+
|
|
|
/* Setup for IP multicast routing */
|
|
|
static int __net_init ipmr_net_init(struct net *net)
|
|
|
{
|
|
|
int err;
|
|
|
|
|
|
+ err = ipmr_notifier_init(net);
|
|
|
+ if (err)
|
|
|
+ goto ipmr_notifier_fail;
|
|
|
+
|
|
|
err = ipmr_rules_init(net);
|
|
|
if (err < 0)
|
|
|
- goto fail;
|
|
|
+ goto ipmr_rules_fail;
|
|
|
|
|
|
#ifdef CONFIG_PROC_FS
|
|
|
err = -ENOMEM;
|
|
@@ -3074,7 +3204,9 @@ proc_cache_fail:
|
|
|
proc_vif_fail:
|
|
|
ipmr_rules_exit(net);
|
|
|
#endif
|
|
|
-fail:
|
|
|
+ipmr_rules_fail:
|
|
|
+ ipmr_notifier_exit(net);
|
|
|
+ipmr_notifier_fail:
|
|
|
return err;
|
|
|
}
|
|
|
|
|
@@ -3084,6 +3216,7 @@ static void __net_exit ipmr_net_exit(struct net *net)
|
|
|
remove_proc_entry("ip_mr_cache", net->proc_net);
|
|
|
remove_proc_entry("ip_mr_vif", net->proc_net);
|
|
|
#endif
|
|
|
+ ipmr_notifier_exit(net);
|
|
|
ipmr_rules_exit(net);
|
|
|
}
|
|
|
|