blob: 5ffb1d1cf4026b7e289e83a573a1b2a13d01fe03 [file] [log] [blame]
Andrey Vagineaaa3132013-03-21 20:33:48 +04001#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
10static 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
21static 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
56out_nlmsg_trim:
57 nlmsg_cancel(skb, nlh);
58 return -EMSGSIZE;
59}
60
61static 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 }
115done:
116 cb->args[0] = num;
117 cb->args[1] = protocol;
118
119 return ret;
120}
121
122static 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
153static 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
170static const struct sock_diag_handler netlink_diag_handler = {
171 .family = AF_NETLINK,
172 .dump = netlink_diag_handler_dump,
173};
174
175static int __init netlink_diag_init(void)
176{
177 return sock_diag_register(&netlink_diag_handler);
178}
179
180static void __exit netlink_diag_exit(void)
181{
182 sock_diag_unregister(&netlink_diag_handler);
183}
184
185module_init(netlink_diag_init);
186module_exit(netlink_diag_exit);
187MODULE_LICENSE("GPL");
188MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 16 /* AF_NETLINK */);