extern int fib_rules_lookup(struct fib_rules_ops *,
struct flowi *, int flags,
struct fib_lookup_arg *);
-
-extern int fib_rules_dump(struct sk_buff *,
- struct netlink_callback *, int);
#endif
extern void rtnl_unregister_all(int protocol);
extern int rtnl_dump_all(struct sk_buff *skb, struct netlink_callback *cb);
+static inline int rtnl_msg_family(struct nlmsghdr *nlh)
+{
+ if (nlmsg_len(nlh) >= sizeof(struct rtgenmsg))
+ return ((struct rtgenmsg *) nlmsg_data(nlh))->rtgen_family;
+ else
+ return AF_UNSPEC;
+}
+
#endif
return -EMSGSIZE;
}
-int fib_rules_dump(struct sk_buff *skb, struct netlink_callback *cb, int family)
+static int dump_rules(struct sk_buff *skb, struct netlink_callback *cb,
+ struct fib_rules_ops *ops)
{
int idx = 0;
struct fib_rule *rule;
- struct fib_rules_ops *ops;
-
- ops = lookup_rules_ops(family);
- if (ops == NULL)
- return -EAFNOSUPPORT;
rcu_read_lock();
list_for_each_entry_rcu(rule, ops->rules_list, list) {
- if (idx < cb->args[0])
+ if (idx < cb->args[1])
goto skip;
if (fib_nl_fill_rule(skb, rule, NETLINK_CB(cb->skb).pid,
idx++;
}
rcu_read_unlock();
- cb->args[0] = idx;
+ cb->args[1] = idx;
rules_ops_put(ops);
return skb->len;
}
-EXPORT_SYMBOL_GPL(fib_rules_dump);
+static int fib_nl_dumprule(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct fib_rules_ops *ops;
+ int idx = 0, family;
+
+ family = rtnl_msg_family(cb->nlh);
+ if (family != AF_UNSPEC) {
+ /* Protocol specific dump request */
+ ops = lookup_rules_ops(family);
+ if (ops == NULL)
+ return -EAFNOSUPPORT;
+
+ return dump_rules(skb, cb, ops);
+ }
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(ops, &rules_ops, list) {
+ if (idx < cb->args[0] || !try_module_get(ops->owner))
+ goto skip;
+
+ if (dump_rules(skb, cb, ops) < 0)
+ break;
+
+ cb->args[1] = 0;
+ skip:
+ idx++;
+ }
+ rcu_read_unlock();
+ cb->args[0] = idx;
+
+ return skb->len;
+}
static void notify_rule_change(int event, struct fib_rule *rule,
struct fib_rules_ops *ops, struct nlmsghdr *nlh,
{
rtnl_register(PF_UNSPEC, RTM_NEWRULE, fib_nl_newrule, NULL);
rtnl_register(PF_UNSPEC, RTM_DELRULE, fib_nl_delrule, NULL);
- rtnl_register(PF_UNSPEC, RTM_GETRULE, NULL, rtnl_dump_all);
+ rtnl_register(PF_UNSPEC, RTM_GETRULE, NULL, fib_nl_dumprule);
return register_netdevice_notifier(&fib_rules_notifier);
}
return 0;
}
-static int dn_fib_dump_rules(struct sk_buff *skb, struct netlink_callback *cb)
-{
- return fib_rules_dump(skb, cb, AF_DECnet);
-}
-
static struct fib_rules_ops dn_fib_rules_ops = {
.family = AF_DECnet,
.rule_size = sizeof(struct dn_fib_rule),
{
list_add_tail(&default_rule.common.list, &dn_fib_rules);
fib_rules_register(&dn_fib_rules_ops);
- rtnl_register(PF_DECnet, RTM_GETRULE, NULL, dn_fib_dump_rules);
}
void __exit dn_fib_rules_cleanup(void)
{
- rtnl_unregister(PF_DECnet, RTM_GETRULE);
fib_rules_unregister(&dn_fib_rules_ops);
}
return -ENOBUFS;
}
-static int fib4_rule_dump(struct sk_buff *skb, struct netlink_callback *cb)
-{
- return fib_rules_dump(skb, cb, AF_INET);
-}
-
static u32 fib4_rule_default_pref(void)
{
struct list_head *pos;
list_add_tail(&default_rule.common.list, &fib4_rules);
fib_rules_register(&fib4_rules_ops);
-
- rtnl_register(PF_INET, RTM_GETRULE, NULL, fib4_rule_dump);
}
return -ENOBUFS;
}
-static int fib6_rules_dump(struct sk_buff *skb, struct netlink_callback *cb)
-{
- return fib_rules_dump(skb, cb, AF_INET6);
-}
-
static u32 fib6_rule_default_pref(void)
{
return 0x3FFF;
list_add_tail(&main_rule.common.list, &fib6_rules);
fib_rules_register(&fib6_rules_ops);
- __rtnl_register(PF_INET6, RTM_GETRULE, NULL, fib6_rules_dump);
}
void fib6_rules_cleanup(void)
{
- rtnl_unregister(PF_INET6, RTM_GETRULE);
fib_rules_unregister(&fib6_rules_ops);
}