0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034 #include "core.h"
0035 #include "bearer.h"
0036 #include "link.h"
0037 #include "name_table.h"
0038 #include "socket.h"
0039 #include "node.h"
0040 #include "net.h"
0041 #include <net/genetlink.h>
0042 #include <linux/tipc_config.h>
0043
0044
0045
0046
0047 #define ULTRA_STRING_MAX_LEN 32768
0048
0049 #define TIPC_SKB_MAX TLV_SPACE(ULTRA_STRING_MAX_LEN)
0050
0051 #define REPLY_TRUNCATED "<truncated>\n"
0052
0053 struct tipc_nl_compat_msg {
0054 u16 cmd;
0055 int rep_type;
0056 int rep_size;
0057 int req_type;
0058 int req_size;
0059 struct net *net;
0060 struct sk_buff *rep;
0061 struct tlv_desc *req;
0062 struct sock *dst_sk;
0063 };
0064
0065 struct tipc_nl_compat_cmd_dump {
0066 int (*header)(struct tipc_nl_compat_msg *);
0067 int (*dumpit)(struct sk_buff *, struct netlink_callback *);
0068 int (*format)(struct tipc_nl_compat_msg *msg, struct nlattr **attrs);
0069 };
0070
0071 struct tipc_nl_compat_cmd_doit {
0072 int (*doit)(struct sk_buff *skb, struct genl_info *info);
0073 int (*transcode)(struct tipc_nl_compat_cmd_doit *cmd,
0074 struct sk_buff *skb, struct tipc_nl_compat_msg *msg);
0075 };
0076
0077 static int tipc_skb_tailroom(struct sk_buff *skb)
0078 {
0079 int tailroom;
0080 int limit;
0081
0082 tailroom = skb_tailroom(skb);
0083 limit = TIPC_SKB_MAX - skb->len;
0084
0085 if (tailroom < limit)
0086 return tailroom;
0087
0088 return limit;
0089 }
0090
0091 static inline int TLV_GET_DATA_LEN(struct tlv_desc *tlv)
0092 {
0093 return TLV_GET_LEN(tlv) - TLV_SPACE(0);
0094 }
0095
0096 static int tipc_add_tlv(struct sk_buff *skb, u16 type, void *data, u16 len)
0097 {
0098 struct tlv_desc *tlv = (struct tlv_desc *)skb_tail_pointer(skb);
0099
0100 if (tipc_skb_tailroom(skb) < TLV_SPACE(len))
0101 return -EMSGSIZE;
0102
0103 skb_put(skb, TLV_SPACE(len));
0104 tlv->tlv_type = htons(type);
0105 tlv->tlv_len = htons(TLV_LENGTH(len));
0106 if (len && data)
0107 memcpy(TLV_DATA(tlv), data, len);
0108
0109 return 0;
0110 }
0111
0112 static void tipc_tlv_init(struct sk_buff *skb, u16 type)
0113 {
0114 struct tlv_desc *tlv = (struct tlv_desc *)skb->data;
0115
0116 TLV_SET_LEN(tlv, 0);
0117 TLV_SET_TYPE(tlv, type);
0118 skb_put(skb, sizeof(struct tlv_desc));
0119 }
0120
0121 static __printf(2, 3) int tipc_tlv_sprintf(struct sk_buff *skb,
0122 const char *fmt, ...)
0123 {
0124 int n;
0125 u16 len;
0126 u32 rem;
0127 char *buf;
0128 struct tlv_desc *tlv;
0129 va_list args;
0130
0131 rem = tipc_skb_tailroom(skb);
0132
0133 tlv = (struct tlv_desc *)skb->data;
0134 len = TLV_GET_LEN(tlv);
0135 buf = TLV_DATA(tlv) + len;
0136
0137 va_start(args, fmt);
0138 n = vscnprintf(buf, rem, fmt, args);
0139 va_end(args);
0140
0141 TLV_SET_LEN(tlv, n + len);
0142 skb_put(skb, n);
0143
0144 return n;
0145 }
0146
0147 static struct sk_buff *tipc_tlv_alloc(int size)
0148 {
0149 int hdr_len;
0150 struct sk_buff *buf;
0151
0152 size = TLV_SPACE(size);
0153 hdr_len = nlmsg_total_size(GENL_HDRLEN + TIPC_GENL_HDRLEN);
0154
0155 buf = alloc_skb(hdr_len + size, GFP_KERNEL);
0156 if (!buf)
0157 return NULL;
0158
0159 skb_reserve(buf, hdr_len);
0160
0161 return buf;
0162 }
0163
0164 static struct sk_buff *tipc_get_err_tlv(char *str)
0165 {
0166 int str_len = strlen(str) + 1;
0167 struct sk_buff *buf;
0168
0169 buf = tipc_tlv_alloc(TLV_SPACE(str_len));
0170 if (buf)
0171 tipc_add_tlv(buf, TIPC_TLV_ERROR_STRING, str, str_len);
0172
0173 return buf;
0174 }
0175
0176 static inline bool string_is_valid(char *s, int len)
0177 {
0178 return memchr(s, '\0', len) ? true : false;
0179 }
0180
0181 static int __tipc_nl_compat_dumpit(struct tipc_nl_compat_cmd_dump *cmd,
0182 struct tipc_nl_compat_msg *msg,
0183 struct sk_buff *arg)
0184 {
0185 struct genl_dumpit_info info;
0186 int len = 0;
0187 int err;
0188 struct sk_buff *buf;
0189 struct nlmsghdr *nlmsg;
0190 struct netlink_callback cb;
0191 struct nlattr **attrbuf;
0192
0193 memset(&cb, 0, sizeof(cb));
0194 cb.nlh = (struct nlmsghdr *)arg->data;
0195 cb.skb = arg;
0196 cb.data = &info;
0197
0198 buf = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
0199 if (!buf)
0200 return -ENOMEM;
0201
0202 buf->sk = msg->dst_sk;
0203 if (__tipc_dump_start(&cb, msg->net)) {
0204 kfree_skb(buf);
0205 return -ENOMEM;
0206 }
0207
0208 attrbuf = kcalloc(tipc_genl_family.maxattr + 1,
0209 sizeof(struct nlattr *), GFP_KERNEL);
0210 if (!attrbuf) {
0211 err = -ENOMEM;
0212 goto err_out;
0213 }
0214
0215 info.attrs = attrbuf;
0216
0217 if (nlmsg_len(cb.nlh) > 0) {
0218 err = nlmsg_parse_deprecated(cb.nlh, GENL_HDRLEN, attrbuf,
0219 tipc_genl_family.maxattr,
0220 tipc_genl_family.policy, NULL);
0221 if (err)
0222 goto err_out;
0223 }
0224 do {
0225 int rem;
0226
0227 len = (*cmd->dumpit)(buf, &cb);
0228
0229 nlmsg_for_each_msg(nlmsg, nlmsg_hdr(buf), len, rem) {
0230 err = nlmsg_parse_deprecated(nlmsg, GENL_HDRLEN,
0231 attrbuf,
0232 tipc_genl_family.maxattr,
0233 tipc_genl_family.policy,
0234 NULL);
0235 if (err)
0236 goto err_out;
0237
0238 err = (*cmd->format)(msg, attrbuf);
0239 if (err)
0240 goto err_out;
0241
0242 if (tipc_skb_tailroom(msg->rep) <= 1) {
0243 err = -EMSGSIZE;
0244 goto err_out;
0245 }
0246 }
0247
0248 skb_reset_tail_pointer(buf);
0249 buf->len = 0;
0250
0251 } while (len);
0252
0253 err = 0;
0254
0255 err_out:
0256 kfree(attrbuf);
0257 tipc_dump_done(&cb);
0258 kfree_skb(buf);
0259
0260 if (err == -EMSGSIZE) {
0261
0262
0263
0264 if ((TIPC_SKB_MAX - msg->rep->len) <= 1) {
0265 char *tail = skb_tail_pointer(msg->rep);
0266
0267 if (*tail != '\0')
0268 sprintf(tail - sizeof(REPLY_TRUNCATED) - 1,
0269 REPLY_TRUNCATED);
0270 }
0271
0272 return 0;
0273 }
0274
0275 return err;
0276 }
0277
0278 static int tipc_nl_compat_dumpit(struct tipc_nl_compat_cmd_dump *cmd,
0279 struct tipc_nl_compat_msg *msg)
0280 {
0281 struct nlmsghdr *nlh;
0282 struct sk_buff *arg;
0283 int err;
0284
0285 if (msg->req_type && (!msg->req_size ||
0286 !TLV_CHECK_TYPE(msg->req, msg->req_type)))
0287 return -EINVAL;
0288
0289 msg->rep = tipc_tlv_alloc(msg->rep_size);
0290 if (!msg->rep)
0291 return -ENOMEM;
0292
0293 if (msg->rep_type)
0294 tipc_tlv_init(msg->rep, msg->rep_type);
0295
0296 if (cmd->header) {
0297 err = (*cmd->header)(msg);
0298 if (err) {
0299 kfree_skb(msg->rep);
0300 msg->rep = NULL;
0301 return err;
0302 }
0303 }
0304
0305 arg = nlmsg_new(0, GFP_KERNEL);
0306 if (!arg) {
0307 kfree_skb(msg->rep);
0308 msg->rep = NULL;
0309 return -ENOMEM;
0310 }
0311
0312 nlh = nlmsg_put(arg, 0, 0, tipc_genl_family.id, 0, NLM_F_MULTI);
0313 if (!nlh) {
0314 kfree_skb(arg);
0315 kfree_skb(msg->rep);
0316 msg->rep = NULL;
0317 return -EMSGSIZE;
0318 }
0319 nlmsg_end(arg, nlh);
0320
0321 err = __tipc_nl_compat_dumpit(cmd, msg, arg);
0322 if (err) {
0323 kfree_skb(msg->rep);
0324 msg->rep = NULL;
0325 }
0326 kfree_skb(arg);
0327
0328 return err;
0329 }
0330
0331 static int __tipc_nl_compat_doit(struct tipc_nl_compat_cmd_doit *cmd,
0332 struct tipc_nl_compat_msg *msg)
0333 {
0334 int err;
0335 struct sk_buff *doit_buf;
0336 struct sk_buff *trans_buf;
0337 struct nlattr **attrbuf;
0338 struct genl_info info;
0339
0340 trans_buf = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
0341 if (!trans_buf)
0342 return -ENOMEM;
0343
0344 attrbuf = kmalloc_array(tipc_genl_family.maxattr + 1,
0345 sizeof(struct nlattr *),
0346 GFP_KERNEL);
0347 if (!attrbuf) {
0348 err = -ENOMEM;
0349 goto trans_out;
0350 }
0351
0352 doit_buf = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
0353 if (!doit_buf) {
0354 err = -ENOMEM;
0355 goto attrbuf_out;
0356 }
0357
0358 memset(&info, 0, sizeof(info));
0359 info.attrs = attrbuf;
0360
0361 rtnl_lock();
0362 err = (*cmd->transcode)(cmd, trans_buf, msg);
0363 if (err)
0364 goto doit_out;
0365
0366 err = nla_parse_deprecated(attrbuf, tipc_genl_family.maxattr,
0367 (const struct nlattr *)trans_buf->data,
0368 trans_buf->len, NULL, NULL);
0369 if (err)
0370 goto doit_out;
0371
0372 doit_buf->sk = msg->dst_sk;
0373
0374 err = (*cmd->doit)(doit_buf, &info);
0375 doit_out:
0376 rtnl_unlock();
0377
0378 kfree_skb(doit_buf);
0379 attrbuf_out:
0380 kfree(attrbuf);
0381 trans_out:
0382 kfree_skb(trans_buf);
0383
0384 return err;
0385 }
0386
0387 static int tipc_nl_compat_doit(struct tipc_nl_compat_cmd_doit *cmd,
0388 struct tipc_nl_compat_msg *msg)
0389 {
0390 int err;
0391
0392 if (msg->req_type && (!msg->req_size ||
0393 !TLV_CHECK_TYPE(msg->req, msg->req_type)))
0394 return -EINVAL;
0395
0396 err = __tipc_nl_compat_doit(cmd, msg);
0397 if (err)
0398 return err;
0399
0400
0401 msg->rep = tipc_tlv_alloc(0);
0402 if (!msg->rep)
0403 return -ENOMEM;
0404
0405 return 0;
0406 }
0407
0408 static int tipc_nl_compat_bearer_dump(struct tipc_nl_compat_msg *msg,
0409 struct nlattr **attrs)
0410 {
0411 struct nlattr *bearer[TIPC_NLA_BEARER_MAX + 1];
0412 int err;
0413
0414 if (!attrs[TIPC_NLA_BEARER])
0415 return -EINVAL;
0416
0417 err = nla_parse_nested_deprecated(bearer, TIPC_NLA_BEARER_MAX,
0418 attrs[TIPC_NLA_BEARER], NULL, NULL);
0419 if (err)
0420 return err;
0421
0422 return tipc_add_tlv(msg->rep, TIPC_TLV_BEARER_NAME,
0423 nla_data(bearer[TIPC_NLA_BEARER_NAME]),
0424 nla_len(bearer[TIPC_NLA_BEARER_NAME]));
0425 }
0426
0427 static int tipc_nl_compat_bearer_enable(struct tipc_nl_compat_cmd_doit *cmd,
0428 struct sk_buff *skb,
0429 struct tipc_nl_compat_msg *msg)
0430 {
0431 struct nlattr *prop;
0432 struct nlattr *bearer;
0433 struct tipc_bearer_config *b;
0434 int len;
0435
0436 b = (struct tipc_bearer_config *)TLV_DATA(msg->req);
0437
0438 bearer = nla_nest_start_noflag(skb, TIPC_NLA_BEARER);
0439 if (!bearer)
0440 return -EMSGSIZE;
0441
0442 len = TLV_GET_DATA_LEN(msg->req);
0443 len -= offsetof(struct tipc_bearer_config, name);
0444 if (len <= 0)
0445 return -EINVAL;
0446
0447 len = min_t(int, len, TIPC_MAX_BEARER_NAME);
0448 if (!string_is_valid(b->name, len))
0449 return -EINVAL;
0450
0451 if (nla_put_string(skb, TIPC_NLA_BEARER_NAME, b->name))
0452 return -EMSGSIZE;
0453
0454 if (nla_put_u32(skb, TIPC_NLA_BEARER_DOMAIN, ntohl(b->disc_domain)))
0455 return -EMSGSIZE;
0456
0457 if (ntohl(b->priority) <= TIPC_MAX_LINK_PRI) {
0458 prop = nla_nest_start_noflag(skb, TIPC_NLA_BEARER_PROP);
0459 if (!prop)
0460 return -EMSGSIZE;
0461 if (nla_put_u32(skb, TIPC_NLA_PROP_PRIO, ntohl(b->priority)))
0462 return -EMSGSIZE;
0463 nla_nest_end(skb, prop);
0464 }
0465 nla_nest_end(skb, bearer);
0466
0467 return 0;
0468 }
0469
0470 static int tipc_nl_compat_bearer_disable(struct tipc_nl_compat_cmd_doit *cmd,
0471 struct sk_buff *skb,
0472 struct tipc_nl_compat_msg *msg)
0473 {
0474 char *name;
0475 struct nlattr *bearer;
0476 int len;
0477
0478 name = (char *)TLV_DATA(msg->req);
0479
0480 bearer = nla_nest_start_noflag(skb, TIPC_NLA_BEARER);
0481 if (!bearer)
0482 return -EMSGSIZE;
0483
0484 len = TLV_GET_DATA_LEN(msg->req);
0485 if (len <= 0)
0486 return -EINVAL;
0487
0488 len = min_t(int, len, TIPC_MAX_BEARER_NAME);
0489 if (!string_is_valid(name, len))
0490 return -EINVAL;
0491
0492 if (nla_put_string(skb, TIPC_NLA_BEARER_NAME, name))
0493 return -EMSGSIZE;
0494
0495 nla_nest_end(skb, bearer);
0496
0497 return 0;
0498 }
0499
0500 static inline u32 perc(u32 count, u32 total)
0501 {
0502 return (count * 100 + (total / 2)) / total;
0503 }
0504
0505 static void __fill_bc_link_stat(struct tipc_nl_compat_msg *msg,
0506 struct nlattr *prop[], struct nlattr *stats[])
0507 {
0508 tipc_tlv_sprintf(msg->rep, " Window:%u packets\n",
0509 nla_get_u32(prop[TIPC_NLA_PROP_WIN]));
0510
0511 tipc_tlv_sprintf(msg->rep,
0512 " RX packets:%u fragments:%u/%u bundles:%u/%u\n",
0513 nla_get_u32(stats[TIPC_NLA_STATS_RX_INFO]),
0514 nla_get_u32(stats[TIPC_NLA_STATS_RX_FRAGMENTS]),
0515 nla_get_u32(stats[TIPC_NLA_STATS_RX_FRAGMENTED]),
0516 nla_get_u32(stats[TIPC_NLA_STATS_RX_BUNDLES]),
0517 nla_get_u32(stats[TIPC_NLA_STATS_RX_BUNDLED]));
0518
0519 tipc_tlv_sprintf(msg->rep,
0520 " TX packets:%u fragments:%u/%u bundles:%u/%u\n",
0521 nla_get_u32(stats[TIPC_NLA_STATS_TX_INFO]),
0522 nla_get_u32(stats[TIPC_NLA_STATS_TX_FRAGMENTS]),
0523 nla_get_u32(stats[TIPC_NLA_STATS_TX_FRAGMENTED]),
0524 nla_get_u32(stats[TIPC_NLA_STATS_TX_BUNDLES]),
0525 nla_get_u32(stats[TIPC_NLA_STATS_TX_BUNDLED]));
0526
0527 tipc_tlv_sprintf(msg->rep, " RX naks:%u defs:%u dups:%u\n",
0528 nla_get_u32(stats[TIPC_NLA_STATS_RX_NACKS]),
0529 nla_get_u32(stats[TIPC_NLA_STATS_RX_DEFERRED]),
0530 nla_get_u32(stats[TIPC_NLA_STATS_DUPLICATES]));
0531
0532 tipc_tlv_sprintf(msg->rep, " TX naks:%u acks:%u dups:%u\n",
0533 nla_get_u32(stats[TIPC_NLA_STATS_TX_NACKS]),
0534 nla_get_u32(stats[TIPC_NLA_STATS_TX_ACKS]),
0535 nla_get_u32(stats[TIPC_NLA_STATS_RETRANSMITTED]));
0536
0537 tipc_tlv_sprintf(msg->rep,
0538 " Congestion link:%u Send queue max:%u avg:%u",
0539 nla_get_u32(stats[TIPC_NLA_STATS_LINK_CONGS]),
0540 nla_get_u32(stats[TIPC_NLA_STATS_MAX_QUEUE]),
0541 nla_get_u32(stats[TIPC_NLA_STATS_AVG_QUEUE]));
0542 }
0543
0544 static int tipc_nl_compat_link_stat_dump(struct tipc_nl_compat_msg *msg,
0545 struct nlattr **attrs)
0546 {
0547 char *name;
0548 struct nlattr *link[TIPC_NLA_LINK_MAX + 1];
0549 struct nlattr *prop[TIPC_NLA_PROP_MAX + 1];
0550 struct nlattr *stats[TIPC_NLA_STATS_MAX + 1];
0551 int err;
0552 int len;
0553
0554 if (!attrs[TIPC_NLA_LINK])
0555 return -EINVAL;
0556
0557 err = nla_parse_nested_deprecated(link, TIPC_NLA_LINK_MAX,
0558 attrs[TIPC_NLA_LINK], NULL, NULL);
0559 if (err)
0560 return err;
0561
0562 if (!link[TIPC_NLA_LINK_PROP])
0563 return -EINVAL;
0564
0565 err = nla_parse_nested_deprecated(prop, TIPC_NLA_PROP_MAX,
0566 link[TIPC_NLA_LINK_PROP], NULL,
0567 NULL);
0568 if (err)
0569 return err;
0570
0571 if (!link[TIPC_NLA_LINK_STATS])
0572 return -EINVAL;
0573
0574 err = nla_parse_nested_deprecated(stats, TIPC_NLA_STATS_MAX,
0575 link[TIPC_NLA_LINK_STATS], NULL,
0576 NULL);
0577 if (err)
0578 return err;
0579
0580 name = (char *)TLV_DATA(msg->req);
0581
0582 len = TLV_GET_DATA_LEN(msg->req);
0583 if (len <= 0)
0584 return -EINVAL;
0585
0586 len = min_t(int, len, TIPC_MAX_LINK_NAME);
0587 if (!string_is_valid(name, len))
0588 return -EINVAL;
0589
0590 if (strcmp(name, nla_data(link[TIPC_NLA_LINK_NAME])) != 0)
0591 return 0;
0592
0593 tipc_tlv_sprintf(msg->rep, "\nLink <%s>\n",
0594 (char *)nla_data(link[TIPC_NLA_LINK_NAME]));
0595
0596 if (link[TIPC_NLA_LINK_BROADCAST]) {
0597 __fill_bc_link_stat(msg, prop, stats);
0598 return 0;
0599 }
0600
0601 if (link[TIPC_NLA_LINK_ACTIVE])
0602 tipc_tlv_sprintf(msg->rep, " ACTIVE");
0603 else if (link[TIPC_NLA_LINK_UP])
0604 tipc_tlv_sprintf(msg->rep, " STANDBY");
0605 else
0606 tipc_tlv_sprintf(msg->rep, " DEFUNCT");
0607
0608 tipc_tlv_sprintf(msg->rep, " MTU:%u Priority:%u",
0609 nla_get_u32(link[TIPC_NLA_LINK_MTU]),
0610 nla_get_u32(prop[TIPC_NLA_PROP_PRIO]));
0611
0612 tipc_tlv_sprintf(msg->rep, " Tolerance:%u ms Window:%u packets\n",
0613 nla_get_u32(prop[TIPC_NLA_PROP_TOL]),
0614 nla_get_u32(prop[TIPC_NLA_PROP_WIN]));
0615
0616 tipc_tlv_sprintf(msg->rep,
0617 " RX packets:%u fragments:%u/%u bundles:%u/%u\n",
0618 nla_get_u32(link[TIPC_NLA_LINK_RX]) -
0619 nla_get_u32(stats[TIPC_NLA_STATS_RX_INFO]),
0620 nla_get_u32(stats[TIPC_NLA_STATS_RX_FRAGMENTS]),
0621 nla_get_u32(stats[TIPC_NLA_STATS_RX_FRAGMENTED]),
0622 nla_get_u32(stats[TIPC_NLA_STATS_RX_BUNDLES]),
0623 nla_get_u32(stats[TIPC_NLA_STATS_RX_BUNDLED]));
0624
0625 tipc_tlv_sprintf(msg->rep,
0626 " TX packets:%u fragments:%u/%u bundles:%u/%u\n",
0627 nla_get_u32(link[TIPC_NLA_LINK_TX]) -
0628 nla_get_u32(stats[TIPC_NLA_STATS_TX_INFO]),
0629 nla_get_u32(stats[TIPC_NLA_STATS_TX_FRAGMENTS]),
0630 nla_get_u32(stats[TIPC_NLA_STATS_TX_FRAGMENTED]),
0631 nla_get_u32(stats[TIPC_NLA_STATS_TX_BUNDLES]),
0632 nla_get_u32(stats[TIPC_NLA_STATS_TX_BUNDLED]));
0633
0634 tipc_tlv_sprintf(msg->rep,
0635 " TX profile sample:%u packets average:%u octets\n",
0636 nla_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_CNT]),
0637 nla_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_TOT]) /
0638 nla_get_u32(stats[TIPC_NLA_STATS_MSG_PROF_TOT]));
0639
0640 tipc_tlv_sprintf(msg->rep,
0641 " 0-64:%u%% -256:%u%% -1024:%u%% -4096:%u%% ",
0642 perc(nla_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P0]),
0643 nla_get_u32(stats[TIPC_NLA_STATS_MSG_PROF_TOT])),
0644 perc(nla_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P1]),
0645 nla_get_u32(stats[TIPC_NLA_STATS_MSG_PROF_TOT])),
0646 perc(nla_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P2]),
0647 nla_get_u32(stats[TIPC_NLA_STATS_MSG_PROF_TOT])),
0648 perc(nla_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P3]),
0649 nla_get_u32(stats[TIPC_NLA_STATS_MSG_PROF_TOT])));
0650
0651 tipc_tlv_sprintf(msg->rep, "-16384:%u%% -32768:%u%% -66000:%u%%\n",
0652 perc(nla_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P4]),
0653 nla_get_u32(stats[TIPC_NLA_STATS_MSG_PROF_TOT])),
0654 perc(nla_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P5]),
0655 nla_get_u32(stats[TIPC_NLA_STATS_MSG_PROF_TOT])),
0656 perc(nla_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P6]),
0657 nla_get_u32(stats[TIPC_NLA_STATS_MSG_PROF_TOT])));
0658
0659 tipc_tlv_sprintf(msg->rep,
0660 " RX states:%u probes:%u naks:%u defs:%u dups:%u\n",
0661 nla_get_u32(stats[TIPC_NLA_STATS_RX_STATES]),
0662 nla_get_u32(stats[TIPC_NLA_STATS_RX_PROBES]),
0663 nla_get_u32(stats[TIPC_NLA_STATS_RX_NACKS]),
0664 nla_get_u32(stats[TIPC_NLA_STATS_RX_DEFERRED]),
0665 nla_get_u32(stats[TIPC_NLA_STATS_DUPLICATES]));
0666
0667 tipc_tlv_sprintf(msg->rep,
0668 " TX states:%u probes:%u naks:%u acks:%u dups:%u\n",
0669 nla_get_u32(stats[TIPC_NLA_STATS_TX_STATES]),
0670 nla_get_u32(stats[TIPC_NLA_STATS_TX_PROBES]),
0671 nla_get_u32(stats[TIPC_NLA_STATS_TX_NACKS]),
0672 nla_get_u32(stats[TIPC_NLA_STATS_TX_ACKS]),
0673 nla_get_u32(stats[TIPC_NLA_STATS_RETRANSMITTED]));
0674
0675 tipc_tlv_sprintf(msg->rep,
0676 " Congestion link:%u Send queue max:%u avg:%u",
0677 nla_get_u32(stats[TIPC_NLA_STATS_LINK_CONGS]),
0678 nla_get_u32(stats[TIPC_NLA_STATS_MAX_QUEUE]),
0679 nla_get_u32(stats[TIPC_NLA_STATS_AVG_QUEUE]));
0680
0681 return 0;
0682 }
0683
0684 static int tipc_nl_compat_link_dump(struct tipc_nl_compat_msg *msg,
0685 struct nlattr **attrs)
0686 {
0687 struct nlattr *link[TIPC_NLA_LINK_MAX + 1];
0688 struct tipc_link_info link_info;
0689 int err;
0690
0691 if (!attrs[TIPC_NLA_LINK])
0692 return -EINVAL;
0693
0694 err = nla_parse_nested_deprecated(link, TIPC_NLA_LINK_MAX,
0695 attrs[TIPC_NLA_LINK], NULL, NULL);
0696 if (err)
0697 return err;
0698
0699 link_info.dest = htonl(nla_get_flag(link[TIPC_NLA_LINK_DEST]));
0700 link_info.up = htonl(nla_get_flag(link[TIPC_NLA_LINK_UP]));
0701 nla_strscpy(link_info.str, link[TIPC_NLA_LINK_NAME],
0702 TIPC_MAX_LINK_NAME);
0703
0704 return tipc_add_tlv(msg->rep, TIPC_TLV_LINK_INFO,
0705 &link_info, sizeof(link_info));
0706 }
0707
0708 static int __tipc_add_link_prop(struct sk_buff *skb,
0709 struct tipc_nl_compat_msg *msg,
0710 struct tipc_link_config *lc)
0711 {
0712 switch (msg->cmd) {
0713 case TIPC_CMD_SET_LINK_PRI:
0714 return nla_put_u32(skb, TIPC_NLA_PROP_PRIO, ntohl(lc->value));
0715 case TIPC_CMD_SET_LINK_TOL:
0716 return nla_put_u32(skb, TIPC_NLA_PROP_TOL, ntohl(lc->value));
0717 case TIPC_CMD_SET_LINK_WINDOW:
0718 return nla_put_u32(skb, TIPC_NLA_PROP_WIN, ntohl(lc->value));
0719 }
0720
0721 return -EINVAL;
0722 }
0723
0724 static int tipc_nl_compat_media_set(struct sk_buff *skb,
0725 struct tipc_nl_compat_msg *msg)
0726 {
0727 struct nlattr *prop;
0728 struct nlattr *media;
0729 struct tipc_link_config *lc;
0730
0731 lc = (struct tipc_link_config *)TLV_DATA(msg->req);
0732
0733 media = nla_nest_start_noflag(skb, TIPC_NLA_MEDIA);
0734 if (!media)
0735 return -EMSGSIZE;
0736
0737 if (nla_put_string(skb, TIPC_NLA_MEDIA_NAME, lc->name))
0738 return -EMSGSIZE;
0739
0740 prop = nla_nest_start_noflag(skb, TIPC_NLA_MEDIA_PROP);
0741 if (!prop)
0742 return -EMSGSIZE;
0743
0744 __tipc_add_link_prop(skb, msg, lc);
0745 nla_nest_end(skb, prop);
0746 nla_nest_end(skb, media);
0747
0748 return 0;
0749 }
0750
0751 static int tipc_nl_compat_bearer_set(struct sk_buff *skb,
0752 struct tipc_nl_compat_msg *msg)
0753 {
0754 struct nlattr *prop;
0755 struct nlattr *bearer;
0756 struct tipc_link_config *lc;
0757
0758 lc = (struct tipc_link_config *)TLV_DATA(msg->req);
0759
0760 bearer = nla_nest_start_noflag(skb, TIPC_NLA_BEARER);
0761 if (!bearer)
0762 return -EMSGSIZE;
0763
0764 if (nla_put_string(skb, TIPC_NLA_BEARER_NAME, lc->name))
0765 return -EMSGSIZE;
0766
0767 prop = nla_nest_start_noflag(skb, TIPC_NLA_BEARER_PROP);
0768 if (!prop)
0769 return -EMSGSIZE;
0770
0771 __tipc_add_link_prop(skb, msg, lc);
0772 nla_nest_end(skb, prop);
0773 nla_nest_end(skb, bearer);
0774
0775 return 0;
0776 }
0777
0778 static int __tipc_nl_compat_link_set(struct sk_buff *skb,
0779 struct tipc_nl_compat_msg *msg)
0780 {
0781 struct nlattr *prop;
0782 struct nlattr *link;
0783 struct tipc_link_config *lc;
0784
0785 lc = (struct tipc_link_config *)TLV_DATA(msg->req);
0786
0787 link = nla_nest_start_noflag(skb, TIPC_NLA_LINK);
0788 if (!link)
0789 return -EMSGSIZE;
0790
0791 if (nla_put_string(skb, TIPC_NLA_LINK_NAME, lc->name))
0792 return -EMSGSIZE;
0793
0794 prop = nla_nest_start_noflag(skb, TIPC_NLA_LINK_PROP);
0795 if (!prop)
0796 return -EMSGSIZE;
0797
0798 __tipc_add_link_prop(skb, msg, lc);
0799 nla_nest_end(skb, prop);
0800 nla_nest_end(skb, link);
0801
0802 return 0;
0803 }
0804
0805 static int tipc_nl_compat_link_set(struct tipc_nl_compat_cmd_doit *cmd,
0806 struct sk_buff *skb,
0807 struct tipc_nl_compat_msg *msg)
0808 {
0809 struct tipc_link_config *lc;
0810 struct tipc_bearer *bearer;
0811 struct tipc_media *media;
0812 int len;
0813
0814 lc = (struct tipc_link_config *)TLV_DATA(msg->req);
0815
0816 len = TLV_GET_DATA_LEN(msg->req);
0817 len -= offsetof(struct tipc_link_config, name);
0818 if (len <= 0)
0819 return -EINVAL;
0820
0821 len = min_t(int, len, TIPC_MAX_LINK_NAME);
0822 if (!string_is_valid(lc->name, len))
0823 return -EINVAL;
0824
0825 media = tipc_media_find(lc->name);
0826 if (media) {
0827 cmd->doit = &__tipc_nl_media_set;
0828 return tipc_nl_compat_media_set(skb, msg);
0829 }
0830
0831 bearer = tipc_bearer_find(msg->net, lc->name);
0832 if (bearer) {
0833 cmd->doit = &__tipc_nl_bearer_set;
0834 return tipc_nl_compat_bearer_set(skb, msg);
0835 }
0836
0837 return __tipc_nl_compat_link_set(skb, msg);
0838 }
0839
0840 static int tipc_nl_compat_link_reset_stats(struct tipc_nl_compat_cmd_doit *cmd,
0841 struct sk_buff *skb,
0842 struct tipc_nl_compat_msg *msg)
0843 {
0844 char *name;
0845 struct nlattr *link;
0846 int len;
0847
0848 name = (char *)TLV_DATA(msg->req);
0849
0850 link = nla_nest_start_noflag(skb, TIPC_NLA_LINK);
0851 if (!link)
0852 return -EMSGSIZE;
0853
0854 len = TLV_GET_DATA_LEN(msg->req);
0855 if (len <= 0)
0856 return -EINVAL;
0857
0858 len = min_t(int, len, TIPC_MAX_LINK_NAME);
0859 if (!string_is_valid(name, len))
0860 return -EINVAL;
0861
0862 if (nla_put_string(skb, TIPC_NLA_LINK_NAME, name))
0863 return -EMSGSIZE;
0864
0865 nla_nest_end(skb, link);
0866
0867 return 0;
0868 }
0869
0870 static int tipc_nl_compat_name_table_dump_header(struct tipc_nl_compat_msg *msg)
0871 {
0872 int i;
0873 u32 depth;
0874 struct tipc_name_table_query *ntq;
0875 static const char * const header[] = {
0876 "Type ",
0877 "Lower Upper ",
0878 "Port Identity ",
0879 "Publication Scope"
0880 };
0881
0882 ntq = (struct tipc_name_table_query *)TLV_DATA(msg->req);
0883 if (TLV_GET_DATA_LEN(msg->req) < sizeof(struct tipc_name_table_query))
0884 return -EINVAL;
0885
0886 depth = ntohl(ntq->depth);
0887
0888 if (depth > 4)
0889 depth = 4;
0890 for (i = 0; i < depth; i++)
0891 tipc_tlv_sprintf(msg->rep, header[i]);
0892 tipc_tlv_sprintf(msg->rep, "\n");
0893
0894 return 0;
0895 }
0896
0897 static int tipc_nl_compat_name_table_dump(struct tipc_nl_compat_msg *msg,
0898 struct nlattr **attrs)
0899 {
0900 char port_str[27];
0901 struct tipc_name_table_query *ntq;
0902 struct nlattr *nt[TIPC_NLA_NAME_TABLE_MAX + 1];
0903 struct nlattr *publ[TIPC_NLA_PUBL_MAX + 1];
0904 u32 node, depth, type, lowbound, upbound;
0905 static const char * const scope_str[] = {"", " zone", " cluster",
0906 " node"};
0907 int err;
0908
0909 if (!attrs[TIPC_NLA_NAME_TABLE])
0910 return -EINVAL;
0911
0912 err = nla_parse_nested_deprecated(nt, TIPC_NLA_NAME_TABLE_MAX,
0913 attrs[TIPC_NLA_NAME_TABLE], NULL,
0914 NULL);
0915 if (err)
0916 return err;
0917
0918 if (!nt[TIPC_NLA_NAME_TABLE_PUBL])
0919 return -EINVAL;
0920
0921 err = nla_parse_nested_deprecated(publ, TIPC_NLA_PUBL_MAX,
0922 nt[TIPC_NLA_NAME_TABLE_PUBL], NULL,
0923 NULL);
0924 if (err)
0925 return err;
0926
0927 ntq = (struct tipc_name_table_query *)TLV_DATA(msg->req);
0928
0929 depth = ntohl(ntq->depth);
0930 type = ntohl(ntq->type);
0931 lowbound = ntohl(ntq->lowbound);
0932 upbound = ntohl(ntq->upbound);
0933
0934 if (!(depth & TIPC_NTQ_ALLTYPES) &&
0935 (type != nla_get_u32(publ[TIPC_NLA_PUBL_TYPE])))
0936 return 0;
0937 if (lowbound && (lowbound > nla_get_u32(publ[TIPC_NLA_PUBL_UPPER])))
0938 return 0;
0939 if (upbound && (upbound < nla_get_u32(publ[TIPC_NLA_PUBL_LOWER])))
0940 return 0;
0941
0942 tipc_tlv_sprintf(msg->rep, "%-10u ",
0943 nla_get_u32(publ[TIPC_NLA_PUBL_TYPE]));
0944
0945 if (depth == 1)
0946 goto out;
0947
0948 tipc_tlv_sprintf(msg->rep, "%-10u %-10u ",
0949 nla_get_u32(publ[TIPC_NLA_PUBL_LOWER]),
0950 nla_get_u32(publ[TIPC_NLA_PUBL_UPPER]));
0951
0952 if (depth == 2)
0953 goto out;
0954
0955 node = nla_get_u32(publ[TIPC_NLA_PUBL_NODE]);
0956 sprintf(port_str, "<%u.%u.%u:%u>", tipc_zone(node), tipc_cluster(node),
0957 tipc_node(node), nla_get_u32(publ[TIPC_NLA_PUBL_REF]));
0958 tipc_tlv_sprintf(msg->rep, "%-26s ", port_str);
0959
0960 if (depth == 3)
0961 goto out;
0962
0963 tipc_tlv_sprintf(msg->rep, "%-10u %s",
0964 nla_get_u32(publ[TIPC_NLA_PUBL_KEY]),
0965 scope_str[nla_get_u32(publ[TIPC_NLA_PUBL_SCOPE])]);
0966 out:
0967 tipc_tlv_sprintf(msg->rep, "\n");
0968
0969 return 0;
0970 }
0971
0972 static int __tipc_nl_compat_publ_dump(struct tipc_nl_compat_msg *msg,
0973 struct nlattr **attrs)
0974 {
0975 u32 type, lower, upper;
0976 struct nlattr *publ[TIPC_NLA_PUBL_MAX + 1];
0977 int err;
0978
0979 if (!attrs[TIPC_NLA_PUBL])
0980 return -EINVAL;
0981
0982 err = nla_parse_nested_deprecated(publ, TIPC_NLA_PUBL_MAX,
0983 attrs[TIPC_NLA_PUBL], NULL, NULL);
0984 if (err)
0985 return err;
0986
0987 type = nla_get_u32(publ[TIPC_NLA_PUBL_TYPE]);
0988 lower = nla_get_u32(publ[TIPC_NLA_PUBL_LOWER]);
0989 upper = nla_get_u32(publ[TIPC_NLA_PUBL_UPPER]);
0990
0991 if (lower == upper)
0992 tipc_tlv_sprintf(msg->rep, " {%u,%u}", type, lower);
0993 else
0994 tipc_tlv_sprintf(msg->rep, " {%u,%u,%u}", type, lower, upper);
0995
0996 return 0;
0997 }
0998
0999 static int tipc_nl_compat_publ_dump(struct tipc_nl_compat_msg *msg, u32 sock)
1000 {
1001 int err;
1002 void *hdr;
1003 struct nlattr *nest;
1004 struct sk_buff *args;
1005 struct tipc_nl_compat_cmd_dump dump;
1006
1007 args = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
1008 if (!args)
1009 return -ENOMEM;
1010
1011 hdr = genlmsg_put(args, 0, 0, &tipc_genl_family, NLM_F_MULTI,
1012 TIPC_NL_PUBL_GET);
1013 if (!hdr) {
1014 kfree_skb(args);
1015 return -EMSGSIZE;
1016 }
1017
1018 nest = nla_nest_start_noflag(args, TIPC_NLA_SOCK);
1019 if (!nest) {
1020 kfree_skb(args);
1021 return -EMSGSIZE;
1022 }
1023
1024 if (nla_put_u32(args, TIPC_NLA_SOCK_REF, sock)) {
1025 kfree_skb(args);
1026 return -EMSGSIZE;
1027 }
1028
1029 nla_nest_end(args, nest);
1030 genlmsg_end(args, hdr);
1031
1032 dump.dumpit = tipc_nl_publ_dump;
1033 dump.format = __tipc_nl_compat_publ_dump;
1034
1035 err = __tipc_nl_compat_dumpit(&dump, msg, args);
1036
1037 kfree_skb(args);
1038
1039 return err;
1040 }
1041
1042 static int tipc_nl_compat_sk_dump(struct tipc_nl_compat_msg *msg,
1043 struct nlattr **attrs)
1044 {
1045 int err;
1046 u32 sock_ref;
1047 struct nlattr *sock[TIPC_NLA_SOCK_MAX + 1];
1048
1049 if (!attrs[TIPC_NLA_SOCK])
1050 return -EINVAL;
1051
1052 err = nla_parse_nested_deprecated(sock, TIPC_NLA_SOCK_MAX,
1053 attrs[TIPC_NLA_SOCK], NULL, NULL);
1054 if (err)
1055 return err;
1056
1057 sock_ref = nla_get_u32(sock[TIPC_NLA_SOCK_REF]);
1058 tipc_tlv_sprintf(msg->rep, "%u:", sock_ref);
1059
1060 if (sock[TIPC_NLA_SOCK_CON]) {
1061 u32 node;
1062 struct nlattr *con[TIPC_NLA_CON_MAX + 1];
1063
1064 err = nla_parse_nested_deprecated(con, TIPC_NLA_CON_MAX,
1065 sock[TIPC_NLA_SOCK_CON],
1066 NULL, NULL);
1067
1068 if (err)
1069 return err;
1070
1071 node = nla_get_u32(con[TIPC_NLA_CON_NODE]);
1072 tipc_tlv_sprintf(msg->rep, " connected to <%u.%u.%u:%u>",
1073 tipc_zone(node),
1074 tipc_cluster(node),
1075 tipc_node(node),
1076 nla_get_u32(con[TIPC_NLA_CON_SOCK]));
1077
1078 if (con[TIPC_NLA_CON_FLAG])
1079 tipc_tlv_sprintf(msg->rep, " via {%u,%u}\n",
1080 nla_get_u32(con[TIPC_NLA_CON_TYPE]),
1081 nla_get_u32(con[TIPC_NLA_CON_INST]));
1082 else
1083 tipc_tlv_sprintf(msg->rep, "\n");
1084 } else if (sock[TIPC_NLA_SOCK_HAS_PUBL]) {
1085 tipc_tlv_sprintf(msg->rep, " bound to");
1086
1087 err = tipc_nl_compat_publ_dump(msg, sock_ref);
1088 if (err)
1089 return err;
1090 }
1091 tipc_tlv_sprintf(msg->rep, "\n");
1092
1093 return 0;
1094 }
1095
1096 static int tipc_nl_compat_media_dump(struct tipc_nl_compat_msg *msg,
1097 struct nlattr **attrs)
1098 {
1099 struct nlattr *media[TIPC_NLA_MEDIA_MAX + 1];
1100 int err;
1101
1102 if (!attrs[TIPC_NLA_MEDIA])
1103 return -EINVAL;
1104
1105 err = nla_parse_nested_deprecated(media, TIPC_NLA_MEDIA_MAX,
1106 attrs[TIPC_NLA_MEDIA], NULL, NULL);
1107 if (err)
1108 return err;
1109
1110 return tipc_add_tlv(msg->rep, TIPC_TLV_MEDIA_NAME,
1111 nla_data(media[TIPC_NLA_MEDIA_NAME]),
1112 nla_len(media[TIPC_NLA_MEDIA_NAME]));
1113 }
1114
1115 static int tipc_nl_compat_node_dump(struct tipc_nl_compat_msg *msg,
1116 struct nlattr **attrs)
1117 {
1118 struct tipc_node_info node_info;
1119 struct nlattr *node[TIPC_NLA_NODE_MAX + 1];
1120 int err;
1121
1122 if (!attrs[TIPC_NLA_NODE])
1123 return -EINVAL;
1124
1125 err = nla_parse_nested_deprecated(node, TIPC_NLA_NODE_MAX,
1126 attrs[TIPC_NLA_NODE], NULL, NULL);
1127 if (err)
1128 return err;
1129
1130 node_info.addr = htonl(nla_get_u32(node[TIPC_NLA_NODE_ADDR]));
1131 node_info.up = htonl(nla_get_flag(node[TIPC_NLA_NODE_UP]));
1132
1133 return tipc_add_tlv(msg->rep, TIPC_TLV_NODE_INFO, &node_info,
1134 sizeof(node_info));
1135 }
1136
1137 static int tipc_nl_compat_net_set(struct tipc_nl_compat_cmd_doit *cmd,
1138 struct sk_buff *skb,
1139 struct tipc_nl_compat_msg *msg)
1140 {
1141 u32 val;
1142 struct nlattr *net;
1143
1144 val = ntohl(*(__be32 *)TLV_DATA(msg->req));
1145
1146 net = nla_nest_start_noflag(skb, TIPC_NLA_NET);
1147 if (!net)
1148 return -EMSGSIZE;
1149
1150 if (msg->cmd == TIPC_CMD_SET_NODE_ADDR) {
1151 if (nla_put_u32(skb, TIPC_NLA_NET_ADDR, val))
1152 return -EMSGSIZE;
1153 } else if (msg->cmd == TIPC_CMD_SET_NETID) {
1154 if (nla_put_u32(skb, TIPC_NLA_NET_ID, val))
1155 return -EMSGSIZE;
1156 }
1157 nla_nest_end(skb, net);
1158
1159 return 0;
1160 }
1161
1162 static int tipc_nl_compat_net_dump(struct tipc_nl_compat_msg *msg,
1163 struct nlattr **attrs)
1164 {
1165 __be32 id;
1166 struct nlattr *net[TIPC_NLA_NET_MAX + 1];
1167 int err;
1168
1169 if (!attrs[TIPC_NLA_NET])
1170 return -EINVAL;
1171
1172 err = nla_parse_nested_deprecated(net, TIPC_NLA_NET_MAX,
1173 attrs[TIPC_NLA_NET], NULL, NULL);
1174 if (err)
1175 return err;
1176
1177 id = htonl(nla_get_u32(net[TIPC_NLA_NET_ID]));
1178
1179 return tipc_add_tlv(msg->rep, TIPC_TLV_UNSIGNED, &id, sizeof(id));
1180 }
1181
1182 static int tipc_cmd_show_stats_compat(struct tipc_nl_compat_msg *msg)
1183 {
1184 msg->rep = tipc_tlv_alloc(ULTRA_STRING_MAX_LEN);
1185 if (!msg->rep)
1186 return -ENOMEM;
1187
1188 tipc_tlv_init(msg->rep, TIPC_TLV_ULTRA_STRING);
1189 tipc_tlv_sprintf(msg->rep, "TIPC version " TIPC_MOD_VER "\n");
1190
1191 return 0;
1192 }
1193
1194 static int tipc_nl_compat_handle(struct tipc_nl_compat_msg *msg)
1195 {
1196 struct tipc_nl_compat_cmd_dump dump;
1197 struct tipc_nl_compat_cmd_doit doit;
1198
1199 memset(&dump, 0, sizeof(dump));
1200 memset(&doit, 0, sizeof(doit));
1201
1202 switch (msg->cmd) {
1203 case TIPC_CMD_NOOP:
1204 msg->rep = tipc_tlv_alloc(0);
1205 if (!msg->rep)
1206 return -ENOMEM;
1207 return 0;
1208 case TIPC_CMD_GET_BEARER_NAMES:
1209 msg->rep_size = MAX_BEARERS * TLV_SPACE(TIPC_MAX_BEARER_NAME);
1210 dump.dumpit = tipc_nl_bearer_dump;
1211 dump.format = tipc_nl_compat_bearer_dump;
1212 return tipc_nl_compat_dumpit(&dump, msg);
1213 case TIPC_CMD_ENABLE_BEARER:
1214 msg->req_type = TIPC_TLV_BEARER_CONFIG;
1215 doit.doit = __tipc_nl_bearer_enable;
1216 doit.transcode = tipc_nl_compat_bearer_enable;
1217 return tipc_nl_compat_doit(&doit, msg);
1218 case TIPC_CMD_DISABLE_BEARER:
1219 msg->req_type = TIPC_TLV_BEARER_NAME;
1220 doit.doit = __tipc_nl_bearer_disable;
1221 doit.transcode = tipc_nl_compat_bearer_disable;
1222 return tipc_nl_compat_doit(&doit, msg);
1223 case TIPC_CMD_SHOW_LINK_STATS:
1224 msg->req_type = TIPC_TLV_LINK_NAME;
1225 msg->rep_size = ULTRA_STRING_MAX_LEN;
1226 msg->rep_type = TIPC_TLV_ULTRA_STRING;
1227 dump.dumpit = tipc_nl_node_dump_link;
1228 dump.format = tipc_nl_compat_link_stat_dump;
1229 return tipc_nl_compat_dumpit(&dump, msg);
1230 case TIPC_CMD_GET_LINKS:
1231 msg->req_type = TIPC_TLV_NET_ADDR;
1232 msg->rep_size = ULTRA_STRING_MAX_LEN;
1233 dump.dumpit = tipc_nl_node_dump_link;
1234 dump.format = tipc_nl_compat_link_dump;
1235 return tipc_nl_compat_dumpit(&dump, msg);
1236 case TIPC_CMD_SET_LINK_TOL:
1237 case TIPC_CMD_SET_LINK_PRI:
1238 case TIPC_CMD_SET_LINK_WINDOW:
1239 msg->req_type = TIPC_TLV_LINK_CONFIG;
1240 doit.doit = tipc_nl_node_set_link;
1241 doit.transcode = tipc_nl_compat_link_set;
1242 return tipc_nl_compat_doit(&doit, msg);
1243 case TIPC_CMD_RESET_LINK_STATS:
1244 msg->req_type = TIPC_TLV_LINK_NAME;
1245 doit.doit = tipc_nl_node_reset_link_stats;
1246 doit.transcode = tipc_nl_compat_link_reset_stats;
1247 return tipc_nl_compat_doit(&doit, msg);
1248 case TIPC_CMD_SHOW_NAME_TABLE:
1249 msg->req_type = TIPC_TLV_NAME_TBL_QUERY;
1250 msg->rep_size = ULTRA_STRING_MAX_LEN;
1251 msg->rep_type = TIPC_TLV_ULTRA_STRING;
1252 dump.header = tipc_nl_compat_name_table_dump_header;
1253 dump.dumpit = tipc_nl_name_table_dump;
1254 dump.format = tipc_nl_compat_name_table_dump;
1255 return tipc_nl_compat_dumpit(&dump, msg);
1256 case TIPC_CMD_SHOW_PORTS:
1257 msg->rep_size = ULTRA_STRING_MAX_LEN;
1258 msg->rep_type = TIPC_TLV_ULTRA_STRING;
1259 dump.dumpit = tipc_nl_sk_dump;
1260 dump.format = tipc_nl_compat_sk_dump;
1261 return tipc_nl_compat_dumpit(&dump, msg);
1262 case TIPC_CMD_GET_MEDIA_NAMES:
1263 msg->rep_size = MAX_MEDIA * TLV_SPACE(TIPC_MAX_MEDIA_NAME);
1264 dump.dumpit = tipc_nl_media_dump;
1265 dump.format = tipc_nl_compat_media_dump;
1266 return tipc_nl_compat_dumpit(&dump, msg);
1267 case TIPC_CMD_GET_NODES:
1268 msg->rep_size = ULTRA_STRING_MAX_LEN;
1269 dump.dumpit = tipc_nl_node_dump;
1270 dump.format = tipc_nl_compat_node_dump;
1271 return tipc_nl_compat_dumpit(&dump, msg);
1272 case TIPC_CMD_SET_NODE_ADDR:
1273 msg->req_type = TIPC_TLV_NET_ADDR;
1274 doit.doit = __tipc_nl_net_set;
1275 doit.transcode = tipc_nl_compat_net_set;
1276 return tipc_nl_compat_doit(&doit, msg);
1277 case TIPC_CMD_SET_NETID:
1278 msg->req_type = TIPC_TLV_UNSIGNED;
1279 doit.doit = __tipc_nl_net_set;
1280 doit.transcode = tipc_nl_compat_net_set;
1281 return tipc_nl_compat_doit(&doit, msg);
1282 case TIPC_CMD_GET_NETID:
1283 msg->rep_size = sizeof(u32);
1284 dump.dumpit = tipc_nl_net_dump;
1285 dump.format = tipc_nl_compat_net_dump;
1286 return tipc_nl_compat_dumpit(&dump, msg);
1287 case TIPC_CMD_SHOW_STATS:
1288 return tipc_cmd_show_stats_compat(msg);
1289 }
1290
1291 return -EOPNOTSUPP;
1292 }
1293
1294 static int tipc_nl_compat_recv(struct sk_buff *skb, struct genl_info *info)
1295 {
1296 int err;
1297 int len;
1298 struct tipc_nl_compat_msg msg;
1299 struct nlmsghdr *req_nlh;
1300 struct nlmsghdr *rep_nlh;
1301 struct tipc_genlmsghdr *req_userhdr = info->userhdr;
1302
1303 memset(&msg, 0, sizeof(msg));
1304
1305 req_nlh = (struct nlmsghdr *)skb->data;
1306 msg.req = nlmsg_data(req_nlh) + GENL_HDRLEN + TIPC_GENL_HDRLEN;
1307 msg.cmd = req_userhdr->cmd;
1308 msg.net = genl_info_net(info);
1309 msg.dst_sk = skb->sk;
1310
1311 if ((msg.cmd & 0xC000) && (!netlink_net_capable(skb, CAP_NET_ADMIN))) {
1312 msg.rep = tipc_get_err_tlv(TIPC_CFG_NOT_NET_ADMIN);
1313 err = -EACCES;
1314 goto send;
1315 }
1316
1317 msg.req_size = nlmsg_attrlen(req_nlh, GENL_HDRLEN + TIPC_GENL_HDRLEN);
1318 if (msg.req_size && !TLV_OK(msg.req, msg.req_size)) {
1319 msg.rep = tipc_get_err_tlv(TIPC_CFG_NOT_SUPPORTED);
1320 err = -EOPNOTSUPP;
1321 goto send;
1322 }
1323
1324 err = tipc_nl_compat_handle(&msg);
1325 if ((err == -EOPNOTSUPP) || (err == -EPERM))
1326 msg.rep = tipc_get_err_tlv(TIPC_CFG_NOT_SUPPORTED);
1327 else if (err == -EINVAL)
1328 msg.rep = tipc_get_err_tlv(TIPC_CFG_TLV_ERROR);
1329 send:
1330 if (!msg.rep)
1331 return err;
1332
1333 len = nlmsg_total_size(GENL_HDRLEN + TIPC_GENL_HDRLEN);
1334 skb_push(msg.rep, len);
1335 rep_nlh = nlmsg_hdr(msg.rep);
1336 memcpy(rep_nlh, info->nlhdr, len);
1337 rep_nlh->nlmsg_len = msg.rep->len;
1338 genlmsg_unicast(msg.net, msg.rep, NETLINK_CB(skb).portid);
1339
1340 return err;
1341 }
1342
1343 static const struct genl_small_ops tipc_genl_compat_ops[] = {
1344 {
1345 .cmd = TIPC_GENL_CMD,
1346 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
1347 .doit = tipc_nl_compat_recv,
1348 },
1349 };
1350
1351 static struct genl_family tipc_genl_compat_family __ro_after_init = {
1352 .name = TIPC_GENL_NAME,
1353 .version = TIPC_GENL_VERSION,
1354 .hdrsize = TIPC_GENL_HDRLEN,
1355 .maxattr = 0,
1356 .netnsok = true,
1357 .module = THIS_MODULE,
1358 .small_ops = tipc_genl_compat_ops,
1359 .n_small_ops = ARRAY_SIZE(tipc_genl_compat_ops),
1360 };
1361
1362 int __init tipc_netlink_compat_start(void)
1363 {
1364 int res;
1365
1366 res = genl_register_family(&tipc_genl_compat_family);
1367 if (res) {
1368 pr_err("Failed to register legacy compat interface\n");
1369 return res;
1370 }
1371
1372 return 0;
1373 }
1374
1375 void tipc_netlink_compat_stop(void)
1376 {
1377 genl_unregister_family(&tipc_genl_compat_family);
1378 }