aboutsummaryrefslogtreecommitdiffstats
path: root/net/tipc
diff options
context:
space:
mode:
authorRichard Alpe <richard.alpe@ericsson.com>2015-02-09 03:50:04 -0500
committerDavid S. Miller <davem@davemloft.net>2015-02-09 16:20:47 -0500
commitd0796d1ef63deb38147729664691ba3090930b26 (patch)
treeb227569cf4d6cd2250000095f1a1867f86d08854 /net/tipc
parentbfb3e5dd8dfd84dfd13649393abab63e43267b00 (diff)
tipc: convert legacy nl bearer dump to nl compat
Introduce a framework for dumping netlink data from the new netlink API and formatting it to the old legacy API format. This is done by looping the dump data and calling a format handler for each entity, in this case a bearer. We dump until either all data is dumped or we reach the limited buffer size of the legacy API. Remember, the legacy API doesn't scale. In this commit we convert TIPC_CMD_GET_BEARER_NAMES to use the compat layer. Signed-off-by: Richard Alpe <richard.alpe@ericsson.com> Reviewed-by: Erik Hugne <erik.hugne@ericsson.com> Reviewed-by: Ying Xue <ying.xue@windriver.com> Reviewed-by: Jon Maloy <jon.maloy@ericsson.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/tipc')
-rw-r--r--net/tipc/bearer.c29
-rw-r--r--net/tipc/bearer.h1
-rw-r--r--net/tipc/config.c3
-rw-r--r--net/tipc/netlink_compat.c274
4 files changed, 273 insertions, 34 deletions
diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c
index 35d400e8c2e5..7a9e29641e61 100644
--- a/net/tipc/bearer.c
+++ b/net/tipc/bearer.c
@@ -205,35 +205,6 @@ struct tipc_bearer *tipc_bearer_find(struct net *net, const char *name)
205 return NULL; 205 return NULL;
206} 206}
207 207
208/**
209 * tipc_bearer_get_names - record names of bearers in buffer
210 */
211struct sk_buff *tipc_bearer_get_names(struct net *net)
212{
213 struct tipc_net *tn = net_generic(net, tipc_net_id);
214 struct sk_buff *buf;
215 struct tipc_bearer *b;
216 int i, j;
217
218 buf = tipc_cfg_reply_alloc(MAX_BEARERS * TLV_SPACE(TIPC_MAX_BEARER_NAME));
219 if (!buf)
220 return NULL;
221
222 for (i = 0; media_info_array[i] != NULL; i++) {
223 for (j = 0; j < MAX_BEARERS; j++) {
224 b = rtnl_dereference(tn->bearer_list[j]);
225 if (!b)
226 continue;
227 if (b->media == media_info_array[i]) {
228 tipc_cfg_append_tlv(buf, TIPC_TLV_BEARER_NAME,
229 b->name,
230 strlen(b->name) + 1);
231 }
232 }
233 }
234 return buf;
235}
236
237void tipc_bearer_add_dest(struct net *net, u32 bearer_id, u32 dest) 208void tipc_bearer_add_dest(struct net *net, u32 bearer_id, u32 dest)
238{ 209{
239 struct tipc_net *tn = net_generic(net, tipc_net_id); 210 struct tipc_net *tn = net_generic(net, tipc_net_id);
diff --git a/net/tipc/bearer.h b/net/tipc/bearer.h
index c035e3e24764..956858276d93 100644
--- a/net/tipc/bearer.h
+++ b/net/tipc/bearer.h
@@ -205,7 +205,6 @@ void tipc_disable_l2_media(struct tipc_bearer *b);
205int tipc_l2_send_msg(struct net *net, struct sk_buff *buf, 205int tipc_l2_send_msg(struct net *net, struct sk_buff *buf,
206 struct tipc_bearer *b, struct tipc_media_addr *dest); 206 struct tipc_bearer *b, struct tipc_media_addr *dest);
207 207
208struct sk_buff *tipc_bearer_get_names(struct net *net);
209void tipc_bearer_add_dest(struct net *net, u32 bearer_id, u32 dest); 208void tipc_bearer_add_dest(struct net *net, u32 bearer_id, u32 dest);
210void tipc_bearer_remove_dest(struct net *net, u32 bearer_id, u32 dest); 209void tipc_bearer_remove_dest(struct net *net, u32 bearer_id, u32 dest);
211struct tipc_bearer *tipc_bearer_find(struct net *net, const char *name); 210struct tipc_bearer *tipc_bearer_find(struct net *net, const char *name);
diff --git a/net/tipc/config.c b/net/tipc/config.c
index 6873360cda53..52e84b0ac48a 100644
--- a/net/tipc/config.c
+++ b/net/tipc/config.c
@@ -252,9 +252,6 @@ struct sk_buff *tipc_cfg_do_cmd(struct net *net, u32 orig_node, u16 cmd,
252 rep_tlv_buf = tipc_nametbl_get(net, req_tlv_area, 252 rep_tlv_buf = tipc_nametbl_get(net, req_tlv_area,
253 req_tlv_space); 253 req_tlv_space);
254 break; 254 break;
255 case TIPC_CMD_GET_BEARER_NAMES:
256 rep_tlv_buf = tipc_bearer_get_names(net);
257 break;
258 case TIPC_CMD_GET_MEDIA_NAMES: 255 case TIPC_CMD_GET_MEDIA_NAMES:
259 rep_tlv_buf = tipc_media_get_names(); 256 rep_tlv_buf = tipc_media_get_names();
260 break; 257 break;
diff --git a/net/tipc/netlink_compat.c b/net/tipc/netlink_compat.c
index f752854c8b10..bd75ea290e3a 100644
--- a/net/tipc/netlink_compat.c
+++ b/net/tipc/netlink_compat.c
@@ -33,9 +33,265 @@
33 33
34#include "core.h" 34#include "core.h"
35#include "config.h" 35#include "config.h"
36#include "bearer.h"
36#include <net/genetlink.h> 37#include <net/genetlink.h>
37#include <linux/tipc_config.h> 38#include <linux/tipc_config.h>
38 39
40/* The legacy API had an artificial message length limit called
41 * ULTRA_STRING_MAX_LEN.
42 */
43#define ULTRA_STRING_MAX_LEN 32768
44
45#define TIPC_SKB_MAX TLV_SPACE(ULTRA_STRING_MAX_LEN)
46
47#define REPLY_TRUNCATED "<truncated>\n"
48
49struct tipc_nl_compat_msg {
50 u16 cmd;
51 int rep_size;
52 struct sk_buff *rep;
53 struct tlv_desc *req;
54 struct sock *dst_sk;
55};
56
57struct tipc_nl_compat_cmd_dump {
58 int (*dumpit)(struct sk_buff *, struct netlink_callback *);
59 int (*format)(struct tipc_nl_compat_msg *msg, struct nlattr **attrs);
60};
61
62static int tipc_skb_tailroom(struct sk_buff *skb)
63{
64 int tailroom;
65 int limit;
66
67 tailroom = skb_tailroom(skb);
68 limit = TIPC_SKB_MAX - skb->len;
69
70 if (tailroom < limit)
71 return tailroom;
72
73 return limit;
74}
75
76static int tipc_add_tlv(struct sk_buff *skb, u16 type, void *data, u16 len)
77{
78 struct tlv_desc *tlv = (struct tlv_desc *)skb_tail_pointer(skb);
79
80 if (tipc_skb_tailroom(skb) < TLV_SPACE(len))
81 return -EMSGSIZE;
82
83 skb_put(skb, TLV_SPACE(len));
84 tlv->tlv_type = htons(type);
85 tlv->tlv_len = htons(TLV_LENGTH(len));
86 if (len && data)
87 memcpy(TLV_DATA(tlv), data, len);
88
89 return 0;
90}
91
92static struct sk_buff *tipc_tlv_alloc(int size)
93{
94 int hdr_len;
95 struct sk_buff *buf;
96
97 size = TLV_SPACE(size);
98 hdr_len = nlmsg_total_size(GENL_HDRLEN + TIPC_GENL_HDRLEN);
99
100 buf = alloc_skb(hdr_len + size, GFP_KERNEL);
101 if (!buf)
102 return NULL;
103
104 skb_reserve(buf, hdr_len);
105
106 return buf;
107}
108
109static struct sk_buff *tipc_get_err_tlv(char *str)
110{
111 int str_len = strlen(str) + 1;
112 struct sk_buff *buf;
113
114 buf = tipc_tlv_alloc(TLV_SPACE(str_len));
115 if (buf)
116 tipc_add_tlv(buf, TIPC_TLV_ERROR_STRING, str, str_len);
117
118 return buf;
119}
120
121static int __tipc_nl_compat_dumpit(struct tipc_nl_compat_cmd_dump *cmd,
122 struct tipc_nl_compat_msg *msg,
123 struct sk_buff *arg)
124{
125 int len = 0;
126 int err;
127 struct sk_buff *buf;
128 struct nlmsghdr *nlmsg;
129 struct netlink_callback cb;
130
131 memset(&cb, 0, sizeof(cb));
132 cb.nlh = (struct nlmsghdr *)arg->data;
133 cb.skb = arg;
134
135 buf = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
136 if (!buf)
137 return -ENOMEM;
138
139 buf->sk = msg->dst_sk;
140
141 do {
142 int rem;
143
144 len = (*cmd->dumpit)(buf, &cb);
145
146 nlmsg_for_each_msg(nlmsg, nlmsg_hdr(buf), len, rem) {
147 struct nlattr **attrs;
148
149 err = tipc_nlmsg_parse(nlmsg, &attrs);
150 if (err)
151 goto err_out;
152
153 err = (*cmd->format)(msg, attrs);
154 if (err)
155 goto err_out;
156
157 if (tipc_skb_tailroom(msg->rep) <= 1) {
158 err = -EMSGSIZE;
159 goto err_out;
160 }
161 }
162
163 skb_reset_tail_pointer(buf);
164 buf->len = 0;
165
166 } while (len);
167
168 err = 0;
169
170err_out:
171 kfree_skb(buf);
172
173 if (err == -EMSGSIZE) {
174 /* The legacy API only considered messages filling
175 * "ULTRA_STRING_MAX_LEN" to be truncated.
176 */
177 if ((TIPC_SKB_MAX - msg->rep->len) <= 1) {
178 char *tail = skb_tail_pointer(msg->rep);
179
180 if (*tail != '\0')
181 sprintf(tail - sizeof(REPLY_TRUNCATED) - 1,
182 REPLY_TRUNCATED);
183 }
184
185 return 0;
186 }
187
188 return err;
189}
190
191static int tipc_nl_compat_dumpit(struct tipc_nl_compat_cmd_dump *cmd,
192 struct tipc_nl_compat_msg *msg)
193{
194 int err;
195 struct sk_buff *arg;
196
197 msg->rep = tipc_tlv_alloc(msg->rep_size);
198 if (!msg->rep)
199 return -ENOMEM;
200
201 arg = nlmsg_new(0, GFP_KERNEL);
202 if (!arg) {
203 kfree_skb(msg->rep);
204 return -ENOMEM;
205 }
206
207 err = __tipc_nl_compat_dumpit(cmd, msg, arg);
208 if (err)
209 kfree_skb(msg->rep);
210
211 kfree_skb(arg);
212
213 return err;
214}
215
216static int tipc_nl_compat_bearer_dump(struct tipc_nl_compat_msg *msg,
217 struct nlattr **attrs)
218{
219 struct nlattr *bearer[TIPC_NLA_BEARER_MAX + 1];
220
221 nla_parse_nested(bearer, TIPC_NLA_BEARER_MAX, attrs[TIPC_NLA_BEARER],
222 NULL);
223
224 return tipc_add_tlv(msg->rep, TIPC_TLV_BEARER_NAME,
225 nla_data(bearer[TIPC_NLA_BEARER_NAME]),
226 nla_len(bearer[TIPC_NLA_BEARER_NAME]));
227}
228
229static int tipc_nl_compat_handle(struct tipc_nl_compat_msg *msg)
230{
231 struct tipc_nl_compat_cmd_dump dump;
232
233 memset(&dump, 0, sizeof(dump));
234
235 switch (msg->cmd) {
236 case TIPC_CMD_GET_BEARER_NAMES:
237 msg->rep_size = MAX_BEARERS * TLV_SPACE(TIPC_MAX_BEARER_NAME);
238 dump.dumpit = tipc_nl_bearer_dump;
239 dump.format = tipc_nl_compat_bearer_dump;
240 return tipc_nl_compat_dumpit(&dump, msg);
241 }
242
243 return -EOPNOTSUPP;
244}
245
246static int tipc_nl_compat_recv(struct sk_buff *skb, struct genl_info *info)
247{
248 int err;
249 int len;
250 struct tipc_nl_compat_msg msg;
251 struct nlmsghdr *req_nlh;
252 struct nlmsghdr *rep_nlh;
253 struct tipc_genlmsghdr *req_userhdr = info->userhdr;
254 struct net *net = genl_info_net(info);
255
256 memset(&msg, 0, sizeof(msg));
257
258 req_nlh = (struct nlmsghdr *)skb->data;
259 msg.req = nlmsg_data(req_nlh) + GENL_HDRLEN + TIPC_GENL_HDRLEN;
260 msg.cmd = req_userhdr->cmd;
261 msg.dst_sk = info->dst_sk;
262
263 if ((msg.cmd & 0xC000) && (!netlink_net_capable(skb, CAP_NET_ADMIN))) {
264 msg.rep = tipc_get_err_tlv(TIPC_CFG_NOT_NET_ADMIN);
265 err = -EACCES;
266 goto send;
267 }
268
269 len = nlmsg_attrlen(req_nlh, GENL_HDRLEN + TIPC_GENL_HDRLEN);
270 if (TLV_GET_LEN(msg.req) && !TLV_OK(msg.req, len)) {
271 msg.rep = tipc_get_err_tlv(TIPC_CFG_NOT_SUPPORTED);
272 err = -EOPNOTSUPP;
273 goto send;
274 }
275
276 err = tipc_nl_compat_handle(&msg);
277 if (err == -EOPNOTSUPP)
278 msg.rep = tipc_get_err_tlv(TIPC_CFG_NOT_SUPPORTED);
279 else if (err == -EINVAL)
280 msg.rep = tipc_get_err_tlv(TIPC_CFG_TLV_ERROR);
281send:
282 if (!msg.rep)
283 return err;
284
285 len = nlmsg_total_size(GENL_HDRLEN + TIPC_GENL_HDRLEN);
286 skb_push(msg.rep, len);
287 rep_nlh = nlmsg_hdr(msg.rep);
288 memcpy(rep_nlh, info->nlhdr, len);
289 rep_nlh->nlmsg_len = msg.rep->len;
290 genlmsg_unicast(net, msg.rep, NETLINK_CB(skb).portid);
291
292 return err;
293}
294
39static int handle_cmd(struct sk_buff *skb, struct genl_info *info) 295static int handle_cmd(struct sk_buff *skb, struct genl_info *info)
40{ 296{
41 struct net *net = genl_info_net(info); 297 struct net *net = genl_info_net(info);
@@ -69,6 +325,22 @@ static int handle_cmd(struct sk_buff *skb, struct genl_info *info)
69 return 0; 325 return 0;
70} 326}
71 327
328/* Temporary function to keep functionality throughout the patchset
329 * without having to mess with the global variables and other trickery
330 * of the old API.
331 */
332static int tipc_nl_compat_tmp_wrap(struct sk_buff *skb, struct genl_info *info)
333{
334 struct tipc_genlmsghdr *req = info->userhdr;
335
336 switch (req->cmd) {
337 case TIPC_CMD_GET_BEARER_NAMES:
338 return tipc_nl_compat_recv(skb, info);
339 }
340
341 return handle_cmd(skb, info);
342}
343
72static struct genl_family tipc_genl_compat_family = { 344static struct genl_family tipc_genl_compat_family = {
73 .id = GENL_ID_GENERATE, 345 .id = GENL_ID_GENERATE,
74 .name = TIPC_GENL_NAME, 346 .name = TIPC_GENL_NAME,
@@ -81,7 +353,7 @@ static struct genl_family tipc_genl_compat_family = {
81static struct genl_ops tipc_genl_compat_ops[] = { 353static struct genl_ops tipc_genl_compat_ops[] = {
82 { 354 {
83 .cmd = TIPC_GENL_CMD, 355 .cmd = TIPC_GENL_CMD,
84 .doit = handle_cmd, 356 .doit = tipc_nl_compat_tmp_wrap,
85 }, 357 },
86}; 358};
87 359