Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
[linux-2.6-microblaze.git] / net / ipv4 / tcp_ipv4.c
index 04bfcbb..116c11a 100644 (file)
@@ -2223,13 +2223,18 @@ EXPORT_SYMBOL(tcp_v4_destroy_sock);
  */
 static void *listening_get_next(struct seq_file *seq, void *cur)
 {
-       struct tcp_seq_afinfo *afinfo = PDE_DATA(file_inode(seq->file));
+       struct tcp_seq_afinfo *afinfo;
        struct tcp_iter_state *st = seq->private;
        struct net *net = seq_file_net(seq);
        struct inet_listen_hashbucket *ilb;
        struct hlist_nulls_node *node;
        struct sock *sk = cur;
 
+       if (st->bpf_seq_afinfo)
+               afinfo = st->bpf_seq_afinfo;
+       else
+               afinfo = PDE_DATA(file_inode(seq->file));
+
        if (!sk) {
 get_head:
                ilb = &tcp_hashinfo.listening_hash[st->bucket];
@@ -2247,7 +2252,8 @@ get_sk:
        sk_nulls_for_each_from(sk, node) {
                if (!net_eq(sock_net(sk), net))
                        continue;
-               if (sk->sk_family == afinfo->family)
+               if (afinfo->family == AF_UNSPEC ||
+                   sk->sk_family == afinfo->family)
                        return sk;
        }
        spin_unlock(&ilb->lock);
@@ -2284,11 +2290,16 @@ static inline bool empty_bucket(const struct tcp_iter_state *st)
  */
 static void *established_get_first(struct seq_file *seq)
 {
-       struct tcp_seq_afinfo *afinfo = PDE_DATA(file_inode(seq->file));
+       struct tcp_seq_afinfo *afinfo;
        struct tcp_iter_state *st = seq->private;
        struct net *net = seq_file_net(seq);
        void *rc = NULL;
 
+       if (st->bpf_seq_afinfo)
+               afinfo = st->bpf_seq_afinfo;
+       else
+               afinfo = PDE_DATA(file_inode(seq->file));
+
        st->offset = 0;
        for (; st->bucket <= tcp_hashinfo.ehash_mask; ++st->bucket) {
                struct sock *sk;
@@ -2301,7 +2312,8 @@ static void *established_get_first(struct seq_file *seq)
 
                spin_lock_bh(lock);
                sk_nulls_for_each(sk, node, &tcp_hashinfo.ehash[st->bucket].chain) {
-                       if (sk->sk_family != afinfo->family ||
+                       if ((afinfo->family != AF_UNSPEC &&
+                            sk->sk_family != afinfo->family) ||
                            !net_eq(sock_net(sk), net)) {
                                continue;
                        }
@@ -2316,19 +2328,25 @@ out:
 
 static void *established_get_next(struct seq_file *seq, void *cur)
 {
-       struct tcp_seq_afinfo *afinfo = PDE_DATA(file_inode(seq->file));
+       struct tcp_seq_afinfo *afinfo;
        struct sock *sk = cur;
        struct hlist_nulls_node *node;
        struct tcp_iter_state *st = seq->private;
        struct net *net = seq_file_net(seq);
 
+       if (st->bpf_seq_afinfo)
+               afinfo = st->bpf_seq_afinfo;
+       else
+               afinfo = PDE_DATA(file_inode(seq->file));
+
        ++st->num;
        ++st->offset;
 
        sk = sk_nulls_next(sk);
 
        sk_nulls_for_each_from(sk, node) {
-               if (sk->sk_family == afinfo->family &&
+               if ((afinfo->family == AF_UNSPEC ||
+                    sk->sk_family == afinfo->family) &&
                    net_eq(sock_net(sk), net))
                        return sk;
        }
@@ -2607,6 +2625,74 @@ out:
        return 0;
 }
 
+#ifdef CONFIG_BPF_SYSCALL
+struct bpf_iter__tcp {
+       __bpf_md_ptr(struct bpf_iter_meta *, meta);
+       __bpf_md_ptr(struct sock_common *, sk_common);
+       uid_t uid __aligned(8);
+};
+
+static int tcp_prog_seq_show(struct bpf_prog *prog, struct bpf_iter_meta *meta,
+                            struct sock_common *sk_common, uid_t uid)
+{
+       struct bpf_iter__tcp ctx;
+
+       meta->seq_num--;  /* skip SEQ_START_TOKEN */
+       ctx.meta = meta;
+       ctx.sk_common = sk_common;
+       ctx.uid = uid;
+       return bpf_iter_run_prog(prog, &ctx);
+}
+
+static int bpf_iter_tcp_seq_show(struct seq_file *seq, void *v)
+{
+       struct bpf_iter_meta meta;
+       struct bpf_prog *prog;
+       struct sock *sk = v;
+       uid_t uid;
+
+       if (v == SEQ_START_TOKEN)
+               return 0;
+
+       if (sk->sk_state == TCP_TIME_WAIT) {
+               uid = 0;
+       } else if (sk->sk_state == TCP_NEW_SYN_RECV) {
+               const struct request_sock *req = v;
+
+               uid = from_kuid_munged(seq_user_ns(seq),
+                                      sock_i_uid(req->rsk_listener));
+       } else {
+               uid = from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk));
+       }
+
+       meta.seq = seq;
+       prog = bpf_iter_get_info(&meta, false);
+       return tcp_prog_seq_show(prog, &meta, v, uid);
+}
+
+static void bpf_iter_tcp_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)tcp_prog_seq_show(prog, &meta, v, 0);
+       }
+
+       tcp_seq_stop(seq, v);
+}
+
+static const struct seq_operations bpf_iter_tcp_seq_ops = {
+       .show           = bpf_iter_tcp_seq_show,
+       .start          = tcp_seq_start,
+       .next           = tcp_seq_next,
+       .stop           = bpf_iter_tcp_seq_stop,
+};
+#endif
+
 static const struct seq_operations tcp4_seq_ops = {
        .show           = tcp4_seq_show,
        .start          = tcp_seq_start,
@@ -2838,8 +2924,63 @@ static struct pernet_operations __net_initdata tcp_sk_ops = {
        .exit_batch = tcp_sk_exit_batch,
 };
 
+#if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS)
+DEFINE_BPF_ITER_FUNC(tcp, struct bpf_iter_meta *meta,
+                    struct sock_common *sk_common, uid_t uid)
+
+static int bpf_iter_init_tcp(void *priv_data)
+{
+       struct tcp_iter_state *st = priv_data;
+       struct tcp_seq_afinfo *afinfo;
+       int ret;
+
+       afinfo = kmalloc(sizeof(*afinfo), GFP_USER | __GFP_NOWARN);
+       if (!afinfo)
+               return -ENOMEM;
+
+       afinfo->family = AF_UNSPEC;
+       st->bpf_seq_afinfo = afinfo;
+       ret = bpf_iter_init_seq_net(priv_data);
+       if (ret)
+               kfree(afinfo);
+       return ret;
+}
+
+static void bpf_iter_fini_tcp(void *priv_data)
+{
+       struct tcp_iter_state *st = priv_data;
+
+       kfree(st->bpf_seq_afinfo);
+       bpf_iter_fini_seq_net(priv_data);
+}
+
+static const struct bpf_iter_reg tcp_reg_info = {
+       .target                 = "tcp",
+       .seq_ops                = &bpf_iter_tcp_seq_ops,
+       .init_seq_private       = bpf_iter_init_tcp,
+       .fini_seq_private       = bpf_iter_fini_tcp,
+       .seq_priv_size          = sizeof(struct tcp_iter_state),
+       .ctx_arg_info_size      = 1,
+       .ctx_arg_info           = {
+               { offsetof(struct bpf_iter__tcp, sk_common),
+                 PTR_TO_BTF_ID_OR_NULL },
+       },
+};
+
+static void __init bpf_iter_register(void)
+{
+       if (bpf_iter_reg_target(&tcp_reg_info))
+               pr_warn("Warning: could not register bpf iterator tcp\n");
+}
+
+#endif
+
 void __init tcp_v4_init(void)
 {
        if (register_pernet_subsys(&tcp_sk_ops))
                panic("Failed to create the TCP control socket.\n");
+
+#if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS)
+       bpf_iter_register();
+#endif
 }