fib: introduce FIB notification infrastructure
authorJiri Pirko <jiri@mellanox.com>
Mon, 26 Sep 2016 10:52:29 +0000 (12:52 +0200)
committerDavid S. Miller <davem@davemloft.net>
Wed, 28 Sep 2016 08:48:00 +0000 (04:48 -0400)
This allows to pass information about added/deleted FIB entries/rules to
whoever is interested. This is done in a very similar way as devinet
notifies address additions/removals.

Signed-off-by: Jiri Pirko <jiri@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/ip_fib.h
net/ipv4/fib_frontend.c
net/ipv4/fib_rules.c
net/ipv4/fib_trie.c

index 7d4a72e75f334d300f9d55cd53bdd1d1e82c08f9..116a9c0eb455a29d9df7a76b1a7b3b6a4c092d15 100644 (file)
@@ -22,6 +22,7 @@
 #include <net/fib_rules.h>
 #include <net/inetpeer.h>
 #include <linux/percpu.h>
+#include <linux/notifier.h>
 
 struct fib_config {
        u8                      fc_dst_len;
@@ -185,6 +186,33 @@ __be32 fib_info_update_nh_saddr(struct net *net, struct fib_nh *nh);
 #define FIB_RES_PREFSRC(net, res)      ((res).fi->fib_prefsrc ? : \
                                         FIB_RES_SADDR(net, res))
 
+struct fib_notifier_info {
+       struct net *net;
+};
+
+struct fib_entry_notifier_info {
+       struct fib_notifier_info info; /* must be first */
+       u32 dst;
+       int dst_len;
+       struct fib_info *fi;
+       u8 tos;
+       u8 type;
+       u32 tb_id;
+       u32 nlflags;
+};
+
+enum fib_event_type {
+       FIB_EVENT_ENTRY_ADD,
+       FIB_EVENT_ENTRY_DEL,
+       FIB_EVENT_RULE_ADD,
+       FIB_EVENT_RULE_DEL,
+};
+
+int register_fib_notifier(struct notifier_block *nb);
+int unregister_fib_notifier(struct notifier_block *nb);
+int call_fib_notifiers(struct net *net, enum fib_event_type event_type,
+                      struct fib_notifier_info *info);
+
 struct fib_table {
        struct hlist_node       tb_hlist;
        u32                     tb_id;
@@ -196,11 +224,11 @@ struct fib_table {
 
 int fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp,
                     struct fib_result *res, int fib_flags);
-int fib_table_insert(struct fib_table *, struct fib_config *);
-int fib_table_delete(struct fib_table *, struct fib_config *);
+int fib_table_insert(struct net *, struct fib_table *, struct fib_config *);
+int fib_table_delete(struct net *, struct fib_table *, struct fib_config *);
 int fib_table_dump(struct fib_table *table, struct sk_buff *skb,
                   struct netlink_callback *cb);
-int fib_table_flush(struct fib_table *table);
+int fib_table_flush(struct net *net, struct fib_table *table);
 struct fib_table *fib_trie_unmerge(struct fib_table *main_tb);
 void fib_table_flush_external(struct fib_table *table);
 void fib_free_table(struct fib_table *tb);
index 4e56a4c20a3c67a468f09fb743d0a38e30372cb2..86c43dc9a60e0d180ab306ca6045228e3558de7a 100644 (file)
@@ -182,7 +182,7 @@ static void fib_flush(struct net *net)
                struct fib_table *tb;
 
                hlist_for_each_entry_safe(tb, tmp, head, tb_hlist)
-                       flushed += fib_table_flush(tb);
+                       flushed += fib_table_flush(net, tb);
        }
 
        if (flushed)
@@ -590,13 +590,13 @@ int ip_rt_ioctl(struct net *net, unsigned int cmd, void __user *arg)
                        if (cmd == SIOCDELRT) {
                                tb = fib_get_table(net, cfg.fc_table);
                                if (tb)
-                                       err = fib_table_delete(tb, &cfg);
+                                       err = fib_table_delete(net, tb, &cfg);
                                else
                                        err = -ESRCH;
                        } else {
                                tb = fib_new_table(net, cfg.fc_table);
                                if (tb)
-                                       err = fib_table_insert(tb, &cfg);
+                                       err = fib_table_insert(net, tb, &cfg);
                                else
                                        err = -ENOBUFS;
                        }
@@ -719,7 +719,7 @@ static int inet_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh)
                goto errout;
        }
 
-       err = fib_table_delete(tb, &cfg);
+       err = fib_table_delete(net, tb, &cfg);
 errout:
        return err;
 }
@@ -741,7 +741,7 @@ static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh)
                goto errout;
        }
 
-       err = fib_table_insert(tb, &cfg);
+       err = fib_table_insert(net, tb, &cfg);
 errout:
        return err;
 }
@@ -828,9 +828,9 @@ static void fib_magic(int cmd, int type, __be32 dst, int dst_len, struct in_ifad
                cfg.fc_scope = RT_SCOPE_HOST;
 
        if (cmd == RTM_NEWROUTE)
-               fib_table_insert(tb, &cfg);
+               fib_table_insert(net, tb, &cfg);
        else
-               fib_table_delete(tb, &cfg);
+               fib_table_delete(net, tb, &cfg);
 }
 
 void fib_add_ifaddr(struct in_ifaddr *ifa)
@@ -1254,7 +1254,7 @@ static void ip_fib_net_exit(struct net *net)
 
                hlist_for_each_entry_safe(tb, tmp, head, tb_hlist) {
                        hlist_del(&tb->tb_hlist);
-                       fib_table_flush(tb);
+                       fib_table_flush(net, tb);
                        fib_free_table(tb);
                }
        }
index 770bebed6b28823d0490627711c1e1986729474f..ebadf6b994994ddfea68a56e91db618e486c6934 100644 (file)
@@ -164,6 +164,14 @@ static struct fib_table *fib_empty_table(struct net *net)
        return NULL;
 }
 
+static int call_fib_rule_notifiers(struct net *net,
+                                  enum fib_event_type event_type)
+{
+       struct fib_notifier_info info;
+
+       return call_fib_notifiers(net, event_type, &info);
+}
+
 static const struct nla_policy fib4_rule_policy[FRA_MAX+1] = {
        FRA_GENERIC_POLICY,
        [FRA_FLOW]      = { .type = NLA_U32 },
@@ -221,6 +229,7 @@ static int fib4_rule_configure(struct fib_rule *rule, struct sk_buff *skb,
 
        net->ipv4.fib_has_custom_rules = true;
        fib_flush_external(rule->fr_net);
+       call_fib_rule_notifiers(net, FIB_EVENT_RULE_ADD);
 
        err = 0;
 errout:
@@ -243,6 +252,7 @@ static int fib4_rule_delete(struct fib_rule *rule)
 #endif
        net->ipv4.fib_has_custom_rules = true;
        fib_flush_external(rule->fr_net);
+       call_fib_rule_notifiers(net, FIB_EVENT_RULE_DEL);
 errout:
        return err;
 }
index 241f27bbd7adcc782d63a2961d3b258ef649325c..51a4537eb1451699bbeb52b11282ab676fff8268 100644 (file)
@@ -73,6 +73,7 @@
 #include <linux/slab.h>
 #include <linux/export.h>
 #include <linux/vmalloc.h>
+#include <linux/notifier.h>
 #include <net/net_namespace.h>
 #include <net/ip.h>
 #include <net/protocol.h>
 #include <trace/events/fib.h>
 #include "fib_lookup.h"
 
+static BLOCKING_NOTIFIER_HEAD(fib_chain);
+
+int register_fib_notifier(struct notifier_block *nb)
+{
+       return blocking_notifier_chain_register(&fib_chain, nb);
+}
+EXPORT_SYMBOL(register_fib_notifier);
+
+int unregister_fib_notifier(struct notifier_block *nb)
+{
+       return blocking_notifier_chain_unregister(&fib_chain, nb);
+}
+EXPORT_SYMBOL(unregister_fib_notifier);
+
+int call_fib_notifiers(struct net *net, enum fib_event_type event_type,
+                      struct fib_notifier_info *info)
+{
+       info->net = net;
+       return blocking_notifier_call_chain(&fib_chain, event_type, info);
+}
+
+static int call_fib_entry_notifiers(struct net *net,
+                                   enum fib_event_type event_type, u32 dst,
+                                   int dst_len, struct fib_info *fi,
+                                   u8 tos, u8 type, u32 tb_id, u32 nlflags)
+{
+       struct fib_entry_notifier_info info = {
+               .dst = dst,
+               .dst_len = dst_len,
+               .fi = fi,
+               .tos = tos,
+               .type = type,
+               .tb_id = tb_id,
+               .nlflags = nlflags,
+       };
+       return call_fib_notifiers(net, event_type, &info.info);
+}
+
 #define MAX_STAT_DEPTH 32
 
 #define KEYLENGTH      (8*sizeof(t_key))
@@ -1076,7 +1115,8 @@ static int fib_insert_alias(struct trie *t, struct key_vector *tp,
 }
 
 /* Caller must hold RTNL. */
-int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)
+int fib_table_insert(struct net *net, struct fib_table *tb,
+                    struct fib_config *cfg)
 {
        struct trie *t = (struct trie *)tb->tb_data;
        struct fib_alias *fa, *new_fa;
@@ -1193,6 +1233,11 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)
                        fib_release_info(fi_drop);
                        if (state & FA_S_ACCESSED)
                                rt_cache_flush(cfg->fc_nlinfo.nl_net);
+
+                       call_fib_entry_notifiers(net, FIB_EVENT_ENTRY_ADD,
+                                                key, plen, fi,
+                                                new_fa->fa_tos, cfg->fc_type,
+                                                tb->tb_id, cfg->fc_nlflags);
                        rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen,
                                tb->tb_id, &cfg->fc_nlinfo, nlflags);
 
@@ -1245,6 +1290,8 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)
                tb->tb_num_default++;
 
        rt_cache_flush(cfg->fc_nlinfo.nl_net);
+       call_fib_entry_notifiers(net, FIB_EVENT_ENTRY_ADD, key, plen, fi, tos,
+                                cfg->fc_type, tb->tb_id, cfg->fc_nlflags);
        rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen, new_fa->tb_id,
                  &cfg->fc_nlinfo, nlflags);
 succeeded:
@@ -1490,7 +1537,8 @@ static void fib_remove_alias(struct trie *t, struct key_vector *tp,
 }
 
 /* Caller must hold RTNL. */
-int fib_table_delete(struct fib_table *tb, struct fib_config *cfg)
+int fib_table_delete(struct net *net, struct fib_table *tb,
+                    struct fib_config *cfg)
 {
        struct trie *t = (struct trie *) tb->tb_data;
        struct fib_alias *fa, *fa_to_delete;
@@ -1546,6 +1594,9 @@ int fib_table_delete(struct fib_table *tb, struct fib_config *cfg)
        switchdev_fib_ipv4_del(key, plen, fa_to_delete->fa_info, tos,
                               cfg->fc_type, tb->tb_id);
 
+       call_fib_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, key, plen,
+                                fa_to_delete->fa_info, tos, cfg->fc_type,
+                                tb->tb_id, 0);
        rtmsg_fib(RTM_DELROUTE, htonl(key), fa_to_delete, plen, tb->tb_id,
                  &cfg->fc_nlinfo, 0);
 
@@ -1809,7 +1860,7 @@ void fib_table_flush_external(struct fib_table *tb)
 }
 
 /* Caller must hold RTNL. */
-int fib_table_flush(struct fib_table *tb)
+int fib_table_flush(struct net *net, struct fib_table *tb)
 {
        struct trie *t = (struct trie *)tb->tb_data;
        struct key_vector *pn = t->kv;
@@ -1861,6 +1912,11 @@ int fib_table_flush(struct fib_table *tb)
                        switchdev_fib_ipv4_del(n->key, KEYLENGTH - fa->fa_slen,
                                               fi, fa->fa_tos, fa->fa_type,
                                               tb->tb_id);
+                       call_fib_entry_notifiers(net, FIB_EVENT_ENTRY_DEL,
+                                                n->key,
+                                                KEYLENGTH - fa->fa_slen,
+                                                fi, fa->fa_tos, fa->fa_type,
+                                                tb->tb_id, 0);
                        hlist_del_rcu(&fa->fa_list);
                        fib_release_info(fa->fa_info);
                        alias_free_mem_rcu(fa);