netlink: Diag core and basic socket info dumping (v2)
[linux-2.6-block.git] / net / netlink / diag.c
1 #include <linux/module.h>
2
3 #include <net/sock.h>
4 #include <linux/netlink.h>
5 #include <linux/sock_diag.h>
6 #include <linux/netlink_diag.h>
7
8 #include "af_netlink.h"
9
10 static int sk_diag_dump_groups(struct sock *sk, struct sk_buff *nlskb)
11 {
12         struct netlink_sock *nlk = nlk_sk(sk);
13
14         if (nlk->groups == NULL)
15                 return 0;
16
17         return nla_put(nlskb, NETLINK_DIAG_GROUPS, NLGRPSZ(nlk->ngroups),
18                        nlk->groups);
19 }
20
21 static int sk_diag_fill(struct sock *sk, struct sk_buff *skb,
22                         struct netlink_diag_req *req,
23                         u32 portid, u32 seq, u32 flags, int sk_ino)
24 {
25         struct nlmsghdr *nlh;
26         struct netlink_diag_msg *rep;
27         struct netlink_sock *nlk = nlk_sk(sk);
28
29         nlh = nlmsg_put(skb, portid, seq, SOCK_DIAG_BY_FAMILY, sizeof(*rep),
30                         flags);
31         if (!nlh)
32                 return -EMSGSIZE;
33
34         rep = nlmsg_data(nlh);
35         rep->ndiag_family       = AF_NETLINK;
36         rep->ndiag_type         = sk->sk_type;
37         rep->ndiag_protocol     = sk->sk_protocol;
38         rep->ndiag_state        = sk->sk_state;
39
40         rep->ndiag_ino          = sk_ino;
41         rep->ndiag_portid       = nlk->portid;
42         rep->ndiag_dst_portid   = nlk->dst_portid;
43         rep->ndiag_dst_group    = nlk->dst_group;
44         sock_diag_save_cookie(sk, rep->ndiag_cookie);
45
46         if ((req->ndiag_show & NDIAG_SHOW_GROUPS) &&
47             sk_diag_dump_groups(sk, skb))
48                 goto out_nlmsg_trim;
49
50         if ((req->ndiag_show & NDIAG_SHOW_MEMINFO) &&
51             sock_diag_put_meminfo(sk, skb, NETLINK_DIAG_MEMINFO))
52                 goto out_nlmsg_trim;
53
54         return nlmsg_end(skb, nlh);
55
56 out_nlmsg_trim:
57         nlmsg_cancel(skb, nlh);
58         return -EMSGSIZE;
59 }
60
61 static int __netlink_diag_dump(struct sk_buff *skb, struct netlink_callback *cb,
62                                 int protocol, int s_num)
63 {
64         struct netlink_table *tbl = &nl_table[protocol];
65         struct nl_portid_hash *hash = &tbl->hash;
66         struct net *net = sock_net(skb->sk);
67         struct netlink_diag_req *req;
68         struct sock *sk;
69         int ret = 0, num = 0, i;
70
71         req = nlmsg_data(cb->nlh);
72
73         for (i = 0; i <= hash->mask; i++) {
74                 sk_for_each(sk, &hash->table[i]) {
75                         if (!net_eq(sock_net(sk), net))
76                                 continue;
77                         if (num < s_num) {
78                                 num++;
79                                 continue;
80                         }
81
82                         if (sk_diag_fill(sk, skb, req,
83                                          NETLINK_CB(cb->skb).portid,
84                                          cb->nlh->nlmsg_seq,
85                                          NLM_F_MULTI,
86                                          sock_i_ino(sk)) < 0) {
87                                 ret = 1;
88                                 goto done;
89                         }
90
91                         num++;
92                 }
93         }
94
95         sk_for_each_bound(sk, &tbl->mc_list) {
96                 if (sk_hashed(sk))
97                         continue;
98                 if (!net_eq(sock_net(sk), net))
99                         continue;
100                 if (num < s_num) {
101                         num++;
102                         continue;
103                 }
104
105                 if (sk_diag_fill(sk, skb, req,
106                                  NETLINK_CB(cb->skb).portid,
107                                  cb->nlh->nlmsg_seq,
108                                  NLM_F_MULTI,
109                                  sock_i_ino(sk)) < 0) {
110                         ret = 1;
111                         goto done;
112                 }
113                 num++;
114         }
115 done:
116         cb->args[0] = num;
117         cb->args[1] = protocol;
118
119         return ret;
120 }
121
122 static int netlink_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
123 {
124         struct netlink_diag_req *req;
125         int s_num = cb->args[0];
126
127         req = nlmsg_data(cb->nlh);
128
129         read_lock(&nl_table_lock);
130
131         if (req->sdiag_protocol == NDIAG_PROTO_ALL) {
132                 int i;
133
134                 for (i = cb->args[1]; i < MAX_LINKS; i++) {
135                         if (__netlink_diag_dump(skb, cb, i, s_num))
136                                 break;
137                         s_num = 0;
138                 }
139         } else {
140                 if (req->sdiag_protocol >= MAX_LINKS) {
141                         read_unlock(&nl_table_lock);
142                         return -ENOENT;
143                 }
144
145                 __netlink_diag_dump(skb, cb, req->sdiag_protocol, s_num);
146         }
147
148         read_unlock(&nl_table_lock);
149
150         return skb->len;
151 }
152
153 static int netlink_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h)
154 {
155         int hdrlen = sizeof(struct netlink_diag_req);
156         struct net *net = sock_net(skb->sk);
157
158         if (nlmsg_len(h) < hdrlen)
159                 return -EINVAL;
160
161         if (h->nlmsg_flags & NLM_F_DUMP) {
162                 struct netlink_dump_control c = {
163                         .dump = netlink_diag_dump,
164                 };
165                 return netlink_dump_start(net->diag_nlsk, skb, h, &c);
166         } else
167                 return -EOPNOTSUPP;
168 }
169
170 static const struct sock_diag_handler netlink_diag_handler = {
171         .family = AF_NETLINK,
172         .dump = netlink_diag_handler_dump,
173 };
174
175 static int __init netlink_diag_init(void)
176 {
177         return sock_diag_register(&netlink_diag_handler);
178 }
179
180 static void __exit netlink_diag_exit(void)
181 {
182         sock_diag_unregister(&netlink_diag_handler);
183 }
184
185 module_init(netlink_diag_init);
186 module_exit(netlink_diag_exit);
187 MODULE_LICENSE("GPL");
188 MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 16 /* AF_NETLINK */);