aboutsummaryrefslogtreecommitdiffstats
path: root/net/tipc/netlink_compat.c
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2015-02-09 16:20:53 -0500
committerDavid S. Miller <davem@davemloft.net>2015-02-09 16:20:53 -0500
commit9dce285b70c157754d753203112cfef22770b1f9 (patch)
tree4859799a8311ecd637e2a582600af1057a78e08b /net/tipc/netlink_compat.c
parentc8ac18f2006b2926ce375c01646b2f487d1c33b2 (diff)
parent941787b82982b3f33ac398c8c00035ddd0f8c514 (diff)
Merge branch 'tipc-next'
Richard Alpe says: ==================== tipc: new compat layer for the legacy NL API This is a compatibility / transcoding layer for the old netlink API. It relies on the new netlink API to collect data or perform actions (dumpit / doit). The main benefit of this compat layer is that it removes a lot of complex code from the tipc core as only the new API needs to be able harness data or perform actions. I.e. the compat layer isn't concerned with locking or how the internal data-structures look. As long as the new API stays relatively intact the compat layer should be fine. The main challenge in this compat layer is the randomness of the legacy API. Some commands send binary data and some send ASCII data, some are very picky in optimizing there buffer sizes and some just don't care. Most legacy commands put there data in a single TLV (data container) but some segment the data into multiple TLV's. This list of randomness goes on and on.. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/tipc/netlink_compat.c')
-rw-r--r--net/tipc/netlink_compat.c1084
1 files changed, 1084 insertions, 0 deletions
diff --git a/net/tipc/netlink_compat.c b/net/tipc/netlink_compat.c
new file mode 100644
index 000000000000..ce9121e8e990
--- /dev/null
+++ b/net/tipc/netlink_compat.c
@@ -0,0 +1,1084 @@
1/*
2 * Copyright (c) 2014, Ericsson AB
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the names of the copyright holders nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
16 *
17 * Alternatively, this software may be distributed under the terms of the
18 * GNU General Public License ("GPL") version 2 as published by the Free
19 * Software Foundation.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 */
33
34#include "core.h"
35#include "bearer.h"
36#include "link.h"
37#include "name_table.h"
38#include "socket.h"
39#include "node.h"
40#include "net.h"
41#include <net/genetlink.h>
42#include <linux/tipc_config.h>
43
44/* The legacy API had an artificial message length limit called
45 * ULTRA_STRING_MAX_LEN.
46 */
47#define ULTRA_STRING_MAX_LEN 32768
48
49#define TIPC_SKB_MAX TLV_SPACE(ULTRA_STRING_MAX_LEN)
50
51#define REPLY_TRUNCATED "<truncated>\n"
52
53struct tipc_nl_compat_msg {
54 u16 cmd;
55 int rep_type;
56 int rep_size;
57 int req_type;
58 struct sk_buff *rep;
59 struct tlv_desc *req;
60 struct sock *dst_sk;
61};
62
63struct tipc_nl_compat_cmd_dump {
64 int (*header)(struct tipc_nl_compat_msg *);
65 int (*dumpit)(struct sk_buff *, struct netlink_callback *);
66 int (*format)(struct tipc_nl_compat_msg *msg, struct nlattr **attrs);
67};
68
69struct tipc_nl_compat_cmd_doit {
70 int (*doit)(struct sk_buff *skb, struct genl_info *info);
71 int (*transcode)(struct sk_buff *skb, struct tipc_nl_compat_msg *msg);
72};
73
74static int tipc_skb_tailroom(struct sk_buff *skb)
75{
76 int tailroom;
77 int limit;
78
79 tailroom = skb_tailroom(skb);
80 limit = TIPC_SKB_MAX - skb->len;
81
82 if (tailroom < limit)
83 return tailroom;
84
85 return limit;
86}
87
88static int tipc_add_tlv(struct sk_buff *skb, u16 type, void *data, u16 len)
89{
90 struct tlv_desc *tlv = (struct tlv_desc *)skb_tail_pointer(skb);
91
92 if (tipc_skb_tailroom(skb) < TLV_SPACE(len))
93 return -EMSGSIZE;
94
95 skb_put(skb, TLV_SPACE(len));
96 tlv->tlv_type = htons(type);
97 tlv->tlv_len = htons(TLV_LENGTH(len));
98 if (len && data)
99 memcpy(TLV_DATA(tlv), data, len);
100
101 return 0;
102}
103
104static void tipc_tlv_init(struct sk_buff *skb, u16 type)
105{
106 struct tlv_desc *tlv = (struct tlv_desc *)skb->data;
107
108 TLV_SET_LEN(tlv, 0);
109 TLV_SET_TYPE(tlv, type);
110 skb_put(skb, sizeof(struct tlv_desc));
111}
112
113static int tipc_tlv_sprintf(struct sk_buff *skb, const char *fmt, ...)
114{
115 int n;
116 u16 len;
117 u32 rem;
118 char *buf;
119 struct tlv_desc *tlv;
120 va_list args;
121
122 rem = tipc_skb_tailroom(skb);
123
124 tlv = (struct tlv_desc *)skb->data;
125 len = TLV_GET_LEN(tlv);
126 buf = TLV_DATA(tlv) + len;
127
128 va_start(args, fmt);
129 n = vscnprintf(buf, rem, fmt, args);
130 va_end(args);
131
132 TLV_SET_LEN(tlv, n + len);
133 skb_put(skb, n);
134
135 return n;
136}
137
138static struct sk_buff *tipc_tlv_alloc(int size)
139{
140 int hdr_len;
141 struct sk_buff *buf;
142
143 size = TLV_SPACE(size);
144 hdr_len = nlmsg_total_size(GENL_HDRLEN + TIPC_GENL_HDRLEN);
145
146 buf = alloc_skb(hdr_len + size, GFP_KERNEL);
147 if (!buf)
148 return NULL;
149
150 skb_reserve(buf, hdr_len);
151
152 return buf;
153}
154
155static struct sk_buff *tipc_get_err_tlv(char *str)
156{
157 int str_len = strlen(str) + 1;
158 struct sk_buff *buf;
159
160 buf = tipc_tlv_alloc(TLV_SPACE(str_len));
161 if (buf)
162 tipc_add_tlv(buf, TIPC_TLV_ERROR_STRING, str, str_len);
163
164 return buf;
165}
166
167static int __tipc_nl_compat_dumpit(struct tipc_nl_compat_cmd_dump *cmd,
168 struct tipc_nl_compat_msg *msg,
169 struct sk_buff *arg)
170{
171 int len = 0;
172 int err;
173 struct sk_buff *buf;
174 struct nlmsghdr *nlmsg;
175 struct netlink_callback cb;
176
177 memset(&cb, 0, sizeof(cb));
178 cb.nlh = (struct nlmsghdr *)arg->data;
179 cb.skb = arg;
180
181 buf = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
182 if (!buf)
183 return -ENOMEM;
184
185 buf->sk = msg->dst_sk;
186
187 do {
188 int rem;
189
190 len = (*cmd->dumpit)(buf, &cb);
191
192 nlmsg_for_each_msg(nlmsg, nlmsg_hdr(buf), len, rem) {
193 struct nlattr **attrs;
194
195 err = tipc_nlmsg_parse(nlmsg, &attrs);
196 if (err)
197 goto err_out;
198
199 err = (*cmd->format)(msg, attrs);
200 if (err)
201 goto err_out;
202
203 if (tipc_skb_tailroom(msg->rep) <= 1) {
204 err = -EMSGSIZE;
205 goto err_out;
206 }
207 }
208
209 skb_reset_tail_pointer(buf);
210 buf->len = 0;
211
212 } while (len);
213
214 err = 0;
215
216err_out:
217 kfree_skb(buf);
218
219 if (err == -EMSGSIZE) {
220 /* The legacy API only considered messages filling
221 * "ULTRA_STRING_MAX_LEN" to be truncated.
222 */
223 if ((TIPC_SKB_MAX - msg->rep->len) <= 1) {
224 char *tail = skb_tail_pointer(msg->rep);
225
226 if (*tail != '\0')
227 sprintf(tail - sizeof(REPLY_TRUNCATED) - 1,
228 REPLY_TRUNCATED);
229 }
230
231 return 0;
232 }
233
234 return err;
235}
236
237static int tipc_nl_compat_dumpit(struct tipc_nl_compat_cmd_dump *cmd,
238 struct tipc_nl_compat_msg *msg)
239{
240 int err;
241 struct sk_buff *arg;
242
243 if (msg->req_type && !TLV_CHECK_TYPE(msg->req, msg->req_type))
244 return -EINVAL;
245
246 msg->rep = tipc_tlv_alloc(msg->rep_size);
247 if (!msg->rep)
248 return -ENOMEM;
249
250 if (msg->rep_type)
251 tipc_tlv_init(msg->rep, msg->rep_type);
252
253 if (cmd->header)
254 (*cmd->header)(msg);
255
256 arg = nlmsg_new(0, GFP_KERNEL);
257 if (!arg) {
258 kfree_skb(msg->rep);
259 return -ENOMEM;
260 }
261
262 err = __tipc_nl_compat_dumpit(cmd, msg, arg);
263 if (err)
264 kfree_skb(msg->rep);
265
266 kfree_skb(arg);
267
268 return err;
269}
270
271static int __tipc_nl_compat_doit(struct tipc_nl_compat_cmd_doit *cmd,
272 struct tipc_nl_compat_msg *msg)
273{
274 int err;
275 struct sk_buff *doit_buf;
276 struct sk_buff *trans_buf;
277 struct nlattr **attrbuf;
278 struct genl_info info;
279
280 trans_buf = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
281 if (!trans_buf)
282 return -ENOMEM;
283
284 err = (*cmd->transcode)(trans_buf, msg);
285 if (err)
286 goto trans_out;
287
288 attrbuf = kmalloc((tipc_genl_family.maxattr + 1) *
289 sizeof(struct nlattr *), GFP_KERNEL);
290 if (!attrbuf) {
291 err = -ENOMEM;
292 goto trans_out;
293 }
294
295 err = nla_parse(attrbuf, tipc_genl_family.maxattr,
296 (const struct nlattr *)trans_buf->data,
297 trans_buf->len, NULL);
298 if (err)
299 goto parse_out;
300
301 doit_buf = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
302 if (!doit_buf) {
303 err = -ENOMEM;
304 goto parse_out;
305 }
306
307 doit_buf->sk = msg->dst_sk;
308
309 memset(&info, 0, sizeof(info));
310 info.attrs = attrbuf;
311
312 err = (*cmd->doit)(doit_buf, &info);
313
314 kfree_skb(doit_buf);
315parse_out:
316 kfree(attrbuf);
317trans_out:
318 kfree_skb(trans_buf);
319
320 return err;
321}
322
323static int tipc_nl_compat_doit(struct tipc_nl_compat_cmd_doit *cmd,
324 struct tipc_nl_compat_msg *msg)
325{
326 int err;
327
328 if (msg->req_type && !TLV_CHECK_TYPE(msg->req, msg->req_type))
329 return -EINVAL;
330
331 err = __tipc_nl_compat_doit(cmd, msg);
332 if (err)
333 return err;
334
335 /* The legacy API considered an empty message a success message */
336 msg->rep = tipc_tlv_alloc(0);
337 if (!msg->rep)
338 return -ENOMEM;
339
340 return 0;
341}
342
343static int tipc_nl_compat_bearer_dump(struct tipc_nl_compat_msg *msg,
344 struct nlattr **attrs)
345{
346 struct nlattr *bearer[TIPC_NLA_BEARER_MAX + 1];
347
348 nla_parse_nested(bearer, TIPC_NLA_BEARER_MAX, attrs[TIPC_NLA_BEARER],
349 NULL);
350
351 return tipc_add_tlv(msg->rep, TIPC_TLV_BEARER_NAME,
352 nla_data(bearer[TIPC_NLA_BEARER_NAME]),
353 nla_len(bearer[TIPC_NLA_BEARER_NAME]));
354}
355
356static int tipc_nl_compat_bearer_enable(struct sk_buff *skb,
357 struct tipc_nl_compat_msg *msg)
358{
359 struct nlattr *prop;
360 struct nlattr *bearer;
361 struct tipc_bearer_config *b;
362
363 b = (struct tipc_bearer_config *)TLV_DATA(msg->req);
364
365 bearer = nla_nest_start(skb, TIPC_NLA_BEARER);
366 if (!bearer)
367 return -EMSGSIZE;
368
369 if (nla_put_string(skb, TIPC_NLA_BEARER_NAME, b->name))
370 return -EMSGSIZE;
371
372 if (nla_put_u32(skb, TIPC_NLA_BEARER_DOMAIN, ntohl(b->disc_domain)))
373 return -EMSGSIZE;
374
375 if (ntohl(b->priority) <= TIPC_MAX_LINK_PRI) {
376 prop = nla_nest_start(skb, TIPC_NLA_BEARER_PROP);
377 if (!prop)
378 return -EMSGSIZE;
379 if (nla_put_u32(skb, TIPC_NLA_PROP_PRIO, ntohl(b->priority)))
380 return -EMSGSIZE;
381 nla_nest_end(skb, prop);
382 }
383 nla_nest_end(skb, bearer);
384
385 return 0;
386}
387
388static int tipc_nl_compat_bearer_disable(struct sk_buff *skb,
389 struct tipc_nl_compat_msg *msg)
390{
391 char *name;
392 struct nlattr *bearer;
393
394 name = (char *)TLV_DATA(msg->req);
395
396 bearer = nla_nest_start(skb, TIPC_NLA_BEARER);
397 if (!bearer)
398 return -EMSGSIZE;
399
400 if (nla_put_string(skb, TIPC_NLA_BEARER_NAME, name))
401 return -EMSGSIZE;
402
403 nla_nest_end(skb, bearer);
404
405 return 0;
406}
407
408static inline u32 perc(u32 count, u32 total)
409{
410 return (count * 100 + (total / 2)) / total;
411}
412
413static void __fill_bc_link_stat(struct tipc_nl_compat_msg *msg,
414 struct nlattr *prop[], struct nlattr *stats[])
415{
416 tipc_tlv_sprintf(msg->rep, " Window:%u packets\n",
417 nla_get_u32(prop[TIPC_NLA_PROP_WIN]));
418
419 tipc_tlv_sprintf(msg->rep,
420 " RX packets:%u fragments:%u/%u bundles:%u/%u\n",
421 nla_get_u32(stats[TIPC_NLA_STATS_RX_INFO]),
422 nla_get_u32(stats[TIPC_NLA_STATS_RX_FRAGMENTS]),
423 nla_get_u32(stats[TIPC_NLA_STATS_RX_FRAGMENTED]),
424 nla_get_u32(stats[TIPC_NLA_STATS_RX_BUNDLES]),
425 nla_get_u32(stats[TIPC_NLA_STATS_RX_BUNDLED]));
426
427 tipc_tlv_sprintf(msg->rep,
428 " TX packets:%u fragments:%u/%u bundles:%u/%u\n",
429 nla_get_u32(stats[TIPC_NLA_STATS_TX_INFO]),
430 nla_get_u32(stats[TIPC_NLA_STATS_TX_FRAGMENTS]),
431 nla_get_u32(stats[TIPC_NLA_STATS_TX_FRAGMENTED]),
432 nla_get_u32(stats[TIPC_NLA_STATS_TX_BUNDLES]),
433 nla_get_u32(stats[TIPC_NLA_STATS_TX_BUNDLED]));
434
435 tipc_tlv_sprintf(msg->rep, " RX naks:%u defs:%u dups:%u\n",
436 nla_get_u32(stats[TIPC_NLA_STATS_RX_NACKS]),
437 nla_get_u32(stats[TIPC_NLA_STATS_RX_DEFERRED]),
438 nla_get_u32(stats[TIPC_NLA_STATS_DUPLICATES]));
439
440 tipc_tlv_sprintf(msg->rep, " TX naks:%u acks:%u dups:%u\n",
441 nla_get_u32(stats[TIPC_NLA_STATS_TX_NACKS]),
442 nla_get_u32(stats[TIPC_NLA_STATS_TX_ACKS]),
443 nla_get_u32(stats[TIPC_NLA_STATS_RETRANSMITTED]));
444
445 tipc_tlv_sprintf(msg->rep,
446 " Congestion link:%u Send queue max:%u avg:%u",
447 nla_get_u32(stats[TIPC_NLA_STATS_LINK_CONGS]),
448 nla_get_u32(stats[TIPC_NLA_STATS_MAX_QUEUE]),
449 nla_get_u32(stats[TIPC_NLA_STATS_AVG_QUEUE]));
450}
451
452static int tipc_nl_compat_link_stat_dump(struct tipc_nl_compat_msg *msg,
453 struct nlattr **attrs)
454{
455 char *name;
456 struct nlattr *link[TIPC_NLA_LINK_MAX + 1];
457 struct nlattr *prop[TIPC_NLA_PROP_MAX + 1];
458 struct nlattr *stats[TIPC_NLA_STATS_MAX + 1];
459
460 nla_parse_nested(link, TIPC_NLA_LINK_MAX, attrs[TIPC_NLA_LINK], NULL);
461
462 nla_parse_nested(prop, TIPC_NLA_PROP_MAX, link[TIPC_NLA_LINK_PROP],
463 NULL);
464
465 nla_parse_nested(stats, TIPC_NLA_STATS_MAX, link[TIPC_NLA_LINK_STATS],
466 NULL);
467
468 name = (char *)TLV_DATA(msg->req);
469 if (strcmp(name, nla_data(link[TIPC_NLA_LINK_NAME])) != 0)
470 return 0;
471
472 tipc_tlv_sprintf(msg->rep, "\nLink <%s>\n",
473 nla_data(link[TIPC_NLA_LINK_NAME]));
474
475 if (link[TIPC_NLA_LINK_BROADCAST]) {
476 __fill_bc_link_stat(msg, prop, stats);
477 return 0;
478 }
479
480 if (link[TIPC_NLA_LINK_ACTIVE])
481 tipc_tlv_sprintf(msg->rep, " ACTIVE");
482 else if (link[TIPC_NLA_LINK_UP])
483 tipc_tlv_sprintf(msg->rep, " STANDBY");
484 else
485 tipc_tlv_sprintf(msg->rep, " DEFUNCT");
486
487 tipc_tlv_sprintf(msg->rep, " MTU:%u Priority:%u",
488 nla_get_u32(link[TIPC_NLA_LINK_MTU]),
489 nla_get_u32(prop[TIPC_NLA_PROP_PRIO]));
490
491 tipc_tlv_sprintf(msg->rep, " Tolerance:%u ms Window:%u packets\n",
492 nla_get_u32(prop[TIPC_NLA_PROP_TOL]),
493 nla_get_u32(prop[TIPC_NLA_PROP_WIN]));
494
495 tipc_tlv_sprintf(msg->rep,
496 " RX packets:%u fragments:%u/%u bundles:%u/%u\n",
497 nla_get_u32(link[TIPC_NLA_LINK_RX]) -
498 nla_get_u32(stats[TIPC_NLA_STATS_RX_INFO]),
499 nla_get_u32(stats[TIPC_NLA_STATS_RX_FRAGMENTS]),
500 nla_get_u32(stats[TIPC_NLA_STATS_RX_FRAGMENTED]),
501 nla_get_u32(stats[TIPC_NLA_STATS_RX_BUNDLES]),
502 nla_get_u32(stats[TIPC_NLA_STATS_RX_BUNDLED]));
503
504 tipc_tlv_sprintf(msg->rep,
505 " TX packets:%u fragments:%u/%u bundles:%u/%u\n",
506 nla_get_u32(link[TIPC_NLA_LINK_TX]) -
507 nla_get_u32(stats[TIPC_NLA_STATS_TX_INFO]),
508 nla_get_u32(stats[TIPC_NLA_STATS_TX_FRAGMENTS]),
509 nla_get_u32(stats[TIPC_NLA_STATS_TX_FRAGMENTED]),
510 nla_get_u32(stats[TIPC_NLA_STATS_TX_BUNDLES]),
511 nla_get_u32(stats[TIPC_NLA_STATS_TX_BUNDLED]));
512
513 tipc_tlv_sprintf(msg->rep,
514 " TX profile sample:%u packets average:%u octets\n",
515 nla_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_CNT]),
516 nla_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_TOT]) /
517 nla_get_u32(stats[TIPC_NLA_STATS_MSG_PROF_TOT]));
518
519 tipc_tlv_sprintf(msg->rep,
520 " 0-64:%u%% -256:%u%% -1024:%u%% -4096:%u%% ",
521 perc(nla_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P0]),
522 nla_get_u32(stats[TIPC_NLA_STATS_MSG_PROF_TOT])),
523 perc(nla_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P1]),
524 nla_get_u32(stats[TIPC_NLA_STATS_MSG_PROF_TOT])),
525 perc(nla_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P2]),
526 nla_get_u32(stats[TIPC_NLA_STATS_MSG_PROF_TOT])),
527 perc(nla_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P3]),
528 nla_get_u32(stats[TIPC_NLA_STATS_MSG_PROF_TOT])));
529
530 tipc_tlv_sprintf(msg->rep, "-16384:%u%% -32768:%u%% -66000:%u%%\n",
531 perc(nla_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P4]),
532 nla_get_u32(stats[TIPC_NLA_STATS_MSG_PROF_TOT])),
533 perc(nla_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P5]),
534 nla_get_u32(stats[TIPC_NLA_STATS_MSG_PROF_TOT])),
535 perc(nla_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P6]),
536 nla_get_u32(stats[TIPC_NLA_STATS_MSG_PROF_TOT])));
537
538 tipc_tlv_sprintf(msg->rep,
539 " RX states:%u probes:%u naks:%u defs:%u dups:%u\n",
540 nla_get_u32(stats[TIPC_NLA_STATS_RX_STATES]),
541 nla_get_u32(stats[TIPC_NLA_STATS_RX_PROBES]),
542 nla_get_u32(stats[TIPC_NLA_STATS_RX_NACKS]),
543 nla_get_u32(stats[TIPC_NLA_STATS_RX_DEFERRED]),
544 nla_get_u32(stats[TIPC_NLA_STATS_DUPLICATES]));
545
546 tipc_tlv_sprintf(msg->rep,
547 " TX states:%u probes:%u naks:%u acks:%u dups:%u\n",
548 nla_get_u32(stats[TIPC_NLA_STATS_TX_STATES]),
549 nla_get_u32(stats[TIPC_NLA_STATS_TX_PROBES]),
550 nla_get_u32(stats[TIPC_NLA_STATS_TX_NACKS]),
551 nla_get_u32(stats[TIPC_NLA_STATS_TX_ACKS]),
552 nla_get_u32(stats[TIPC_NLA_STATS_RETRANSMITTED]));
553
554 tipc_tlv_sprintf(msg->rep,
555 " Congestion link:%u Send queue max:%u avg:%u",
556 nla_get_u32(stats[TIPC_NLA_STATS_LINK_CONGS]),
557 nla_get_u32(stats[TIPC_NLA_STATS_MAX_QUEUE]),
558 nla_get_u32(stats[TIPC_NLA_STATS_AVG_QUEUE]));
559
560 return 0;
561}
562
563static int tipc_nl_compat_link_dump(struct tipc_nl_compat_msg *msg,
564 struct nlattr **attrs)
565{
566 struct nlattr *link[TIPC_NLA_LINK_MAX + 1];
567 struct tipc_link_info link_info;
568
569 nla_parse_nested(link, TIPC_NLA_LINK_MAX, attrs[TIPC_NLA_LINK], NULL);
570
571 link_info.dest = nla_get_flag(link[TIPC_NLA_LINK_DEST]);
572 link_info.up = htonl(nla_get_flag(link[TIPC_NLA_LINK_UP]));
573 strcpy(link_info.str, nla_data(link[TIPC_NLA_LINK_NAME]));
574
575 return tipc_add_tlv(msg->rep, TIPC_TLV_LINK_INFO,
576 &link_info, sizeof(link_info));
577}
578
579static int tipc_nl_compat_link_set(struct sk_buff *skb,
580 struct tipc_nl_compat_msg *msg)
581{
582 struct nlattr *link;
583 struct nlattr *prop;
584 struct tipc_link_config *lc;
585
586 lc = (struct tipc_link_config *)TLV_DATA(msg->req);
587
588 link = nla_nest_start(skb, TIPC_NLA_LINK);
589 if (!link)
590 return -EMSGSIZE;
591
592 if (nla_put_string(skb, TIPC_NLA_LINK_NAME, lc->name))
593 return -EMSGSIZE;
594
595 prop = nla_nest_start(skb, TIPC_NLA_LINK_PROP);
596 if (!prop)
597 return -EMSGSIZE;
598
599 if (msg->cmd == TIPC_CMD_SET_LINK_PRI) {
600 if (nla_put_u32(skb, TIPC_NLA_PROP_PRIO, ntohl(lc->value)))
601 return -EMSGSIZE;
602 } else if (msg->cmd == TIPC_CMD_SET_LINK_TOL) {
603 if (nla_put_u32(skb, TIPC_NLA_PROP_TOL, ntohl(lc->value)))
604 return -EMSGSIZE;
605 } else if (msg->cmd == TIPC_CMD_SET_LINK_WINDOW) {
606 if (nla_put_u32(skb, TIPC_NLA_PROP_WIN, ntohl(lc->value)))
607 return -EMSGSIZE;
608 }
609
610 nla_nest_end(skb, prop);
611 nla_nest_end(skb, link);
612
613 return 0;
614}
615
616static int tipc_nl_compat_link_reset_stats(struct sk_buff *skb,
617 struct tipc_nl_compat_msg *msg)
618{
619 char *name;
620 struct nlattr *link;
621
622 name = (char *)TLV_DATA(msg->req);
623
624 link = nla_nest_start(skb, TIPC_NLA_LINK);
625 if (!link)
626 return -EMSGSIZE;
627
628 if (nla_put_string(skb, TIPC_NLA_LINK_NAME, name))
629 return -EMSGSIZE;
630
631 nla_nest_end(skb, link);
632
633 return 0;
634}
635
636static int tipc_nl_compat_name_table_dump_header(struct tipc_nl_compat_msg *msg)
637{
638 int i;
639 u32 depth;
640 struct tipc_name_table_query *ntq;
641 static const char * const header[] = {
642 "Type ",
643 "Lower Upper ",
644 "Port Identity ",
645 "Publication Scope"
646 };
647
648 ntq = (struct tipc_name_table_query *)TLV_DATA(msg->req);
649
650 depth = ntohl(ntq->depth);
651
652 if (depth > 4)
653 depth = 4;
654 for (i = 0; i < depth; i++)
655 tipc_tlv_sprintf(msg->rep, header[i]);
656 tipc_tlv_sprintf(msg->rep, "\n");
657
658 return 0;
659}
660
661static int tipc_nl_compat_name_table_dump(struct tipc_nl_compat_msg *msg,
662 struct nlattr **attrs)
663{
664 char port_str[27];
665 struct tipc_name_table_query *ntq;
666 struct nlattr *nt[TIPC_NLA_NAME_TABLE_MAX + 1];
667 struct nlattr *publ[TIPC_NLA_PUBL_MAX + 1];
668 u32 node, depth, type, lowbound, upbound;
669 static const char * const scope_str[] = {"", " zone", " cluster",
670 " node"};
671
672 nla_parse_nested(nt, TIPC_NLA_NAME_TABLE_MAX,
673 attrs[TIPC_NLA_NAME_TABLE], NULL);
674
675 nla_parse_nested(publ, TIPC_NLA_PUBL_MAX, nt[TIPC_NLA_NAME_TABLE_PUBL],
676 NULL);
677
678 ntq = (struct tipc_name_table_query *)TLV_DATA(msg->req);
679
680 depth = ntohl(ntq->depth);
681 type = ntohl(ntq->type);
682 lowbound = ntohl(ntq->lowbound);
683 upbound = ntohl(ntq->upbound);
684
685 if (!(depth & TIPC_NTQ_ALLTYPES) &&
686 (type != nla_get_u32(publ[TIPC_NLA_PUBL_TYPE])))
687 return 0;
688 if (lowbound && (lowbound > nla_get_u32(publ[TIPC_NLA_PUBL_UPPER])))
689 return 0;
690 if (upbound && (upbound < nla_get_u32(publ[TIPC_NLA_PUBL_LOWER])))
691 return 0;
692
693 tipc_tlv_sprintf(msg->rep, "%-10u ",
694 nla_get_u32(publ[TIPC_NLA_PUBL_TYPE]));
695
696 if (depth == 1)
697 goto out;
698
699 tipc_tlv_sprintf(msg->rep, "%-10u %-10u ",
700 nla_get_u32(publ[TIPC_NLA_PUBL_LOWER]),
701 nla_get_u32(publ[TIPC_NLA_PUBL_UPPER]));
702
703 if (depth == 2)
704 goto out;
705
706 node = nla_get_u32(publ[TIPC_NLA_PUBL_NODE]);
707 sprintf(port_str, "<%u.%u.%u:%u>", tipc_zone(node), tipc_cluster(node),
708 tipc_node(node), nla_get_u32(publ[TIPC_NLA_PUBL_REF]));
709 tipc_tlv_sprintf(msg->rep, "%-26s ", port_str);
710
711 if (depth == 3)
712 goto out;
713
714 tipc_tlv_sprintf(msg->rep, "%-10u %s",
715 nla_get_u32(publ[TIPC_NLA_PUBL_REF]),
716 scope_str[nla_get_u32(publ[TIPC_NLA_PUBL_SCOPE])]);
717out:
718 tipc_tlv_sprintf(msg->rep, "\n");
719
720 return 0;
721}
722
723static int __tipc_nl_compat_publ_dump(struct tipc_nl_compat_msg *msg,
724 struct nlattr **attrs)
725{
726 u32 type, lower, upper;
727 struct nlattr *publ[TIPC_NLA_PUBL_MAX + 1];
728
729 nla_parse_nested(publ, TIPC_NLA_PUBL_MAX, attrs[TIPC_NLA_PUBL], NULL);
730
731 type = nla_get_u32(publ[TIPC_NLA_PUBL_TYPE]);
732 lower = nla_get_u32(publ[TIPC_NLA_PUBL_LOWER]);
733 upper = nla_get_u32(publ[TIPC_NLA_PUBL_UPPER]);
734
735 if (lower == upper)
736 tipc_tlv_sprintf(msg->rep, " {%u,%u}", type, lower);
737 else
738 tipc_tlv_sprintf(msg->rep, " {%u,%u,%u}", type, lower, upper);
739
740 return 0;
741}
742
743static int tipc_nl_compat_publ_dump(struct tipc_nl_compat_msg *msg, u32 sock)
744{
745 int err;
746 void *hdr;
747 struct nlattr *nest;
748 struct sk_buff *args;
749 struct tipc_nl_compat_cmd_dump dump;
750
751 args = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
752 if (!args)
753 return -ENOMEM;
754
755 hdr = genlmsg_put(args, 0, 0, &tipc_genl_family, NLM_F_MULTI,
756 TIPC_NL_PUBL_GET);
757
758 nest = nla_nest_start(args, TIPC_NLA_SOCK);
759 if (!nest) {
760 kfree_skb(args);
761 return -EMSGSIZE;
762 }
763
764 if (nla_put_u32(args, TIPC_NLA_SOCK_REF, sock)) {
765 kfree_skb(args);
766 return -EMSGSIZE;
767 }
768
769 nla_nest_end(args, nest);
770 genlmsg_end(args, hdr);
771
772 dump.dumpit = tipc_nl_publ_dump;
773 dump.format = __tipc_nl_compat_publ_dump;
774
775 err = __tipc_nl_compat_dumpit(&dump, msg, args);
776
777 kfree_skb(args);
778
779 return err;
780}
781
782static int tipc_nl_compat_sk_dump(struct tipc_nl_compat_msg *msg,
783 struct nlattr **attrs)
784{
785 int err;
786 u32 sock_ref;
787 struct nlattr *sock[TIPC_NLA_SOCK_MAX + 1];
788
789 nla_parse_nested(sock, TIPC_NLA_SOCK_MAX, attrs[TIPC_NLA_SOCK], NULL);
790
791 sock_ref = nla_get_u32(sock[TIPC_NLA_SOCK_REF]);
792 tipc_tlv_sprintf(msg->rep, "%u:", sock_ref);
793
794 if (sock[TIPC_NLA_SOCK_CON]) {
795 u32 node;
796 struct nlattr *con[TIPC_NLA_CON_MAX + 1];
797
798 nla_parse_nested(con, TIPC_NLA_CON_MAX, sock[TIPC_NLA_SOCK_CON],
799 NULL);
800
801 node = nla_get_u32(con[TIPC_NLA_CON_NODE]);
802 tipc_tlv_sprintf(msg->rep, " connected to <%u.%u.%u:%u>",
803 tipc_zone(node),
804 tipc_cluster(node),
805 tipc_node(node),
806 nla_get_u32(con[TIPC_NLA_CON_SOCK]));
807
808 if (con[TIPC_NLA_CON_FLAG])
809 tipc_tlv_sprintf(msg->rep, " via {%u,%u}\n",
810 nla_get_u32(con[TIPC_NLA_CON_TYPE]),
811 nla_get_u32(con[TIPC_NLA_CON_INST]));
812 else
813 tipc_tlv_sprintf(msg->rep, "\n");
814 } else if (sock[TIPC_NLA_SOCK_HAS_PUBL]) {
815 tipc_tlv_sprintf(msg->rep, " bound to");
816
817 err = tipc_nl_compat_publ_dump(msg, sock_ref);
818 if (err)
819 return err;
820 }
821 tipc_tlv_sprintf(msg->rep, "\n");
822
823 return 0;
824}
825
826static int tipc_nl_compat_media_dump(struct tipc_nl_compat_msg *msg,
827 struct nlattr **attrs)
828{
829 struct nlattr *media[TIPC_NLA_MEDIA_MAX + 1];
830
831 nla_parse_nested(media, TIPC_NLA_MEDIA_MAX, attrs[TIPC_NLA_MEDIA],
832 NULL);
833
834 return tipc_add_tlv(msg->rep, TIPC_TLV_MEDIA_NAME,
835 nla_data(media[TIPC_NLA_MEDIA_NAME]),
836 nla_len(media[TIPC_NLA_MEDIA_NAME]));
837}
838
839static int tipc_nl_compat_node_dump(struct tipc_nl_compat_msg *msg,
840 struct nlattr **attrs)
841{
842 struct tipc_node_info node_info;
843 struct nlattr *node[TIPC_NLA_NODE_MAX + 1];
844
845 nla_parse_nested(node, TIPC_NLA_NODE_MAX, attrs[TIPC_NLA_NODE], NULL);
846
847 node_info.addr = htonl(nla_get_u32(node[TIPC_NLA_NODE_ADDR]));
848 node_info.up = htonl(nla_get_flag(node[TIPC_NLA_NODE_UP]));
849
850 return tipc_add_tlv(msg->rep, TIPC_TLV_NODE_INFO, &node_info,
851 sizeof(node_info));
852}
853
854static int tipc_nl_compat_net_set(struct sk_buff *skb,
855 struct tipc_nl_compat_msg *msg)
856{
857 u32 val;
858 struct nlattr *net;
859
860 val = ntohl(*(__be32 *)TLV_DATA(msg->req));
861
862 net = nla_nest_start(skb, TIPC_NLA_NET);
863 if (!net)
864 return -EMSGSIZE;
865
866 if (msg->cmd == TIPC_CMD_SET_NODE_ADDR) {
867 if (nla_put_u32(skb, TIPC_NLA_NET_ADDR, val))
868 return -EMSGSIZE;
869 } else if (msg->cmd == TIPC_CMD_SET_NETID) {
870 if (nla_put_u32(skb, TIPC_NLA_NET_ID, val))
871 return -EMSGSIZE;
872 }
873 nla_nest_end(skb, net);
874
875 return 0;
876}
877
878static int tipc_nl_compat_net_dump(struct tipc_nl_compat_msg *msg,
879 struct nlattr **attrs)
880{
881 __be32 id;
882 struct nlattr *net[TIPC_NLA_NET_MAX + 1];
883
884 nla_parse_nested(net, TIPC_NLA_NET_MAX, attrs[TIPC_NLA_NET], NULL);
885 id = htonl(nla_get_u32(net[TIPC_NLA_NET_ID]));
886
887 return tipc_add_tlv(msg->rep, TIPC_TLV_UNSIGNED, &id, sizeof(id));
888}
889
890static int tipc_cmd_show_stats_compat(struct tipc_nl_compat_msg *msg)
891{
892 msg->rep = tipc_tlv_alloc(ULTRA_STRING_MAX_LEN);
893 if (!msg->rep)
894 return -ENOMEM;
895
896 tipc_tlv_init(msg->rep, TIPC_TLV_ULTRA_STRING);
897 tipc_tlv_sprintf(msg->rep, "TIPC version " TIPC_MOD_VER "\n");
898
899 return 0;
900}
901
902static int tipc_nl_compat_handle(struct tipc_nl_compat_msg *msg)
903{
904 struct tipc_nl_compat_cmd_dump dump;
905 struct tipc_nl_compat_cmd_doit doit;
906
907 memset(&dump, 0, sizeof(dump));
908 memset(&doit, 0, sizeof(doit));
909
910 switch (msg->cmd) {
911 case TIPC_CMD_NOOP:
912 msg->rep = tipc_tlv_alloc(0);
913 if (!msg->rep)
914 return -ENOMEM;
915 return 0;
916 case TIPC_CMD_GET_BEARER_NAMES:
917 msg->rep_size = MAX_BEARERS * TLV_SPACE(TIPC_MAX_BEARER_NAME);
918 dump.dumpit = tipc_nl_bearer_dump;
919 dump.format = tipc_nl_compat_bearer_dump;
920 return tipc_nl_compat_dumpit(&dump, msg);
921 case TIPC_CMD_ENABLE_BEARER:
922 msg->req_type = TIPC_TLV_BEARER_CONFIG;
923 doit.doit = tipc_nl_bearer_enable;
924 doit.transcode = tipc_nl_compat_bearer_enable;
925 return tipc_nl_compat_doit(&doit, msg);
926 case TIPC_CMD_DISABLE_BEARER:
927 msg->req_type = TIPC_TLV_BEARER_NAME;
928 doit.doit = tipc_nl_bearer_disable;
929 doit.transcode = tipc_nl_compat_bearer_disable;
930 return tipc_nl_compat_doit(&doit, msg);
931 case TIPC_CMD_SHOW_LINK_STATS:
932 msg->req_type = TIPC_TLV_LINK_NAME;
933 msg->rep_size = ULTRA_STRING_MAX_LEN;
934 msg->rep_type = TIPC_TLV_ULTRA_STRING;
935 dump.dumpit = tipc_nl_link_dump;
936 dump.format = tipc_nl_compat_link_stat_dump;
937 return tipc_nl_compat_dumpit(&dump, msg);
938 case TIPC_CMD_GET_LINKS:
939 msg->req_type = TIPC_TLV_NET_ADDR;
940 msg->rep_size = ULTRA_STRING_MAX_LEN;
941 dump.dumpit = tipc_nl_link_dump;
942 dump.format = tipc_nl_compat_link_dump;
943 return tipc_nl_compat_dumpit(&dump, msg);
944 case TIPC_CMD_SET_LINK_TOL:
945 case TIPC_CMD_SET_LINK_PRI:
946 case TIPC_CMD_SET_LINK_WINDOW:
947 msg->req_type = TIPC_TLV_LINK_CONFIG;
948 doit.doit = tipc_nl_link_set;
949 doit.transcode = tipc_nl_compat_link_set;
950 return tipc_nl_compat_doit(&doit, msg);
951 case TIPC_CMD_RESET_LINK_STATS:
952 msg->req_type = TIPC_TLV_LINK_NAME;
953 doit.doit = tipc_nl_link_reset_stats;
954 doit.transcode = tipc_nl_compat_link_reset_stats;
955 return tipc_nl_compat_doit(&doit, msg);
956 case TIPC_CMD_SHOW_NAME_TABLE:
957 msg->req_type = TIPC_TLV_NAME_TBL_QUERY;
958 msg->rep_size = ULTRA_STRING_MAX_LEN;
959 msg->rep_type = TIPC_TLV_ULTRA_STRING;
960 dump.header = tipc_nl_compat_name_table_dump_header;
961 dump.dumpit = tipc_nl_name_table_dump;
962 dump.format = tipc_nl_compat_name_table_dump;
963 return tipc_nl_compat_dumpit(&dump, msg);
964 case TIPC_CMD_SHOW_PORTS:
965 msg->rep_size = ULTRA_STRING_MAX_LEN;
966 msg->rep_type = TIPC_TLV_ULTRA_STRING;
967 dump.dumpit = tipc_nl_sk_dump;
968 dump.format = tipc_nl_compat_sk_dump;
969 return tipc_nl_compat_dumpit(&dump, msg);
970 case TIPC_CMD_GET_MEDIA_NAMES:
971 msg->rep_size = MAX_MEDIA * TLV_SPACE(TIPC_MAX_MEDIA_NAME);
972 dump.dumpit = tipc_nl_media_dump;
973 dump.format = tipc_nl_compat_media_dump;
974 return tipc_nl_compat_dumpit(&dump, msg);
975 case TIPC_CMD_GET_NODES:
976 msg->rep_size = ULTRA_STRING_MAX_LEN;
977 dump.dumpit = tipc_nl_node_dump;
978 dump.format = tipc_nl_compat_node_dump;
979 return tipc_nl_compat_dumpit(&dump, msg);
980 case TIPC_CMD_SET_NODE_ADDR:
981 msg->req_type = TIPC_TLV_NET_ADDR;
982 doit.doit = tipc_nl_net_set;
983 doit.transcode = tipc_nl_compat_net_set;
984 return tipc_nl_compat_doit(&doit, msg);
985 case TIPC_CMD_SET_NETID:
986 msg->req_type = TIPC_TLV_UNSIGNED;
987 doit.doit = tipc_nl_net_set;
988 doit.transcode = tipc_nl_compat_net_set;
989 return tipc_nl_compat_doit(&doit, msg);
990 case TIPC_CMD_GET_NETID:
991 msg->rep_size = sizeof(u32);
992 dump.dumpit = tipc_nl_net_dump;
993 dump.format = tipc_nl_compat_net_dump;
994 return tipc_nl_compat_dumpit(&dump, msg);
995 case TIPC_CMD_SHOW_STATS:
996 return tipc_cmd_show_stats_compat(msg);
997 }
998
999 return -EOPNOTSUPP;
1000}
1001
1002static int tipc_nl_compat_recv(struct sk_buff *skb, struct genl_info *info)
1003{
1004 int err;
1005 int len;
1006 struct tipc_nl_compat_msg msg;
1007 struct nlmsghdr *req_nlh;
1008 struct nlmsghdr *rep_nlh;
1009 struct tipc_genlmsghdr *req_userhdr = info->userhdr;
1010 struct net *net = genl_info_net(info);
1011
1012 memset(&msg, 0, sizeof(msg));
1013
1014 req_nlh = (struct nlmsghdr *)skb->data;
1015 msg.req = nlmsg_data(req_nlh) + GENL_HDRLEN + TIPC_GENL_HDRLEN;
1016 msg.cmd = req_userhdr->cmd;
1017 msg.dst_sk = info->dst_sk;
1018
1019 if ((msg.cmd & 0xC000) && (!netlink_net_capable(skb, CAP_NET_ADMIN))) {
1020 msg.rep = tipc_get_err_tlv(TIPC_CFG_NOT_NET_ADMIN);
1021 err = -EACCES;
1022 goto send;
1023 }
1024
1025 len = nlmsg_attrlen(req_nlh, GENL_HDRLEN + TIPC_GENL_HDRLEN);
1026 if (TLV_GET_LEN(msg.req) && !TLV_OK(msg.req, len)) {
1027 msg.rep = tipc_get_err_tlv(TIPC_CFG_NOT_SUPPORTED);
1028 err = -EOPNOTSUPP;
1029 goto send;
1030 }
1031
1032 err = tipc_nl_compat_handle(&msg);
1033 if (err == -EOPNOTSUPP)
1034 msg.rep = tipc_get_err_tlv(TIPC_CFG_NOT_SUPPORTED);
1035 else if (err == -EINVAL)
1036 msg.rep = tipc_get_err_tlv(TIPC_CFG_TLV_ERROR);
1037send:
1038 if (!msg.rep)
1039 return err;
1040
1041 len = nlmsg_total_size(GENL_HDRLEN + TIPC_GENL_HDRLEN);
1042 skb_push(msg.rep, len);
1043 rep_nlh = nlmsg_hdr(msg.rep);
1044 memcpy(rep_nlh, info->nlhdr, len);
1045 rep_nlh->nlmsg_len = msg.rep->len;
1046 genlmsg_unicast(net, msg.rep, NETLINK_CB(skb).portid);
1047
1048 return err;
1049}
1050
1051static struct genl_family tipc_genl_compat_family = {
1052 .id = GENL_ID_GENERATE,
1053 .name = TIPC_GENL_NAME,
1054 .version = TIPC_GENL_VERSION,
1055 .hdrsize = TIPC_GENL_HDRLEN,
1056 .maxattr = 0,
1057 .netnsok = true,
1058};
1059
1060static struct genl_ops tipc_genl_compat_ops[] = {
1061 {
1062 .cmd = TIPC_GENL_CMD,
1063 .doit = tipc_nl_compat_recv,
1064 },
1065};
1066
1067int tipc_netlink_compat_start(void)
1068{
1069 int res;
1070
1071 res = genl_register_family_with_ops(&tipc_genl_compat_family,
1072 tipc_genl_compat_ops);
1073 if (res) {
1074 pr_err("Failed to register legacy compat interface\n");
1075 return res;
1076 }
1077
1078 return 0;
1079}
1080
1081void tipc_netlink_compat_stop(void)
1082{
1083 genl_unregister_family(&tipc_genl_compat_family);
1084}