net: bpf: Add netlink and ipv6_route bpf_iter targets
authorYonghong Song <yhs@fb.com>
Sat, 9 May 2020 17:59:10 +0000 (10:59 -0700)
committerAlexei Starovoitov <ast@kernel.org>
Sun, 10 May 2020 00:05:26 +0000 (17:05 -0700)
This patch added netlink and ipv6_route targets, using
the same seq_ops (except show() and minor changes for stop())
for /proc/net/{netlink,ipv6_route}.

The net namespace for these targets are the current net
namespace at file open stage, similar to
/proc/net/{netlink,ipv6_route} reference counting
the net namespace at seq_file open stage.

Since module is not supported for now, ipv6_route is
supported only if the IPV6 is built-in, i.e., not compiled
as a module. The restriction can be lifted once module
is properly supported for bpf_iter.

Signed-off-by: Yonghong Song <yhs@fb.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Andrii Nakryiko <andriin@fb.com>
Link: https://lore.kernel.org/bpf/20200509175910.2476329-1-yhs@fb.com
fs/proc/proc_net.c
include/linux/proc_fs.h
net/ipv6/ip6_fib.c
net/ipv6/route.c
net/netlink/af_netlink.c

index 4888c52..dba63b2 100644 (file)
@@ -98,6 +98,25 @@ static const struct proc_ops proc_net_seq_ops = {
        .proc_release   = seq_release_net,
 };
 
+int bpf_iter_init_seq_net(void *priv_data)
+{
+#ifdef CONFIG_NET_NS
+       struct seq_net_private *p = priv_data;
+
+       p->net = get_net(current->nsproxy->net_ns);
+#endif
+       return 0;
+}
+
+void bpf_iter_fini_seq_net(void *priv_data)
+{
+#ifdef CONFIG_NET_NS
+       struct seq_net_private *p = priv_data;
+
+       put_net(p->net);
+#endif
+}
+
 struct proc_dir_entry *proc_create_net_data(const char *name, umode_t mode,
                struct proc_dir_entry *parent, const struct seq_operations *ops,
                unsigned int state_size, void *data)
index 45c05fd..03953c5 100644 (file)
@@ -105,6 +105,9 @@ struct proc_dir_entry *proc_create_net_single_write(const char *name, umode_t mo
                                                    void *data);
 extern struct pid *tgid_pidfd_to_pid(const struct file *file);
 
+extern int bpf_iter_init_seq_net(void *priv_data);
+extern void bpf_iter_fini_seq_net(void *priv_data);
+
 #ifdef CONFIG_PROC_PID_ARCH_STATUS
 /*
  * The architecture which selects CONFIG_PROC_PID_ARCH_STATUS must
index 46ed567..a1fcc0c 100644 (file)
@@ -2467,7 +2467,7 @@ void fib6_gc_cleanup(void)
 }
 
 #ifdef CONFIG_PROC_FS
-static int ipv6_route_seq_show(struct seq_file *seq, void *v)
+static int ipv6_route_native_seq_show(struct seq_file *seq, void *v)
 {
        struct fib6_info *rt = v;
        struct ipv6_route_iter *iter = seq->private;
@@ -2625,7 +2625,7 @@ static bool ipv6_route_iter_active(struct ipv6_route_iter *iter)
        return w->node && !(w->state == FWS_U && w->node == w->root);
 }
 
-static void ipv6_route_seq_stop(struct seq_file *seq, void *v)
+static void ipv6_route_native_seq_stop(struct seq_file *seq, void *v)
        __releases(RCU_BH)
 {
        struct net *net = seq_file_net(seq);
@@ -2637,6 +2637,67 @@ static void ipv6_route_seq_stop(struct seq_file *seq, void *v)
        rcu_read_unlock_bh();
 }
 
+#if IS_BUILTIN(CONFIG_IPV6) && defined(CONFIG_BPF_SYSCALL)
+struct bpf_iter__ipv6_route {
+       __bpf_md_ptr(struct bpf_iter_meta *, meta);
+       __bpf_md_ptr(struct fib6_info *, rt);
+};
+
+static int ipv6_route_prog_seq_show(struct bpf_prog *prog,
+                                   struct bpf_iter_meta *meta,
+                                   void *v)
+{
+       struct bpf_iter__ipv6_route ctx;
+
+       ctx.meta = meta;
+       ctx.rt = v;
+       return bpf_iter_run_prog(prog, &ctx);
+}
+
+static int ipv6_route_seq_show(struct seq_file *seq, void *v)
+{
+       struct ipv6_route_iter *iter = seq->private;
+       struct bpf_iter_meta meta;
+       struct bpf_prog *prog;
+       int ret;
+
+       meta.seq = seq;
+       prog = bpf_iter_get_info(&meta, false);
+       if (!prog)
+               return ipv6_route_native_seq_show(seq, v);
+
+       ret = ipv6_route_prog_seq_show(prog, &meta, v);
+       iter->w.leaf = NULL;
+
+       return ret;
+}
+
+static void ipv6_route_seq_stop(struct seq_file *seq, void *v)
+{
+       struct bpf_iter_meta meta;
+       struct bpf_prog *prog;
+
+       if (!v) {
+               meta.seq = seq;
+               prog = bpf_iter_get_info(&meta, true);
+               if (prog)
+                       (void)ipv6_route_prog_seq_show(prog, &meta, v);
+       }
+
+       ipv6_route_native_seq_stop(seq, v);
+}
+#else
+static int ipv6_route_seq_show(struct seq_file *seq, void *v)
+{
+       return ipv6_route_native_seq_show(seq, v);
+}
+
+static void ipv6_route_seq_stop(struct seq_file *seq, void *v)
+{
+       ipv6_route_native_seq_stop(seq, v);
+}
+#endif
+
 const struct seq_operations ipv6_route_seq_ops = {
        .start  = ipv6_route_seq_start,
        .next   = ipv6_route_seq_next,
index 3912aac..25f6d3e 100644 (file)
@@ -6393,6 +6393,30 @@ void __init ip6_route_init_special_entries(void)
   #endif
 }
 
+#if IS_BUILTIN(CONFIG_IPV6)
+#if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS)
+DEFINE_BPF_ITER_FUNC(ipv6_route, struct bpf_iter_meta *meta, struct fib6_info *rt)
+
+static int __init bpf_iter_register(void)
+{
+       struct bpf_iter_reg reg_info = {
+               .target                 = "ipv6_route",
+               .seq_ops                = &ipv6_route_seq_ops,
+               .init_seq_private       = bpf_iter_init_seq_net,
+               .fini_seq_private       = bpf_iter_fini_seq_net,
+               .seq_priv_size          = sizeof(struct ipv6_route_iter),
+       };
+
+       return bpf_iter_reg_target(&reg_info);
+}
+
+static void bpf_iter_unregister(void)
+{
+       bpf_iter_unreg_target("ipv6_route");
+}
+#endif
+#endif
+
 int __init ip6_route_init(void)
 {
        int ret;
@@ -6455,6 +6479,14 @@ int __init ip6_route_init(void)
        if (ret)
                goto out_register_late_subsys;
 
+#if IS_BUILTIN(CONFIG_IPV6)
+#if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS)
+       ret = bpf_iter_register();
+       if (ret)
+               goto out_register_late_subsys;
+#endif
+#endif
+
        for_each_possible_cpu(cpu) {
                struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu);
 
@@ -6487,6 +6519,11 @@ out_kmem_cache:
 
 void ip6_route_cleanup(void)
 {
+#if IS_BUILTIN(CONFIG_IPV6)
+#if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS)
+       bpf_iter_unregister();
+#endif
+#endif
        unregister_netdevice_notifier(&ip6_route_dev_notifier);
        unregister_pernet_subsys(&ip6_route_net_late_ops);
        fib6_rules_cleanup();
index 5ded01c..33cda9b 100644 (file)
@@ -2596,7 +2596,7 @@ static void *netlink_seq_next(struct seq_file *seq, void *v, loff_t *pos)
        return __netlink_seq_next(seq);
 }
 
-static void netlink_seq_stop(struct seq_file *seq, void *v)
+static void netlink_native_seq_stop(struct seq_file *seq, void *v)
 {
        struct nl_seq_iter *iter = seq->private;
 
@@ -2607,7 +2607,7 @@ static void netlink_seq_stop(struct seq_file *seq, void *v)
 }
 
 
-static int netlink_seq_show(struct seq_file *seq, void *v)
+static int netlink_native_seq_show(struct seq_file *seq, void *v)
 {
        if (v == SEQ_START_TOKEN) {
                seq_puts(seq,
@@ -2634,6 +2634,68 @@ static int netlink_seq_show(struct seq_file *seq, void *v)
        return 0;
 }
 
+#ifdef CONFIG_BPF_SYSCALL
+struct bpf_iter__netlink {
+       __bpf_md_ptr(struct bpf_iter_meta *, meta);
+       __bpf_md_ptr(struct netlink_sock *, sk);
+};
+
+DEFINE_BPF_ITER_FUNC(netlink, struct bpf_iter_meta *meta, struct netlink_sock *sk)
+
+static int netlink_prog_seq_show(struct bpf_prog *prog,
+                                 struct bpf_iter_meta *meta,
+                                 void *v)
+{
+       struct bpf_iter__netlink ctx;
+
+       meta->seq_num--;  /* skip SEQ_START_TOKEN */
+       ctx.meta = meta;
+       ctx.sk = nlk_sk((struct sock *)v);
+       return bpf_iter_run_prog(prog, &ctx);
+}
+
+static int netlink_seq_show(struct seq_file *seq, void *v)
+{
+       struct bpf_iter_meta meta;
+       struct bpf_prog *prog;
+
+       meta.seq = seq;
+       prog = bpf_iter_get_info(&meta, false);
+       if (!prog)
+               return netlink_native_seq_show(seq, v);
+
+       if (v != SEQ_START_TOKEN)
+               return netlink_prog_seq_show(prog, &meta, v);
+
+       return 0;
+}
+
+static void netlink_seq_stop(struct seq_file *seq, void *v)
+{
+       struct bpf_iter_meta meta;
+       struct bpf_prog *prog;
+
+       if (!v) {
+               meta.seq = seq;
+               prog = bpf_iter_get_info(&meta, true);
+               if (prog)
+                       (void)netlink_prog_seq_show(prog, &meta, v);
+       }
+
+       netlink_native_seq_stop(seq, v);
+}
+#else
+static int netlink_seq_show(struct seq_file *seq, void *v)
+{
+       return netlink_native_seq_show(seq, v);
+}
+
+static void netlink_seq_stop(struct seq_file *seq, void *v)
+{
+       netlink_native_seq_stop(seq, v);
+}
+#endif
+
 static const struct seq_operations netlink_seq_ops = {
        .start  = netlink_seq_start,
        .next   = netlink_seq_next,
@@ -2740,6 +2802,21 @@ static const struct rhashtable_params netlink_rhashtable_params = {
        .automatic_shrinking = true,
 };
 
+#if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS)
+static int __init bpf_iter_register(void)
+{
+       struct bpf_iter_reg reg_info = {
+               .target                 = "netlink",
+               .seq_ops                = &netlink_seq_ops,
+               .init_seq_private       = bpf_iter_init_seq_net,
+               .fini_seq_private       = bpf_iter_fini_seq_net,
+               .seq_priv_size          = sizeof(struct nl_seq_iter),
+       };
+
+       return bpf_iter_reg_target(&reg_info);
+}
+#endif
+
 static int __init netlink_proto_init(void)
 {
        int i;
@@ -2748,6 +2825,12 @@ static int __init netlink_proto_init(void)
        if (err != 0)
                goto out;
 
+#if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS)
+       err = bpf_iter_register();
+       if (err)
+               goto out;
+#endif
+
        BUILD_BUG_ON(sizeof(struct netlink_skb_parms) > sizeof_field(struct sk_buff, cb));
 
        nl_table = kcalloc(MAX_LINKS, sizeof(*nl_table), GFP_KERNEL);