diff options
author | Andrey Vagin <avagin@openvz.org> | 2013-03-21 12:33:48 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-03-21 12:38:03 -0400 |
commit | eaaa31392690c7609f7afeec5ba38a79d009842d (patch) | |
tree | 226d0deb15b150c1473b5f813414330d28814855 /net/netlink/diag.c | |
parent | 0f29c768646809264d603574b4a1b15d2ff7ad79 (diff) |
netlink: Diag core and basic socket info dumping (v2)
The netlink_diag can be built as a module, just like it's done in
unix sockets.
The core dumping message carries the basic info about netlink sockets:
family, type and protocol, portis, dst_group, dst_portid, state.
Groups can be received as an optional parameter NETLINK_DIAG_GROUPS.
Netlink sockets cab be filtered by protocols.
The socket inode number and cookie is reserved for future per-socket info
retrieving. The per-protocol filtering is also reserved for future by
requiring the sdiag_protocol to be zero.
The file /proc/net/netlink doesn't provide enough information for
dumping netlink sockets. It doesn't provide dst_group, dst_portid,
groups above 32.
v2: fix NETLINK_DIAG_MAX. Now it's equal to the last constant.
Acked-by: Pavel Emelyanov <xemul@parallels.com>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Pablo Neira Ayuso <pablo@netfilter.org>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Gao feng <gaofeng@cn.fujitsu.com>
Cc: Thomas Graf <tgraf@suug.ch>
Signed-off-by: Andrey Vagin <avagin@openvz.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/netlink/diag.c')
-rw-r--r-- | net/netlink/diag.c | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/net/netlink/diag.c b/net/netlink/diag.c new file mode 100644 index 000000000000..5ffb1d1cf402 --- /dev/null +++ b/net/netlink/diag.c | |||
@@ -0,0 +1,188 @@ | |||
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 */); | ||