1bfb3e5ddSRichard Alpe /* 2bfb3e5ddSRichard Alpe * Copyright (c) 2014, Ericsson AB 3bfb3e5ddSRichard Alpe * All rights reserved. 4bfb3e5ddSRichard Alpe * 5bfb3e5ddSRichard Alpe * Redistribution and use in source and binary forms, with or without 6bfb3e5ddSRichard Alpe * modification, are permitted provided that the following conditions are met: 7bfb3e5ddSRichard Alpe * 8bfb3e5ddSRichard Alpe * 1. Redistributions of source code must retain the above copyright 9bfb3e5ddSRichard Alpe * notice, this list of conditions and the following disclaimer. 10bfb3e5ddSRichard Alpe * 2. Redistributions in binary form must reproduce the above copyright 11bfb3e5ddSRichard Alpe * notice, this list of conditions and the following disclaimer in the 12bfb3e5ddSRichard Alpe * documentation and/or other materials provided with the distribution. 13bfb3e5ddSRichard Alpe * 3. Neither the names of the copyright holders nor the names of its 14bfb3e5ddSRichard Alpe * contributors may be used to endorse or promote products derived from 15bfb3e5ddSRichard Alpe * this software without specific prior written permission. 16bfb3e5ddSRichard Alpe * 17bfb3e5ddSRichard Alpe * Alternatively, this software may be distributed under the terms of the 18bfb3e5ddSRichard Alpe * GNU General Public License ("GPL") version 2 as published by the Free 19bfb3e5ddSRichard Alpe * Software Foundation. 20bfb3e5ddSRichard Alpe * 21bfb3e5ddSRichard Alpe * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22bfb3e5ddSRichard Alpe * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23bfb3e5ddSRichard Alpe * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24bfb3e5ddSRichard Alpe * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 25bfb3e5ddSRichard Alpe * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26bfb3e5ddSRichard Alpe * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27bfb3e5ddSRichard Alpe * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28bfb3e5ddSRichard Alpe * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29bfb3e5ddSRichard Alpe * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30bfb3e5ddSRichard Alpe * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31bfb3e5ddSRichard Alpe * POSSIBILITY OF SUCH DAMAGE. 32bfb3e5ddSRichard Alpe */ 33bfb3e5ddSRichard Alpe 34bfb3e5ddSRichard Alpe #include "core.h" 35d0796d1eSRichard Alpe #include "bearer.h" 36f2b3b2d4SRichard Alpe #include "link.h" 3744a8ae94SRichard Alpe #include "name_table.h" 38487d2a3aSRichard Alpe #include "socket.h" 394b28cb58SRichard Alpe #include "node.h" 40964f9501SRichard Alpe #include "net.h" 41bfb3e5ddSRichard Alpe #include <net/genetlink.h> 42bfb3e5ddSRichard Alpe #include <linux/tipc_config.h> 43bfb3e5ddSRichard Alpe 44d0796d1eSRichard Alpe /* The legacy API had an artificial message length limit called 45d0796d1eSRichard Alpe * ULTRA_STRING_MAX_LEN. 46d0796d1eSRichard Alpe */ 47d0796d1eSRichard Alpe #define ULTRA_STRING_MAX_LEN 32768 48d0796d1eSRichard Alpe 49d0796d1eSRichard Alpe #define TIPC_SKB_MAX TLV_SPACE(ULTRA_STRING_MAX_LEN) 50d0796d1eSRichard Alpe 51d0796d1eSRichard Alpe #define REPLY_TRUNCATED "<truncated>\n" 52d0796d1eSRichard Alpe 53d0796d1eSRichard Alpe struct tipc_nl_compat_msg { 54d0796d1eSRichard Alpe u16 cmd; 55f2b3b2d4SRichard Alpe int rep_type; 56d0796d1eSRichard Alpe int rep_size; 579ab15465SRichard Alpe int req_type; 58c3d6fb85SRichard Alpe struct net *net; 59d0796d1eSRichard Alpe struct sk_buff *rep; 60d0796d1eSRichard Alpe struct tlv_desc *req; 61d0796d1eSRichard Alpe struct sock *dst_sk; 62d0796d1eSRichard Alpe }; 63d0796d1eSRichard Alpe 64d0796d1eSRichard Alpe struct tipc_nl_compat_cmd_dump { 6544a8ae94SRichard Alpe int (*header)(struct tipc_nl_compat_msg *); 66d0796d1eSRichard Alpe int (*dumpit)(struct sk_buff *, struct netlink_callback *); 67d0796d1eSRichard Alpe int (*format)(struct tipc_nl_compat_msg *msg, struct nlattr **attrs); 68d0796d1eSRichard Alpe }; 69d0796d1eSRichard Alpe 709ab15465SRichard Alpe struct tipc_nl_compat_cmd_doit { 719ab15465SRichard Alpe int (*doit)(struct sk_buff *skb, struct genl_info *info); 72c3d6fb85SRichard Alpe int (*transcode)(struct tipc_nl_compat_cmd_doit *cmd, 73c3d6fb85SRichard Alpe struct sk_buff *skb, struct tipc_nl_compat_msg *msg); 749ab15465SRichard Alpe }; 759ab15465SRichard Alpe 76d0796d1eSRichard Alpe static int tipc_skb_tailroom(struct sk_buff *skb) 77d0796d1eSRichard Alpe { 78d0796d1eSRichard Alpe int tailroom; 79d0796d1eSRichard Alpe int limit; 80d0796d1eSRichard Alpe 81d0796d1eSRichard Alpe tailroom = skb_tailroom(skb); 82d0796d1eSRichard Alpe limit = TIPC_SKB_MAX - skb->len; 83d0796d1eSRichard Alpe 84d0796d1eSRichard Alpe if (tailroom < limit) 85d0796d1eSRichard Alpe return tailroom; 86d0796d1eSRichard Alpe 87d0796d1eSRichard Alpe return limit; 88d0796d1eSRichard Alpe } 89d0796d1eSRichard Alpe 908b66fee7SYing Xue static inline int TLV_GET_DATA_LEN(struct tlv_desc *tlv) 918b66fee7SYing Xue { 928b66fee7SYing Xue return TLV_GET_LEN(tlv) - TLV_SPACE(0); 938b66fee7SYing Xue } 948b66fee7SYing Xue 95d0796d1eSRichard Alpe static int tipc_add_tlv(struct sk_buff *skb, u16 type, void *data, u16 len) 96d0796d1eSRichard Alpe { 97d0796d1eSRichard Alpe struct tlv_desc *tlv = (struct tlv_desc *)skb_tail_pointer(skb); 98d0796d1eSRichard Alpe 99d0796d1eSRichard Alpe if (tipc_skb_tailroom(skb) < TLV_SPACE(len)) 100d0796d1eSRichard Alpe return -EMSGSIZE; 101d0796d1eSRichard Alpe 102d0796d1eSRichard Alpe skb_put(skb, TLV_SPACE(len)); 103d0796d1eSRichard Alpe tlv->tlv_type = htons(type); 104d0796d1eSRichard Alpe tlv->tlv_len = htons(TLV_LENGTH(len)); 105d0796d1eSRichard Alpe if (len && data) 106d0796d1eSRichard Alpe memcpy(TLV_DATA(tlv), data, len); 107d0796d1eSRichard Alpe 108d0796d1eSRichard Alpe return 0; 109d0796d1eSRichard Alpe } 110d0796d1eSRichard Alpe 111f2b3b2d4SRichard Alpe static void tipc_tlv_init(struct sk_buff *skb, u16 type) 112f2b3b2d4SRichard Alpe { 113f2b3b2d4SRichard Alpe struct tlv_desc *tlv = (struct tlv_desc *)skb->data; 114f2b3b2d4SRichard Alpe 115f2b3b2d4SRichard Alpe TLV_SET_LEN(tlv, 0); 116f2b3b2d4SRichard Alpe TLV_SET_TYPE(tlv, type); 117f2b3b2d4SRichard Alpe skb_put(skb, sizeof(struct tlv_desc)); 118f2b3b2d4SRichard Alpe } 119f2b3b2d4SRichard Alpe 120f2b3b2d4SRichard Alpe static int tipc_tlv_sprintf(struct sk_buff *skb, const char *fmt, ...) 121f2b3b2d4SRichard Alpe { 122f2b3b2d4SRichard Alpe int n; 123f2b3b2d4SRichard Alpe u16 len; 124f2b3b2d4SRichard Alpe u32 rem; 125f2b3b2d4SRichard Alpe char *buf; 126f2b3b2d4SRichard Alpe struct tlv_desc *tlv; 127f2b3b2d4SRichard Alpe va_list args; 128f2b3b2d4SRichard Alpe 129f2b3b2d4SRichard Alpe rem = tipc_skb_tailroom(skb); 130f2b3b2d4SRichard Alpe 131f2b3b2d4SRichard Alpe tlv = (struct tlv_desc *)skb->data; 132f2b3b2d4SRichard Alpe len = TLV_GET_LEN(tlv); 133f2b3b2d4SRichard Alpe buf = TLV_DATA(tlv) + len; 134f2b3b2d4SRichard Alpe 135f2b3b2d4SRichard Alpe va_start(args, fmt); 136f2b3b2d4SRichard Alpe n = vscnprintf(buf, rem, fmt, args); 137f2b3b2d4SRichard Alpe va_end(args); 138f2b3b2d4SRichard Alpe 139f2b3b2d4SRichard Alpe TLV_SET_LEN(tlv, n + len); 140f2b3b2d4SRichard Alpe skb_put(skb, n); 141f2b3b2d4SRichard Alpe 142f2b3b2d4SRichard Alpe return n; 143f2b3b2d4SRichard Alpe } 144f2b3b2d4SRichard Alpe 145d0796d1eSRichard Alpe static struct sk_buff *tipc_tlv_alloc(int size) 146d0796d1eSRichard Alpe { 147d0796d1eSRichard Alpe int hdr_len; 148d0796d1eSRichard Alpe struct sk_buff *buf; 149d0796d1eSRichard Alpe 150d0796d1eSRichard Alpe size = TLV_SPACE(size); 151d0796d1eSRichard Alpe hdr_len = nlmsg_total_size(GENL_HDRLEN + TIPC_GENL_HDRLEN); 152d0796d1eSRichard Alpe 153d0796d1eSRichard Alpe buf = alloc_skb(hdr_len + size, GFP_KERNEL); 154d0796d1eSRichard Alpe if (!buf) 155d0796d1eSRichard Alpe return NULL; 156d0796d1eSRichard Alpe 157d0796d1eSRichard Alpe skb_reserve(buf, hdr_len); 158d0796d1eSRichard Alpe 159d0796d1eSRichard Alpe return buf; 160d0796d1eSRichard Alpe } 161d0796d1eSRichard Alpe 162d0796d1eSRichard Alpe static struct sk_buff *tipc_get_err_tlv(char *str) 163d0796d1eSRichard Alpe { 164d0796d1eSRichard Alpe int str_len = strlen(str) + 1; 165d0796d1eSRichard Alpe struct sk_buff *buf; 166d0796d1eSRichard Alpe 167d0796d1eSRichard Alpe buf = tipc_tlv_alloc(TLV_SPACE(str_len)); 168d0796d1eSRichard Alpe if (buf) 169d0796d1eSRichard Alpe tipc_add_tlv(buf, TIPC_TLV_ERROR_STRING, str, str_len); 170d0796d1eSRichard Alpe 171d0796d1eSRichard Alpe return buf; 172d0796d1eSRichard Alpe } 173d0796d1eSRichard Alpe 1748b66fee7SYing Xue static inline bool string_is_valid(char *s, int len) 1758b66fee7SYing Xue { 1768b66fee7SYing Xue return memchr(s, '\0', len) ? true : false; 1778b66fee7SYing Xue } 1788b66fee7SYing Xue 179d0796d1eSRichard Alpe static int __tipc_nl_compat_dumpit(struct tipc_nl_compat_cmd_dump *cmd, 180d0796d1eSRichard Alpe struct tipc_nl_compat_msg *msg, 181d0796d1eSRichard Alpe struct sk_buff *arg) 182d0796d1eSRichard Alpe { 183d0796d1eSRichard Alpe int len = 0; 184d0796d1eSRichard Alpe int err; 185d0796d1eSRichard Alpe struct sk_buff *buf; 186d0796d1eSRichard Alpe struct nlmsghdr *nlmsg; 187d0796d1eSRichard Alpe struct netlink_callback cb; 188d0796d1eSRichard Alpe 189d0796d1eSRichard Alpe memset(&cb, 0, sizeof(cb)); 190d0796d1eSRichard Alpe cb.nlh = (struct nlmsghdr *)arg->data; 191d0796d1eSRichard Alpe cb.skb = arg; 192d0796d1eSRichard Alpe 193d0796d1eSRichard Alpe buf = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); 194d0796d1eSRichard Alpe if (!buf) 195d0796d1eSRichard Alpe return -ENOMEM; 196d0796d1eSRichard Alpe 197d0796d1eSRichard Alpe buf->sk = msg->dst_sk; 19812a78b02SCong Wang if (__tipc_dump_start(&cb, msg->net)) { 19912a78b02SCong Wang kfree_skb(buf); 20012a78b02SCong Wang return -ENOMEM; 20112a78b02SCong Wang } 202d0796d1eSRichard Alpe 203d0796d1eSRichard Alpe do { 204d0796d1eSRichard Alpe int rem; 205d0796d1eSRichard Alpe 206d0796d1eSRichard Alpe len = (*cmd->dumpit)(buf, &cb); 207d0796d1eSRichard Alpe 208d0796d1eSRichard Alpe nlmsg_for_each_msg(nlmsg, nlmsg_hdr(buf), len, rem) { 209d0796d1eSRichard Alpe struct nlattr **attrs; 210d0796d1eSRichard Alpe 211d0796d1eSRichard Alpe err = tipc_nlmsg_parse(nlmsg, &attrs); 212d0796d1eSRichard Alpe if (err) 213d0796d1eSRichard Alpe goto err_out; 214d0796d1eSRichard Alpe 215d0796d1eSRichard Alpe err = (*cmd->format)(msg, attrs); 216d0796d1eSRichard Alpe if (err) 217d0796d1eSRichard Alpe goto err_out; 218d0796d1eSRichard Alpe 219d0796d1eSRichard Alpe if (tipc_skb_tailroom(msg->rep) <= 1) { 220d0796d1eSRichard Alpe err = -EMSGSIZE; 221d0796d1eSRichard Alpe goto err_out; 222d0796d1eSRichard Alpe } 223d0796d1eSRichard Alpe } 224d0796d1eSRichard Alpe 225d0796d1eSRichard Alpe skb_reset_tail_pointer(buf); 226d0796d1eSRichard Alpe buf->len = 0; 227d0796d1eSRichard Alpe 228d0796d1eSRichard Alpe } while (len); 229d0796d1eSRichard Alpe 230d0796d1eSRichard Alpe err = 0; 231d0796d1eSRichard Alpe 232d0796d1eSRichard Alpe err_out: 2338f5c5fcfSCong Wang tipc_dump_done(&cb); 234d0796d1eSRichard Alpe kfree_skb(buf); 235d0796d1eSRichard Alpe 236d0796d1eSRichard Alpe if (err == -EMSGSIZE) { 237d0796d1eSRichard Alpe /* The legacy API only considered messages filling 238d0796d1eSRichard Alpe * "ULTRA_STRING_MAX_LEN" to be truncated. 239d0796d1eSRichard Alpe */ 240d0796d1eSRichard Alpe if ((TIPC_SKB_MAX - msg->rep->len) <= 1) { 241d0796d1eSRichard Alpe char *tail = skb_tail_pointer(msg->rep); 242d0796d1eSRichard Alpe 243d0796d1eSRichard Alpe if (*tail != '\0') 244d0796d1eSRichard Alpe sprintf(tail - sizeof(REPLY_TRUNCATED) - 1, 245d0796d1eSRichard Alpe REPLY_TRUNCATED); 246d0796d1eSRichard Alpe } 247d0796d1eSRichard Alpe 248d0796d1eSRichard Alpe return 0; 249d0796d1eSRichard Alpe } 250d0796d1eSRichard Alpe 251d0796d1eSRichard Alpe return err; 252d0796d1eSRichard Alpe } 253d0796d1eSRichard Alpe 254d0796d1eSRichard Alpe static int tipc_nl_compat_dumpit(struct tipc_nl_compat_cmd_dump *cmd, 255d0796d1eSRichard Alpe struct tipc_nl_compat_msg *msg) 256d0796d1eSRichard Alpe { 257d0796d1eSRichard Alpe int err; 258d0796d1eSRichard Alpe struct sk_buff *arg; 259d0796d1eSRichard Alpe 260f2b3b2d4SRichard Alpe if (msg->req_type && !TLV_CHECK_TYPE(msg->req, msg->req_type)) 261f2b3b2d4SRichard Alpe return -EINVAL; 262f2b3b2d4SRichard Alpe 263d0796d1eSRichard Alpe msg->rep = tipc_tlv_alloc(msg->rep_size); 264d0796d1eSRichard Alpe if (!msg->rep) 265d0796d1eSRichard Alpe return -ENOMEM; 266d0796d1eSRichard Alpe 267f2b3b2d4SRichard Alpe if (msg->rep_type) 268f2b3b2d4SRichard Alpe tipc_tlv_init(msg->rep, msg->rep_type); 269f2b3b2d4SRichard Alpe 270*2ac695d1SXin Long if (cmd->header) { 271*2ac695d1SXin Long err = (*cmd->header)(msg); 272*2ac695d1SXin Long if (err) { 273*2ac695d1SXin Long kfree_skb(msg->rep); 274*2ac695d1SXin Long msg->rep = NULL; 275*2ac695d1SXin Long return err; 276*2ac695d1SXin Long } 277*2ac695d1SXin Long } 27844a8ae94SRichard Alpe 279d0796d1eSRichard Alpe arg = nlmsg_new(0, GFP_KERNEL); 280d0796d1eSRichard Alpe if (!arg) { 281d0796d1eSRichard Alpe kfree_skb(msg->rep); 2825bfd37b4SEric Dumazet msg->rep = NULL; 283d0796d1eSRichard Alpe return -ENOMEM; 284d0796d1eSRichard Alpe } 285d0796d1eSRichard Alpe 286d0796d1eSRichard Alpe err = __tipc_nl_compat_dumpit(cmd, msg, arg); 2875bfd37b4SEric Dumazet if (err) { 288d0796d1eSRichard Alpe kfree_skb(msg->rep); 2895bfd37b4SEric Dumazet msg->rep = NULL; 2905bfd37b4SEric Dumazet } 291d0796d1eSRichard Alpe kfree_skb(arg); 292d0796d1eSRichard Alpe 293d0796d1eSRichard Alpe return err; 294d0796d1eSRichard Alpe } 295d0796d1eSRichard Alpe 2969ab15465SRichard Alpe static int __tipc_nl_compat_doit(struct tipc_nl_compat_cmd_doit *cmd, 2979ab15465SRichard Alpe struct tipc_nl_compat_msg *msg) 2989ab15465SRichard Alpe { 2999ab15465SRichard Alpe int err; 3009ab15465SRichard Alpe struct sk_buff *doit_buf; 3019ab15465SRichard Alpe struct sk_buff *trans_buf; 3029ab15465SRichard Alpe struct nlattr **attrbuf; 3039ab15465SRichard Alpe struct genl_info info; 3049ab15465SRichard Alpe 3059ab15465SRichard Alpe trans_buf = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 3069ab15465SRichard Alpe if (!trans_buf) 3079ab15465SRichard Alpe return -ENOMEM; 3089ab15465SRichard Alpe 3096da2ec56SKees Cook attrbuf = kmalloc_array(tipc_genl_family.maxattr + 1, 3106da2ec56SKees Cook sizeof(struct nlattr *), 3116da2ec56SKees Cook GFP_KERNEL); 3129ab15465SRichard Alpe if (!attrbuf) { 3139ab15465SRichard Alpe err = -ENOMEM; 3149ab15465SRichard Alpe goto trans_out; 3159ab15465SRichard Alpe } 3169ab15465SRichard Alpe 3179ab15465SRichard Alpe doit_buf = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 3189ab15465SRichard Alpe if (!doit_buf) { 3199ab15465SRichard Alpe err = -ENOMEM; 320e5d1a1eeSYing Xue goto attrbuf_out; 3219ab15465SRichard Alpe } 3229ab15465SRichard Alpe 3239ab15465SRichard Alpe memset(&info, 0, sizeof(info)); 3249ab15465SRichard Alpe info.attrs = attrbuf; 3259ab15465SRichard Alpe 326ed4ffdfeSYing Xue rtnl_lock(); 327e5d1a1eeSYing Xue err = (*cmd->transcode)(cmd, trans_buf, msg); 328e5d1a1eeSYing Xue if (err) 329e5d1a1eeSYing Xue goto doit_out; 330e5d1a1eeSYing Xue 331e5d1a1eeSYing Xue err = nla_parse(attrbuf, tipc_genl_family.maxattr, 332e5d1a1eeSYing Xue (const struct nlattr *)trans_buf->data, 333e5d1a1eeSYing Xue trans_buf->len, NULL, NULL); 334e5d1a1eeSYing Xue if (err) 335e5d1a1eeSYing Xue goto doit_out; 336e5d1a1eeSYing Xue 337e5d1a1eeSYing Xue doit_buf->sk = msg->dst_sk; 338e5d1a1eeSYing Xue 3399ab15465SRichard Alpe err = (*cmd->doit)(doit_buf, &info); 340e5d1a1eeSYing Xue doit_out: 341ed4ffdfeSYing Xue rtnl_unlock(); 3429ab15465SRichard Alpe 3439ab15465SRichard Alpe kfree_skb(doit_buf); 344e5d1a1eeSYing Xue attrbuf_out: 3459ab15465SRichard Alpe kfree(attrbuf); 3469ab15465SRichard Alpe trans_out: 3479ab15465SRichard Alpe kfree_skb(trans_buf); 3489ab15465SRichard Alpe 3499ab15465SRichard Alpe return err; 3509ab15465SRichard Alpe } 3519ab15465SRichard Alpe 3529ab15465SRichard Alpe static int tipc_nl_compat_doit(struct tipc_nl_compat_cmd_doit *cmd, 3539ab15465SRichard Alpe struct tipc_nl_compat_msg *msg) 3549ab15465SRichard Alpe { 3559ab15465SRichard Alpe int err; 3569ab15465SRichard Alpe 3579ab15465SRichard Alpe if (msg->req_type && !TLV_CHECK_TYPE(msg->req, msg->req_type)) 3589ab15465SRichard Alpe return -EINVAL; 3599ab15465SRichard Alpe 3609ab15465SRichard Alpe err = __tipc_nl_compat_doit(cmd, msg); 3619ab15465SRichard Alpe if (err) 3629ab15465SRichard Alpe return err; 3639ab15465SRichard Alpe 3649ab15465SRichard Alpe /* The legacy API considered an empty message a success message */ 3659ab15465SRichard Alpe msg->rep = tipc_tlv_alloc(0); 3669ab15465SRichard Alpe if (!msg->rep) 3679ab15465SRichard Alpe return -ENOMEM; 3689ab15465SRichard Alpe 3699ab15465SRichard Alpe return 0; 3709ab15465SRichard Alpe } 3719ab15465SRichard Alpe 372d0796d1eSRichard Alpe static int tipc_nl_compat_bearer_dump(struct tipc_nl_compat_msg *msg, 373d0796d1eSRichard Alpe struct nlattr **attrs) 374d0796d1eSRichard Alpe { 375d0796d1eSRichard Alpe struct nlattr *bearer[TIPC_NLA_BEARER_MAX + 1]; 376297f7d2cSBaozeng Ding int err; 377d0796d1eSRichard Alpe 378297f7d2cSBaozeng Ding if (!attrs[TIPC_NLA_BEARER]) 379297f7d2cSBaozeng Ding return -EINVAL; 380297f7d2cSBaozeng Ding 381297f7d2cSBaozeng Ding err = nla_parse_nested(bearer, TIPC_NLA_BEARER_MAX, 382fceb6435SJohannes Berg attrs[TIPC_NLA_BEARER], NULL, NULL); 383297f7d2cSBaozeng Ding if (err) 384297f7d2cSBaozeng Ding return err; 385d0796d1eSRichard Alpe 386d0796d1eSRichard Alpe return tipc_add_tlv(msg->rep, TIPC_TLV_BEARER_NAME, 387d0796d1eSRichard Alpe nla_data(bearer[TIPC_NLA_BEARER_NAME]), 388d0796d1eSRichard Alpe nla_len(bearer[TIPC_NLA_BEARER_NAME])); 389d0796d1eSRichard Alpe } 390d0796d1eSRichard Alpe 391c3d6fb85SRichard Alpe static int tipc_nl_compat_bearer_enable(struct tipc_nl_compat_cmd_doit *cmd, 392c3d6fb85SRichard Alpe struct sk_buff *skb, 3939ab15465SRichard Alpe struct tipc_nl_compat_msg *msg) 3949ab15465SRichard Alpe { 3959ab15465SRichard Alpe struct nlattr *prop; 3969ab15465SRichard Alpe struct nlattr *bearer; 3979ab15465SRichard Alpe struct tipc_bearer_config *b; 3980762216cSYing Xue int len; 3999ab15465SRichard Alpe 4009ab15465SRichard Alpe b = (struct tipc_bearer_config *)TLV_DATA(msg->req); 4019ab15465SRichard Alpe 4029ab15465SRichard Alpe bearer = nla_nest_start(skb, TIPC_NLA_BEARER); 4039ab15465SRichard Alpe if (!bearer) 4049ab15465SRichard Alpe return -EMSGSIZE; 4059ab15465SRichard Alpe 4066f07e5f0SXin Long len = TLV_GET_DATA_LEN(msg->req); 4076f07e5f0SXin Long len -= offsetof(struct tipc_bearer_config, name); 4086f07e5f0SXin Long if (len <= 0) 4096f07e5f0SXin Long return -EINVAL; 4106f07e5f0SXin Long 4116f07e5f0SXin Long len = min_t(int, len, TIPC_MAX_BEARER_NAME); 4120762216cSYing Xue if (!string_is_valid(b->name, len)) 4130762216cSYing Xue return -EINVAL; 4140762216cSYing Xue 4159ab15465SRichard Alpe if (nla_put_string(skb, TIPC_NLA_BEARER_NAME, b->name)) 4169ab15465SRichard Alpe return -EMSGSIZE; 4179ab15465SRichard Alpe 4189ab15465SRichard Alpe if (nla_put_u32(skb, TIPC_NLA_BEARER_DOMAIN, ntohl(b->disc_domain))) 4199ab15465SRichard Alpe return -EMSGSIZE; 4209ab15465SRichard Alpe 4219ab15465SRichard Alpe if (ntohl(b->priority) <= TIPC_MAX_LINK_PRI) { 4229ab15465SRichard Alpe prop = nla_nest_start(skb, TIPC_NLA_BEARER_PROP); 4239ab15465SRichard Alpe if (!prop) 4249ab15465SRichard Alpe return -EMSGSIZE; 4259ab15465SRichard Alpe if (nla_put_u32(skb, TIPC_NLA_PROP_PRIO, ntohl(b->priority))) 4269ab15465SRichard Alpe return -EMSGSIZE; 4279ab15465SRichard Alpe nla_nest_end(skb, prop); 4289ab15465SRichard Alpe } 4299ab15465SRichard Alpe nla_nest_end(skb, bearer); 4309ab15465SRichard Alpe 4319ab15465SRichard Alpe return 0; 4329ab15465SRichard Alpe } 4339ab15465SRichard Alpe 434c3d6fb85SRichard Alpe static int tipc_nl_compat_bearer_disable(struct tipc_nl_compat_cmd_doit *cmd, 435c3d6fb85SRichard Alpe struct sk_buff *skb, 4369ab15465SRichard Alpe struct tipc_nl_compat_msg *msg) 4379ab15465SRichard Alpe { 4389ab15465SRichard Alpe char *name; 4399ab15465SRichard Alpe struct nlattr *bearer; 4400762216cSYing Xue int len; 4419ab15465SRichard Alpe 4429ab15465SRichard Alpe name = (char *)TLV_DATA(msg->req); 4439ab15465SRichard Alpe 4449ab15465SRichard Alpe bearer = nla_nest_start(skb, TIPC_NLA_BEARER); 4459ab15465SRichard Alpe if (!bearer) 4469ab15465SRichard Alpe return -EMSGSIZE; 4479ab15465SRichard Alpe 4480762216cSYing Xue len = min_t(int, TLV_GET_DATA_LEN(msg->req), TIPC_MAX_BEARER_NAME); 4490762216cSYing Xue if (!string_is_valid(name, len)) 4500762216cSYing Xue return -EINVAL; 4510762216cSYing Xue 4529ab15465SRichard Alpe if (nla_put_string(skb, TIPC_NLA_BEARER_NAME, name)) 4539ab15465SRichard Alpe return -EMSGSIZE; 4549ab15465SRichard Alpe 4559ab15465SRichard Alpe nla_nest_end(skb, bearer); 4569ab15465SRichard Alpe 4579ab15465SRichard Alpe return 0; 4589ab15465SRichard Alpe } 4599ab15465SRichard Alpe 460f2b3b2d4SRichard Alpe static inline u32 perc(u32 count, u32 total) 461f2b3b2d4SRichard Alpe { 462f2b3b2d4SRichard Alpe return (count * 100 + (total / 2)) / total; 463f2b3b2d4SRichard Alpe } 464f2b3b2d4SRichard Alpe 465f2b3b2d4SRichard Alpe static void __fill_bc_link_stat(struct tipc_nl_compat_msg *msg, 466f2b3b2d4SRichard Alpe struct nlattr *prop[], struct nlattr *stats[]) 467f2b3b2d4SRichard Alpe { 468f2b3b2d4SRichard Alpe tipc_tlv_sprintf(msg->rep, " Window:%u packets\n", 469f2b3b2d4SRichard Alpe nla_get_u32(prop[TIPC_NLA_PROP_WIN])); 470f2b3b2d4SRichard Alpe 471f2b3b2d4SRichard Alpe tipc_tlv_sprintf(msg->rep, 472f2b3b2d4SRichard Alpe " RX packets:%u fragments:%u/%u bundles:%u/%u\n", 473f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_RX_INFO]), 474f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_RX_FRAGMENTS]), 475f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_RX_FRAGMENTED]), 476f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_RX_BUNDLES]), 477f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_RX_BUNDLED])); 478f2b3b2d4SRichard Alpe 479f2b3b2d4SRichard Alpe tipc_tlv_sprintf(msg->rep, 480f2b3b2d4SRichard Alpe " TX packets:%u fragments:%u/%u bundles:%u/%u\n", 481f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_TX_INFO]), 482f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_TX_FRAGMENTS]), 483f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_TX_FRAGMENTED]), 484f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_TX_BUNDLES]), 485f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_TX_BUNDLED])); 486f2b3b2d4SRichard Alpe 487f2b3b2d4SRichard Alpe tipc_tlv_sprintf(msg->rep, " RX naks:%u defs:%u dups:%u\n", 488f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_RX_NACKS]), 489f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_RX_DEFERRED]), 490f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_DUPLICATES])); 491f2b3b2d4SRichard Alpe 492f2b3b2d4SRichard Alpe tipc_tlv_sprintf(msg->rep, " TX naks:%u acks:%u dups:%u\n", 493f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_TX_NACKS]), 494f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_TX_ACKS]), 495f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_RETRANSMITTED])); 496f2b3b2d4SRichard Alpe 497f2b3b2d4SRichard Alpe tipc_tlv_sprintf(msg->rep, 498f2b3b2d4SRichard Alpe " Congestion link:%u Send queue max:%u avg:%u", 499f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_LINK_CONGS]), 500f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_MAX_QUEUE]), 501f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_AVG_QUEUE])); 502f2b3b2d4SRichard Alpe } 503f2b3b2d4SRichard Alpe 504f2b3b2d4SRichard Alpe static int tipc_nl_compat_link_stat_dump(struct tipc_nl_compat_msg *msg, 505f2b3b2d4SRichard Alpe struct nlattr **attrs) 506f2b3b2d4SRichard Alpe { 507f2b3b2d4SRichard Alpe char *name; 508f2b3b2d4SRichard Alpe struct nlattr *link[TIPC_NLA_LINK_MAX + 1]; 509f2b3b2d4SRichard Alpe struct nlattr *prop[TIPC_NLA_PROP_MAX + 1]; 510f2b3b2d4SRichard Alpe struct nlattr *stats[TIPC_NLA_STATS_MAX + 1]; 511297f7d2cSBaozeng Ding int err; 5120762216cSYing Xue int len; 513f2b3b2d4SRichard Alpe 514297f7d2cSBaozeng Ding if (!attrs[TIPC_NLA_LINK]) 515297f7d2cSBaozeng Ding return -EINVAL; 516f2b3b2d4SRichard Alpe 517297f7d2cSBaozeng Ding err = nla_parse_nested(link, TIPC_NLA_LINK_MAX, attrs[TIPC_NLA_LINK], 518fceb6435SJohannes Berg NULL, NULL); 519297f7d2cSBaozeng Ding if (err) 520297f7d2cSBaozeng Ding return err; 521f2b3b2d4SRichard Alpe 522297f7d2cSBaozeng Ding if (!link[TIPC_NLA_LINK_PROP]) 523297f7d2cSBaozeng Ding return -EINVAL; 524297f7d2cSBaozeng Ding 525297f7d2cSBaozeng Ding err = nla_parse_nested(prop, TIPC_NLA_PROP_MAX, 526fceb6435SJohannes Berg link[TIPC_NLA_LINK_PROP], NULL, NULL); 527297f7d2cSBaozeng Ding if (err) 528297f7d2cSBaozeng Ding return err; 529297f7d2cSBaozeng Ding 530297f7d2cSBaozeng Ding if (!link[TIPC_NLA_LINK_STATS]) 531297f7d2cSBaozeng Ding return -EINVAL; 532297f7d2cSBaozeng Ding 533297f7d2cSBaozeng Ding err = nla_parse_nested(stats, TIPC_NLA_STATS_MAX, 534fceb6435SJohannes Berg link[TIPC_NLA_LINK_STATS], NULL, NULL); 535297f7d2cSBaozeng Ding if (err) 536297f7d2cSBaozeng Ding return err; 537f2b3b2d4SRichard Alpe 538f2b3b2d4SRichard Alpe name = (char *)TLV_DATA(msg->req); 5390762216cSYing Xue 5400762216cSYing Xue len = min_t(int, TLV_GET_DATA_LEN(msg->req), TIPC_MAX_LINK_NAME); 5410762216cSYing Xue if (!string_is_valid(name, len)) 5420762216cSYing Xue return -EINVAL; 5430762216cSYing Xue 544f2b3b2d4SRichard Alpe if (strcmp(name, nla_data(link[TIPC_NLA_LINK_NAME])) != 0) 545f2b3b2d4SRichard Alpe return 0; 546f2b3b2d4SRichard Alpe 547f2b3b2d4SRichard Alpe tipc_tlv_sprintf(msg->rep, "\nLink <%s>\n", 548f2b3b2d4SRichard Alpe nla_data(link[TIPC_NLA_LINK_NAME])); 549f2b3b2d4SRichard Alpe 550f2b3b2d4SRichard Alpe if (link[TIPC_NLA_LINK_BROADCAST]) { 551f2b3b2d4SRichard Alpe __fill_bc_link_stat(msg, prop, stats); 552f2b3b2d4SRichard Alpe return 0; 553f2b3b2d4SRichard Alpe } 554f2b3b2d4SRichard Alpe 555f2b3b2d4SRichard Alpe if (link[TIPC_NLA_LINK_ACTIVE]) 556f2b3b2d4SRichard Alpe tipc_tlv_sprintf(msg->rep, " ACTIVE"); 557f2b3b2d4SRichard Alpe else if (link[TIPC_NLA_LINK_UP]) 558f2b3b2d4SRichard Alpe tipc_tlv_sprintf(msg->rep, " STANDBY"); 559f2b3b2d4SRichard Alpe else 560f2b3b2d4SRichard Alpe tipc_tlv_sprintf(msg->rep, " DEFUNCT"); 561f2b3b2d4SRichard Alpe 562f2b3b2d4SRichard Alpe tipc_tlv_sprintf(msg->rep, " MTU:%u Priority:%u", 563f2b3b2d4SRichard Alpe nla_get_u32(link[TIPC_NLA_LINK_MTU]), 564f2b3b2d4SRichard Alpe nla_get_u32(prop[TIPC_NLA_PROP_PRIO])); 565f2b3b2d4SRichard Alpe 566f2b3b2d4SRichard Alpe tipc_tlv_sprintf(msg->rep, " Tolerance:%u ms Window:%u packets\n", 567f2b3b2d4SRichard Alpe nla_get_u32(prop[TIPC_NLA_PROP_TOL]), 568f2b3b2d4SRichard Alpe nla_get_u32(prop[TIPC_NLA_PROP_WIN])); 569f2b3b2d4SRichard Alpe 570f2b3b2d4SRichard Alpe tipc_tlv_sprintf(msg->rep, 571f2b3b2d4SRichard Alpe " RX packets:%u fragments:%u/%u bundles:%u/%u\n", 572f2b3b2d4SRichard Alpe nla_get_u32(link[TIPC_NLA_LINK_RX]) - 573f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_RX_INFO]), 574f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_RX_FRAGMENTS]), 575f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_RX_FRAGMENTED]), 576f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_RX_BUNDLES]), 577f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_RX_BUNDLED])); 578f2b3b2d4SRichard Alpe 579f2b3b2d4SRichard Alpe tipc_tlv_sprintf(msg->rep, 580f2b3b2d4SRichard Alpe " TX packets:%u fragments:%u/%u bundles:%u/%u\n", 581f2b3b2d4SRichard Alpe nla_get_u32(link[TIPC_NLA_LINK_TX]) - 582f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_TX_INFO]), 583f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_TX_FRAGMENTS]), 584f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_TX_FRAGMENTED]), 585f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_TX_BUNDLES]), 586f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_TX_BUNDLED])); 587f2b3b2d4SRichard Alpe 588f2b3b2d4SRichard Alpe tipc_tlv_sprintf(msg->rep, 589f2b3b2d4SRichard Alpe " TX profile sample:%u packets average:%u octets\n", 590f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_CNT]), 591f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_TOT]) / 592f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_MSG_PROF_TOT])); 593f2b3b2d4SRichard Alpe 594f2b3b2d4SRichard Alpe tipc_tlv_sprintf(msg->rep, 595f2b3b2d4SRichard Alpe " 0-64:%u%% -256:%u%% -1024:%u%% -4096:%u%% ", 596f2b3b2d4SRichard Alpe perc(nla_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P0]), 597f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_MSG_PROF_TOT])), 598f2b3b2d4SRichard Alpe perc(nla_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P1]), 599f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_MSG_PROF_TOT])), 600f2b3b2d4SRichard Alpe perc(nla_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P2]), 601f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_MSG_PROF_TOT])), 602f2b3b2d4SRichard Alpe perc(nla_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P3]), 603f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_MSG_PROF_TOT]))); 604f2b3b2d4SRichard Alpe 605f2b3b2d4SRichard Alpe tipc_tlv_sprintf(msg->rep, "-16384:%u%% -32768:%u%% -66000:%u%%\n", 606f2b3b2d4SRichard Alpe perc(nla_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P4]), 607f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_MSG_PROF_TOT])), 608f2b3b2d4SRichard Alpe perc(nla_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P5]), 609f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_MSG_PROF_TOT])), 610f2b3b2d4SRichard Alpe perc(nla_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P6]), 611f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_MSG_PROF_TOT]))); 612f2b3b2d4SRichard Alpe 613f2b3b2d4SRichard Alpe tipc_tlv_sprintf(msg->rep, 614f2b3b2d4SRichard Alpe " RX states:%u probes:%u naks:%u defs:%u dups:%u\n", 615f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_RX_STATES]), 616f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_RX_PROBES]), 617f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_RX_NACKS]), 618f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_RX_DEFERRED]), 619f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_DUPLICATES])); 620f2b3b2d4SRichard Alpe 621f2b3b2d4SRichard Alpe tipc_tlv_sprintf(msg->rep, 622f2b3b2d4SRichard Alpe " TX states:%u probes:%u naks:%u acks:%u dups:%u\n", 623f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_TX_STATES]), 624f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_TX_PROBES]), 625f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_TX_NACKS]), 626f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_TX_ACKS]), 627f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_RETRANSMITTED])); 628f2b3b2d4SRichard Alpe 629f2b3b2d4SRichard Alpe tipc_tlv_sprintf(msg->rep, 630f2b3b2d4SRichard Alpe " Congestion link:%u Send queue max:%u avg:%u", 631f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_LINK_CONGS]), 632f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_MAX_QUEUE]), 633f2b3b2d4SRichard Alpe nla_get_u32(stats[TIPC_NLA_STATS_AVG_QUEUE])); 634f2b3b2d4SRichard Alpe 635f2b3b2d4SRichard Alpe return 0; 636f2b3b2d4SRichard Alpe } 637f2b3b2d4SRichard Alpe 638357ebdbfSRichard Alpe static int tipc_nl_compat_link_dump(struct tipc_nl_compat_msg *msg, 639357ebdbfSRichard Alpe struct nlattr **attrs) 640357ebdbfSRichard Alpe { 641357ebdbfSRichard Alpe struct nlattr *link[TIPC_NLA_LINK_MAX + 1]; 642357ebdbfSRichard Alpe struct tipc_link_info link_info; 643297f7d2cSBaozeng Ding int err; 644357ebdbfSRichard Alpe 645297f7d2cSBaozeng Ding if (!attrs[TIPC_NLA_LINK]) 646297f7d2cSBaozeng Ding return -EINVAL; 647297f7d2cSBaozeng Ding 648297f7d2cSBaozeng Ding err = nla_parse_nested(link, TIPC_NLA_LINK_MAX, attrs[TIPC_NLA_LINK], 649fceb6435SJohannes Berg NULL, NULL); 650297f7d2cSBaozeng Ding if (err) 651297f7d2cSBaozeng Ding return err; 652357ebdbfSRichard Alpe 653357ebdbfSRichard Alpe link_info.dest = nla_get_flag(link[TIPC_NLA_LINK_DEST]); 654357ebdbfSRichard Alpe link_info.up = htonl(nla_get_flag(link[TIPC_NLA_LINK_UP])); 65555e77a3eSRichard Alpe nla_strlcpy(link_info.str, link[TIPC_NLA_LINK_NAME], 6565d2be142SKangjie Lu TIPC_MAX_LINK_NAME); 657357ebdbfSRichard Alpe 658357ebdbfSRichard Alpe return tipc_add_tlv(msg->rep, TIPC_TLV_LINK_INFO, 659357ebdbfSRichard Alpe &link_info, sizeof(link_info)); 660357ebdbfSRichard Alpe } 661357ebdbfSRichard Alpe 662c3d6fb85SRichard Alpe static int __tipc_add_link_prop(struct sk_buff *skb, 663c3d6fb85SRichard Alpe struct tipc_nl_compat_msg *msg, 664c3d6fb85SRichard Alpe struct tipc_link_config *lc) 665c3d6fb85SRichard Alpe { 666c3d6fb85SRichard Alpe switch (msg->cmd) { 667c3d6fb85SRichard Alpe case TIPC_CMD_SET_LINK_PRI: 668c3d6fb85SRichard Alpe return nla_put_u32(skb, TIPC_NLA_PROP_PRIO, ntohl(lc->value)); 669c3d6fb85SRichard Alpe case TIPC_CMD_SET_LINK_TOL: 670c3d6fb85SRichard Alpe return nla_put_u32(skb, TIPC_NLA_PROP_TOL, ntohl(lc->value)); 671c3d6fb85SRichard Alpe case TIPC_CMD_SET_LINK_WINDOW: 672c3d6fb85SRichard Alpe return nla_put_u32(skb, TIPC_NLA_PROP_WIN, ntohl(lc->value)); 673c3d6fb85SRichard Alpe } 674c3d6fb85SRichard Alpe 675c3d6fb85SRichard Alpe return -EINVAL; 676c3d6fb85SRichard Alpe } 677c3d6fb85SRichard Alpe 678c3d6fb85SRichard Alpe static int tipc_nl_compat_media_set(struct sk_buff *skb, 67937e2d484SRichard Alpe struct tipc_nl_compat_msg *msg) 68037e2d484SRichard Alpe { 68137e2d484SRichard Alpe struct nlattr *prop; 682c3d6fb85SRichard Alpe struct nlattr *media; 683c3d6fb85SRichard Alpe struct tipc_link_config *lc; 6840762216cSYing Xue int len; 685c3d6fb85SRichard Alpe 686c3d6fb85SRichard Alpe lc = (struct tipc_link_config *)TLV_DATA(msg->req); 687c3d6fb85SRichard Alpe 688c3d6fb85SRichard Alpe media = nla_nest_start(skb, TIPC_NLA_MEDIA); 689c3d6fb85SRichard Alpe if (!media) 690c3d6fb85SRichard Alpe return -EMSGSIZE; 691c3d6fb85SRichard Alpe 6920762216cSYing Xue len = min_t(int, TLV_GET_DATA_LEN(msg->req), TIPC_MAX_MEDIA_NAME); 6930762216cSYing Xue if (!string_is_valid(lc->name, len)) 6940762216cSYing Xue return -EINVAL; 6950762216cSYing Xue 696c3d6fb85SRichard Alpe if (nla_put_string(skb, TIPC_NLA_MEDIA_NAME, lc->name)) 697c3d6fb85SRichard Alpe return -EMSGSIZE; 698c3d6fb85SRichard Alpe 699c3d6fb85SRichard Alpe prop = nla_nest_start(skb, TIPC_NLA_MEDIA_PROP); 700c3d6fb85SRichard Alpe if (!prop) 701c3d6fb85SRichard Alpe return -EMSGSIZE; 702c3d6fb85SRichard Alpe 703c3d6fb85SRichard Alpe __tipc_add_link_prop(skb, msg, lc); 704c3d6fb85SRichard Alpe nla_nest_end(skb, prop); 705c3d6fb85SRichard Alpe nla_nest_end(skb, media); 706c3d6fb85SRichard Alpe 707c3d6fb85SRichard Alpe return 0; 708c3d6fb85SRichard Alpe } 709c3d6fb85SRichard Alpe 710c3d6fb85SRichard Alpe static int tipc_nl_compat_bearer_set(struct sk_buff *skb, 711c3d6fb85SRichard Alpe struct tipc_nl_compat_msg *msg) 712c3d6fb85SRichard Alpe { 713c3d6fb85SRichard Alpe struct nlattr *prop; 714c3d6fb85SRichard Alpe struct nlattr *bearer; 715c3d6fb85SRichard Alpe struct tipc_link_config *lc; 7160762216cSYing Xue int len; 717c3d6fb85SRichard Alpe 718c3d6fb85SRichard Alpe lc = (struct tipc_link_config *)TLV_DATA(msg->req); 719c3d6fb85SRichard Alpe 720c3d6fb85SRichard Alpe bearer = nla_nest_start(skb, TIPC_NLA_BEARER); 721c3d6fb85SRichard Alpe if (!bearer) 722c3d6fb85SRichard Alpe return -EMSGSIZE; 723c3d6fb85SRichard Alpe 7240762216cSYing Xue len = min_t(int, TLV_GET_DATA_LEN(msg->req), TIPC_MAX_MEDIA_NAME); 7250762216cSYing Xue if (!string_is_valid(lc->name, len)) 7260762216cSYing Xue return -EINVAL; 7270762216cSYing Xue 728c3d6fb85SRichard Alpe if (nla_put_string(skb, TIPC_NLA_BEARER_NAME, lc->name)) 729c3d6fb85SRichard Alpe return -EMSGSIZE; 730c3d6fb85SRichard Alpe 731c3d6fb85SRichard Alpe prop = nla_nest_start(skb, TIPC_NLA_BEARER_PROP); 732c3d6fb85SRichard Alpe if (!prop) 733c3d6fb85SRichard Alpe return -EMSGSIZE; 734c3d6fb85SRichard Alpe 735c3d6fb85SRichard Alpe __tipc_add_link_prop(skb, msg, lc); 736c3d6fb85SRichard Alpe nla_nest_end(skb, prop); 737c3d6fb85SRichard Alpe nla_nest_end(skb, bearer); 738c3d6fb85SRichard Alpe 739c3d6fb85SRichard Alpe return 0; 740c3d6fb85SRichard Alpe } 741c3d6fb85SRichard Alpe 742c3d6fb85SRichard Alpe static int __tipc_nl_compat_link_set(struct sk_buff *skb, 743c3d6fb85SRichard Alpe struct tipc_nl_compat_msg *msg) 744c3d6fb85SRichard Alpe { 745c3d6fb85SRichard Alpe struct nlattr *prop; 746c3d6fb85SRichard Alpe struct nlattr *link; 74737e2d484SRichard Alpe struct tipc_link_config *lc; 74837e2d484SRichard Alpe 74937e2d484SRichard Alpe lc = (struct tipc_link_config *)TLV_DATA(msg->req); 75037e2d484SRichard Alpe 75137e2d484SRichard Alpe link = nla_nest_start(skb, TIPC_NLA_LINK); 75237e2d484SRichard Alpe if (!link) 75337e2d484SRichard Alpe return -EMSGSIZE; 75437e2d484SRichard Alpe 75537e2d484SRichard Alpe if (nla_put_string(skb, TIPC_NLA_LINK_NAME, lc->name)) 75637e2d484SRichard Alpe return -EMSGSIZE; 75737e2d484SRichard Alpe 75837e2d484SRichard Alpe prop = nla_nest_start(skb, TIPC_NLA_LINK_PROP); 75937e2d484SRichard Alpe if (!prop) 76037e2d484SRichard Alpe return -EMSGSIZE; 76137e2d484SRichard Alpe 762c3d6fb85SRichard Alpe __tipc_add_link_prop(skb, msg, lc); 76337e2d484SRichard Alpe nla_nest_end(skb, prop); 76437e2d484SRichard Alpe nla_nest_end(skb, link); 76537e2d484SRichard Alpe 76637e2d484SRichard Alpe return 0; 76737e2d484SRichard Alpe } 76837e2d484SRichard Alpe 769c3d6fb85SRichard Alpe static int tipc_nl_compat_link_set(struct tipc_nl_compat_cmd_doit *cmd, 770c3d6fb85SRichard Alpe struct sk_buff *skb, 771c3d6fb85SRichard Alpe struct tipc_nl_compat_msg *msg) 772c3d6fb85SRichard Alpe { 773c3d6fb85SRichard Alpe struct tipc_link_config *lc; 774c3d6fb85SRichard Alpe struct tipc_bearer *bearer; 775c3d6fb85SRichard Alpe struct tipc_media *media; 776edf5ff04SYing Xue int len; 777c3d6fb85SRichard Alpe 778c3d6fb85SRichard Alpe lc = (struct tipc_link_config *)TLV_DATA(msg->req); 779c3d6fb85SRichard Alpe 7808c63bf9aSXin Long len = TLV_GET_DATA_LEN(msg->req); 7818c63bf9aSXin Long len -= offsetof(struct tipc_link_config, name); 7828c63bf9aSXin Long if (len <= 0) 7838c63bf9aSXin Long return -EINVAL; 7848c63bf9aSXin Long 7858c63bf9aSXin Long len = min_t(int, len, TIPC_MAX_LINK_NAME); 786edf5ff04SYing Xue if (!string_is_valid(lc->name, len)) 787edf5ff04SYing Xue return -EINVAL; 788edf5ff04SYing Xue 789c3d6fb85SRichard Alpe media = tipc_media_find(lc->name); 790c3d6fb85SRichard Alpe if (media) { 791ed4ffdfeSYing Xue cmd->doit = &__tipc_nl_media_set; 792c3d6fb85SRichard Alpe return tipc_nl_compat_media_set(skb, msg); 793c3d6fb85SRichard Alpe } 794c3d6fb85SRichard Alpe 795c3d6fb85SRichard Alpe bearer = tipc_bearer_find(msg->net, lc->name); 796c3d6fb85SRichard Alpe if (bearer) { 797ed4ffdfeSYing Xue cmd->doit = &__tipc_nl_bearer_set; 798c3d6fb85SRichard Alpe return tipc_nl_compat_bearer_set(skb, msg); 799c3d6fb85SRichard Alpe } 800c3d6fb85SRichard Alpe 801c3d6fb85SRichard Alpe return __tipc_nl_compat_link_set(skb, msg); 802c3d6fb85SRichard Alpe } 803c3d6fb85SRichard Alpe 804c3d6fb85SRichard Alpe static int tipc_nl_compat_link_reset_stats(struct tipc_nl_compat_cmd_doit *cmd, 805c3d6fb85SRichard Alpe struct sk_buff *skb, 8061817877bSRichard Alpe struct tipc_nl_compat_msg *msg) 8071817877bSRichard Alpe { 8081817877bSRichard Alpe char *name; 8091817877bSRichard Alpe struct nlattr *link; 8108b66fee7SYing Xue int len; 8111817877bSRichard Alpe 8121817877bSRichard Alpe name = (char *)TLV_DATA(msg->req); 8131817877bSRichard Alpe 8141817877bSRichard Alpe link = nla_nest_start(skb, TIPC_NLA_LINK); 8151817877bSRichard Alpe if (!link) 8161817877bSRichard Alpe return -EMSGSIZE; 8171817877bSRichard Alpe 8188b66fee7SYing Xue len = min_t(int, TLV_GET_DATA_LEN(msg->req), TIPC_MAX_LINK_NAME); 8198b66fee7SYing Xue if (!string_is_valid(name, len)) 8208b66fee7SYing Xue return -EINVAL; 8218b66fee7SYing Xue 8221817877bSRichard Alpe if (nla_put_string(skb, TIPC_NLA_LINK_NAME, name)) 8231817877bSRichard Alpe return -EMSGSIZE; 8241817877bSRichard Alpe 8251817877bSRichard Alpe nla_nest_end(skb, link); 8261817877bSRichard Alpe 8271817877bSRichard Alpe return 0; 8281817877bSRichard Alpe } 8291817877bSRichard Alpe 83044a8ae94SRichard Alpe static int tipc_nl_compat_name_table_dump_header(struct tipc_nl_compat_msg *msg) 83144a8ae94SRichard Alpe { 83244a8ae94SRichard Alpe int i; 83344a8ae94SRichard Alpe u32 depth; 83444a8ae94SRichard Alpe struct tipc_name_table_query *ntq; 83544a8ae94SRichard Alpe static const char * const header[] = { 83644a8ae94SRichard Alpe "Type ", 83744a8ae94SRichard Alpe "Lower Upper ", 83844a8ae94SRichard Alpe "Port Identity ", 83944a8ae94SRichard Alpe "Publication Scope" 84044a8ae94SRichard Alpe }; 84144a8ae94SRichard Alpe 84244a8ae94SRichard Alpe ntq = (struct tipc_name_table_query *)TLV_DATA(msg->req); 843974cb0e3SYing Xue if (TLV_GET_DATA_LEN(msg->req) < sizeof(struct tipc_name_table_query)) 844974cb0e3SYing Xue return -EINVAL; 84544a8ae94SRichard Alpe 84644a8ae94SRichard Alpe depth = ntohl(ntq->depth); 84744a8ae94SRichard Alpe 84844a8ae94SRichard Alpe if (depth > 4) 84944a8ae94SRichard Alpe depth = 4; 85044a8ae94SRichard Alpe for (i = 0; i < depth; i++) 85144a8ae94SRichard Alpe tipc_tlv_sprintf(msg->rep, header[i]); 85244a8ae94SRichard Alpe tipc_tlv_sprintf(msg->rep, "\n"); 85344a8ae94SRichard Alpe 85444a8ae94SRichard Alpe return 0; 85544a8ae94SRichard Alpe } 85644a8ae94SRichard Alpe 85744a8ae94SRichard Alpe static int tipc_nl_compat_name_table_dump(struct tipc_nl_compat_msg *msg, 85844a8ae94SRichard Alpe struct nlattr **attrs) 85944a8ae94SRichard Alpe { 86044a8ae94SRichard Alpe char port_str[27]; 86144a8ae94SRichard Alpe struct tipc_name_table_query *ntq; 86244a8ae94SRichard Alpe struct nlattr *nt[TIPC_NLA_NAME_TABLE_MAX + 1]; 86344a8ae94SRichard Alpe struct nlattr *publ[TIPC_NLA_PUBL_MAX + 1]; 86444a8ae94SRichard Alpe u32 node, depth, type, lowbound, upbound; 86544a8ae94SRichard Alpe static const char * const scope_str[] = {"", " zone", " cluster", 86644a8ae94SRichard Alpe " node"}; 867297f7d2cSBaozeng Ding int err; 86844a8ae94SRichard Alpe 869297f7d2cSBaozeng Ding if (!attrs[TIPC_NLA_NAME_TABLE]) 870297f7d2cSBaozeng Ding return -EINVAL; 871297f7d2cSBaozeng Ding 872297f7d2cSBaozeng Ding err = nla_parse_nested(nt, TIPC_NLA_NAME_TABLE_MAX, 873fceb6435SJohannes Berg attrs[TIPC_NLA_NAME_TABLE], NULL, NULL); 874297f7d2cSBaozeng Ding if (err) 875297f7d2cSBaozeng Ding return err; 87644a8ae94SRichard Alpe 877297f7d2cSBaozeng Ding if (!nt[TIPC_NLA_NAME_TABLE_PUBL]) 878297f7d2cSBaozeng Ding return -EINVAL; 879297f7d2cSBaozeng Ding 880297f7d2cSBaozeng Ding err = nla_parse_nested(publ, TIPC_NLA_PUBL_MAX, 881fceb6435SJohannes Berg nt[TIPC_NLA_NAME_TABLE_PUBL], NULL, NULL); 882297f7d2cSBaozeng Ding if (err) 883297f7d2cSBaozeng Ding return err; 88444a8ae94SRichard Alpe 88544a8ae94SRichard Alpe ntq = (struct tipc_name_table_query *)TLV_DATA(msg->req); 88644a8ae94SRichard Alpe 88744a8ae94SRichard Alpe depth = ntohl(ntq->depth); 88844a8ae94SRichard Alpe type = ntohl(ntq->type); 88944a8ae94SRichard Alpe lowbound = ntohl(ntq->lowbound); 89044a8ae94SRichard Alpe upbound = ntohl(ntq->upbound); 89144a8ae94SRichard Alpe 89244a8ae94SRichard Alpe if (!(depth & TIPC_NTQ_ALLTYPES) && 89344a8ae94SRichard Alpe (type != nla_get_u32(publ[TIPC_NLA_PUBL_TYPE]))) 89444a8ae94SRichard Alpe return 0; 89544a8ae94SRichard Alpe if (lowbound && (lowbound > nla_get_u32(publ[TIPC_NLA_PUBL_UPPER]))) 89644a8ae94SRichard Alpe return 0; 89744a8ae94SRichard Alpe if (upbound && (upbound < nla_get_u32(publ[TIPC_NLA_PUBL_LOWER]))) 89844a8ae94SRichard Alpe return 0; 89944a8ae94SRichard Alpe 90044a8ae94SRichard Alpe tipc_tlv_sprintf(msg->rep, "%-10u ", 90144a8ae94SRichard Alpe nla_get_u32(publ[TIPC_NLA_PUBL_TYPE])); 90244a8ae94SRichard Alpe 90344a8ae94SRichard Alpe if (depth == 1) 90444a8ae94SRichard Alpe goto out; 90544a8ae94SRichard Alpe 90644a8ae94SRichard Alpe tipc_tlv_sprintf(msg->rep, "%-10u %-10u ", 90744a8ae94SRichard Alpe nla_get_u32(publ[TIPC_NLA_PUBL_LOWER]), 90844a8ae94SRichard Alpe nla_get_u32(publ[TIPC_NLA_PUBL_UPPER])); 90944a8ae94SRichard Alpe 91044a8ae94SRichard Alpe if (depth == 2) 91144a8ae94SRichard Alpe goto out; 91244a8ae94SRichard Alpe 91344a8ae94SRichard Alpe node = nla_get_u32(publ[TIPC_NLA_PUBL_NODE]); 91444a8ae94SRichard Alpe sprintf(port_str, "<%u.%u.%u:%u>", tipc_zone(node), tipc_cluster(node), 91544a8ae94SRichard Alpe tipc_node(node), nla_get_u32(publ[TIPC_NLA_PUBL_REF])); 91644a8ae94SRichard Alpe tipc_tlv_sprintf(msg->rep, "%-26s ", port_str); 91744a8ae94SRichard Alpe 91844a8ae94SRichard Alpe if (depth == 3) 91944a8ae94SRichard Alpe goto out; 92044a8ae94SRichard Alpe 92144a8ae94SRichard Alpe tipc_tlv_sprintf(msg->rep, "%-10u %s", 92203aaaa9bSRichard Alpe nla_get_u32(publ[TIPC_NLA_PUBL_KEY]), 92344a8ae94SRichard Alpe scope_str[nla_get_u32(publ[TIPC_NLA_PUBL_SCOPE])]); 92444a8ae94SRichard Alpe out: 92544a8ae94SRichard Alpe tipc_tlv_sprintf(msg->rep, "\n"); 92644a8ae94SRichard Alpe 92744a8ae94SRichard Alpe return 0; 92844a8ae94SRichard Alpe } 92944a8ae94SRichard Alpe 930487d2a3aSRichard Alpe static int __tipc_nl_compat_publ_dump(struct tipc_nl_compat_msg *msg, 931487d2a3aSRichard Alpe struct nlattr **attrs) 932487d2a3aSRichard Alpe { 933487d2a3aSRichard Alpe u32 type, lower, upper; 934487d2a3aSRichard Alpe struct nlattr *publ[TIPC_NLA_PUBL_MAX + 1]; 935297f7d2cSBaozeng Ding int err; 936487d2a3aSRichard Alpe 937297f7d2cSBaozeng Ding if (!attrs[TIPC_NLA_PUBL]) 938297f7d2cSBaozeng Ding return -EINVAL; 939297f7d2cSBaozeng Ding 940297f7d2cSBaozeng Ding err = nla_parse_nested(publ, TIPC_NLA_PUBL_MAX, attrs[TIPC_NLA_PUBL], 941fceb6435SJohannes Berg NULL, NULL); 942297f7d2cSBaozeng Ding if (err) 943297f7d2cSBaozeng Ding return err; 944487d2a3aSRichard Alpe 945487d2a3aSRichard Alpe type = nla_get_u32(publ[TIPC_NLA_PUBL_TYPE]); 946487d2a3aSRichard Alpe lower = nla_get_u32(publ[TIPC_NLA_PUBL_LOWER]); 947487d2a3aSRichard Alpe upper = nla_get_u32(publ[TIPC_NLA_PUBL_UPPER]); 948487d2a3aSRichard Alpe 949487d2a3aSRichard Alpe if (lower == upper) 950487d2a3aSRichard Alpe tipc_tlv_sprintf(msg->rep, " {%u,%u}", type, lower); 951487d2a3aSRichard Alpe else 952487d2a3aSRichard Alpe tipc_tlv_sprintf(msg->rep, " {%u,%u,%u}", type, lower, upper); 953487d2a3aSRichard Alpe 954487d2a3aSRichard Alpe return 0; 955487d2a3aSRichard Alpe } 956487d2a3aSRichard Alpe 957487d2a3aSRichard Alpe static int tipc_nl_compat_publ_dump(struct tipc_nl_compat_msg *msg, u32 sock) 958487d2a3aSRichard Alpe { 959487d2a3aSRichard Alpe int err; 960487d2a3aSRichard Alpe void *hdr; 961487d2a3aSRichard Alpe struct nlattr *nest; 962487d2a3aSRichard Alpe struct sk_buff *args; 963487d2a3aSRichard Alpe struct tipc_nl_compat_cmd_dump dump; 964487d2a3aSRichard Alpe 965487d2a3aSRichard Alpe args = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); 966487d2a3aSRichard Alpe if (!args) 967487d2a3aSRichard Alpe return -ENOMEM; 968487d2a3aSRichard Alpe 969487d2a3aSRichard Alpe hdr = genlmsg_put(args, 0, 0, &tipc_genl_family, NLM_F_MULTI, 970487d2a3aSRichard Alpe TIPC_NL_PUBL_GET); 971f87d8ad9SGustavo A. R. Silva if (!hdr) { 972f87d8ad9SGustavo A. R. Silva kfree_skb(args); 97346273cf7SKangjie Lu return -EMSGSIZE; 974f87d8ad9SGustavo A. R. Silva } 975487d2a3aSRichard Alpe 976487d2a3aSRichard Alpe nest = nla_nest_start(args, TIPC_NLA_SOCK); 977487d2a3aSRichard Alpe if (!nest) { 978487d2a3aSRichard Alpe kfree_skb(args); 979487d2a3aSRichard Alpe return -EMSGSIZE; 980487d2a3aSRichard Alpe } 981487d2a3aSRichard Alpe 982487d2a3aSRichard Alpe if (nla_put_u32(args, TIPC_NLA_SOCK_REF, sock)) { 983487d2a3aSRichard Alpe kfree_skb(args); 984487d2a3aSRichard Alpe return -EMSGSIZE; 985487d2a3aSRichard Alpe } 986487d2a3aSRichard Alpe 987487d2a3aSRichard Alpe nla_nest_end(args, nest); 988487d2a3aSRichard Alpe genlmsg_end(args, hdr); 989487d2a3aSRichard Alpe 990487d2a3aSRichard Alpe dump.dumpit = tipc_nl_publ_dump; 991487d2a3aSRichard Alpe dump.format = __tipc_nl_compat_publ_dump; 992487d2a3aSRichard Alpe 993487d2a3aSRichard Alpe err = __tipc_nl_compat_dumpit(&dump, msg, args); 994487d2a3aSRichard Alpe 995487d2a3aSRichard Alpe kfree_skb(args); 996487d2a3aSRichard Alpe 997487d2a3aSRichard Alpe return err; 998487d2a3aSRichard Alpe } 999487d2a3aSRichard Alpe 1000487d2a3aSRichard Alpe static int tipc_nl_compat_sk_dump(struct tipc_nl_compat_msg *msg, 1001487d2a3aSRichard Alpe struct nlattr **attrs) 1002487d2a3aSRichard Alpe { 1003487d2a3aSRichard Alpe int err; 1004487d2a3aSRichard Alpe u32 sock_ref; 1005487d2a3aSRichard Alpe struct nlattr *sock[TIPC_NLA_SOCK_MAX + 1]; 1006487d2a3aSRichard Alpe 1007297f7d2cSBaozeng Ding if (!attrs[TIPC_NLA_SOCK]) 1008297f7d2cSBaozeng Ding return -EINVAL; 1009297f7d2cSBaozeng Ding 1010297f7d2cSBaozeng Ding err = nla_parse_nested(sock, TIPC_NLA_SOCK_MAX, attrs[TIPC_NLA_SOCK], 1011fceb6435SJohannes Berg NULL, NULL); 1012297f7d2cSBaozeng Ding if (err) 1013297f7d2cSBaozeng Ding return err; 1014487d2a3aSRichard Alpe 1015487d2a3aSRichard Alpe sock_ref = nla_get_u32(sock[TIPC_NLA_SOCK_REF]); 1016487d2a3aSRichard Alpe tipc_tlv_sprintf(msg->rep, "%u:", sock_ref); 1017487d2a3aSRichard Alpe 1018487d2a3aSRichard Alpe if (sock[TIPC_NLA_SOCK_CON]) { 1019487d2a3aSRichard Alpe u32 node; 1020487d2a3aSRichard Alpe struct nlattr *con[TIPC_NLA_CON_MAX + 1]; 1021487d2a3aSRichard Alpe 102289dfd008SAditya Pakki err = nla_parse_nested(con, TIPC_NLA_CON_MAX, 1023fceb6435SJohannes Berg sock[TIPC_NLA_SOCK_CON], NULL, NULL); 1024487d2a3aSRichard Alpe 102589dfd008SAditya Pakki if (err) 102689dfd008SAditya Pakki return err; 102789dfd008SAditya Pakki 1028487d2a3aSRichard Alpe node = nla_get_u32(con[TIPC_NLA_CON_NODE]); 1029487d2a3aSRichard Alpe tipc_tlv_sprintf(msg->rep, " connected to <%u.%u.%u:%u>", 1030487d2a3aSRichard Alpe tipc_zone(node), 1031487d2a3aSRichard Alpe tipc_cluster(node), 1032487d2a3aSRichard Alpe tipc_node(node), 1033487d2a3aSRichard Alpe nla_get_u32(con[TIPC_NLA_CON_SOCK])); 1034487d2a3aSRichard Alpe 1035487d2a3aSRichard Alpe if (con[TIPC_NLA_CON_FLAG]) 1036487d2a3aSRichard Alpe tipc_tlv_sprintf(msg->rep, " via {%u,%u}\n", 1037487d2a3aSRichard Alpe nla_get_u32(con[TIPC_NLA_CON_TYPE]), 1038487d2a3aSRichard Alpe nla_get_u32(con[TIPC_NLA_CON_INST])); 1039487d2a3aSRichard Alpe else 1040487d2a3aSRichard Alpe tipc_tlv_sprintf(msg->rep, "\n"); 1041487d2a3aSRichard Alpe } else if (sock[TIPC_NLA_SOCK_HAS_PUBL]) { 1042487d2a3aSRichard Alpe tipc_tlv_sprintf(msg->rep, " bound to"); 1043487d2a3aSRichard Alpe 1044487d2a3aSRichard Alpe err = tipc_nl_compat_publ_dump(msg, sock_ref); 1045487d2a3aSRichard Alpe if (err) 1046487d2a3aSRichard Alpe return err; 1047487d2a3aSRichard Alpe } 1048487d2a3aSRichard Alpe tipc_tlv_sprintf(msg->rep, "\n"); 1049487d2a3aSRichard Alpe 1050487d2a3aSRichard Alpe return 0; 1051487d2a3aSRichard Alpe } 1052487d2a3aSRichard Alpe 10535bfc335aSRichard Alpe static int tipc_nl_compat_media_dump(struct tipc_nl_compat_msg *msg, 10545bfc335aSRichard Alpe struct nlattr **attrs) 10555bfc335aSRichard Alpe { 10565bfc335aSRichard Alpe struct nlattr *media[TIPC_NLA_MEDIA_MAX + 1]; 1057297f7d2cSBaozeng Ding int err; 10585bfc335aSRichard Alpe 1059297f7d2cSBaozeng Ding if (!attrs[TIPC_NLA_MEDIA]) 1060297f7d2cSBaozeng Ding return -EINVAL; 1061297f7d2cSBaozeng Ding 1062fceb6435SJohannes Berg err = nla_parse_nested(media, TIPC_NLA_MEDIA_MAX, 1063fceb6435SJohannes Berg attrs[TIPC_NLA_MEDIA], NULL, NULL); 1064297f7d2cSBaozeng Ding if (err) 1065297f7d2cSBaozeng Ding return err; 10665bfc335aSRichard Alpe 10675bfc335aSRichard Alpe return tipc_add_tlv(msg->rep, TIPC_TLV_MEDIA_NAME, 10685bfc335aSRichard Alpe nla_data(media[TIPC_NLA_MEDIA_NAME]), 10695bfc335aSRichard Alpe nla_len(media[TIPC_NLA_MEDIA_NAME])); 10705bfc335aSRichard Alpe } 10715bfc335aSRichard Alpe 10724b28cb58SRichard Alpe static int tipc_nl_compat_node_dump(struct tipc_nl_compat_msg *msg, 10734b28cb58SRichard Alpe struct nlattr **attrs) 10744b28cb58SRichard Alpe { 10754b28cb58SRichard Alpe struct tipc_node_info node_info; 10764b28cb58SRichard Alpe struct nlattr *node[TIPC_NLA_NODE_MAX + 1]; 1077297f7d2cSBaozeng Ding int err; 10784b28cb58SRichard Alpe 1079297f7d2cSBaozeng Ding if (!attrs[TIPC_NLA_NODE]) 1080297f7d2cSBaozeng Ding return -EINVAL; 1081297f7d2cSBaozeng Ding 1082297f7d2cSBaozeng Ding err = nla_parse_nested(node, TIPC_NLA_NODE_MAX, attrs[TIPC_NLA_NODE], 1083fceb6435SJohannes Berg NULL, NULL); 1084297f7d2cSBaozeng Ding if (err) 1085297f7d2cSBaozeng Ding return err; 10864b28cb58SRichard Alpe 10874b28cb58SRichard Alpe node_info.addr = htonl(nla_get_u32(node[TIPC_NLA_NODE_ADDR])); 10884b28cb58SRichard Alpe node_info.up = htonl(nla_get_flag(node[TIPC_NLA_NODE_UP])); 10894b28cb58SRichard Alpe 10904b28cb58SRichard Alpe return tipc_add_tlv(msg->rep, TIPC_TLV_NODE_INFO, &node_info, 10914b28cb58SRichard Alpe sizeof(node_info)); 10924b28cb58SRichard Alpe } 10934b28cb58SRichard Alpe 1094c3d6fb85SRichard Alpe static int tipc_nl_compat_net_set(struct tipc_nl_compat_cmd_doit *cmd, 1095c3d6fb85SRichard Alpe struct sk_buff *skb, 1096d7cc75d3SRichard Alpe struct tipc_nl_compat_msg *msg) 1097d7cc75d3SRichard Alpe { 1098d7cc75d3SRichard Alpe u32 val; 1099d7cc75d3SRichard Alpe struct nlattr *net; 1100d7cc75d3SRichard Alpe 1101d7cc75d3SRichard Alpe val = ntohl(*(__be32 *)TLV_DATA(msg->req)); 1102d7cc75d3SRichard Alpe 1103d7cc75d3SRichard Alpe net = nla_nest_start(skb, TIPC_NLA_NET); 1104d7cc75d3SRichard Alpe if (!net) 1105d7cc75d3SRichard Alpe return -EMSGSIZE; 1106d7cc75d3SRichard Alpe 1107964f9501SRichard Alpe if (msg->cmd == TIPC_CMD_SET_NODE_ADDR) { 1108d7cc75d3SRichard Alpe if (nla_put_u32(skb, TIPC_NLA_NET_ADDR, val)) 1109d7cc75d3SRichard Alpe return -EMSGSIZE; 1110964f9501SRichard Alpe } else if (msg->cmd == TIPC_CMD_SET_NETID) { 1111964f9501SRichard Alpe if (nla_put_u32(skb, TIPC_NLA_NET_ID, val)) 1112964f9501SRichard Alpe return -EMSGSIZE; 1113964f9501SRichard Alpe } 1114d7cc75d3SRichard Alpe nla_nest_end(skb, net); 1115d7cc75d3SRichard Alpe 1116d7cc75d3SRichard Alpe return 0; 1117d7cc75d3SRichard Alpe } 1118d7cc75d3SRichard Alpe 11193c26181cSRichard Alpe static int tipc_nl_compat_net_dump(struct tipc_nl_compat_msg *msg, 11203c26181cSRichard Alpe struct nlattr **attrs) 11213c26181cSRichard Alpe { 11223c26181cSRichard Alpe __be32 id; 11233c26181cSRichard Alpe struct nlattr *net[TIPC_NLA_NET_MAX + 1]; 1124297f7d2cSBaozeng Ding int err; 11253c26181cSRichard Alpe 1126297f7d2cSBaozeng Ding if (!attrs[TIPC_NLA_NET]) 1127297f7d2cSBaozeng Ding return -EINVAL; 1128297f7d2cSBaozeng Ding 1129297f7d2cSBaozeng Ding err = nla_parse_nested(net, TIPC_NLA_NET_MAX, attrs[TIPC_NLA_NET], 1130fceb6435SJohannes Berg NULL, NULL); 1131297f7d2cSBaozeng Ding if (err) 1132297f7d2cSBaozeng Ding return err; 1133297f7d2cSBaozeng Ding 11343c26181cSRichard Alpe id = htonl(nla_get_u32(net[TIPC_NLA_NET_ID])); 11353c26181cSRichard Alpe 11363c26181cSRichard Alpe return tipc_add_tlv(msg->rep, TIPC_TLV_UNSIGNED, &id, sizeof(id)); 11373c26181cSRichard Alpe } 11383c26181cSRichard Alpe 11395a81a637SRichard Alpe static int tipc_cmd_show_stats_compat(struct tipc_nl_compat_msg *msg) 11405a81a637SRichard Alpe { 11415a81a637SRichard Alpe msg->rep = tipc_tlv_alloc(ULTRA_STRING_MAX_LEN); 11425a81a637SRichard Alpe if (!msg->rep) 11435a81a637SRichard Alpe return -ENOMEM; 11445a81a637SRichard Alpe 11455a81a637SRichard Alpe tipc_tlv_init(msg->rep, TIPC_TLV_ULTRA_STRING); 11465a81a637SRichard Alpe tipc_tlv_sprintf(msg->rep, "TIPC version " TIPC_MOD_VER "\n"); 11475a81a637SRichard Alpe 11485a81a637SRichard Alpe return 0; 11495a81a637SRichard Alpe } 11505a81a637SRichard Alpe 1151d0796d1eSRichard Alpe static int tipc_nl_compat_handle(struct tipc_nl_compat_msg *msg) 1152d0796d1eSRichard Alpe { 1153d0796d1eSRichard Alpe struct tipc_nl_compat_cmd_dump dump; 11549ab15465SRichard Alpe struct tipc_nl_compat_cmd_doit doit; 1155d0796d1eSRichard Alpe 1156d0796d1eSRichard Alpe memset(&dump, 0, sizeof(dump)); 11579ab15465SRichard Alpe memset(&doit, 0, sizeof(doit)); 1158d0796d1eSRichard Alpe 1159d0796d1eSRichard Alpe switch (msg->cmd) { 116022ae7cffSRichard Alpe case TIPC_CMD_NOOP: 116122ae7cffSRichard Alpe msg->rep = tipc_tlv_alloc(0); 116222ae7cffSRichard Alpe if (!msg->rep) 116322ae7cffSRichard Alpe return -ENOMEM; 116422ae7cffSRichard Alpe return 0; 1165d0796d1eSRichard Alpe case TIPC_CMD_GET_BEARER_NAMES: 1166d0796d1eSRichard Alpe msg->rep_size = MAX_BEARERS * TLV_SPACE(TIPC_MAX_BEARER_NAME); 1167d0796d1eSRichard Alpe dump.dumpit = tipc_nl_bearer_dump; 1168d0796d1eSRichard Alpe dump.format = tipc_nl_compat_bearer_dump; 1169d0796d1eSRichard Alpe return tipc_nl_compat_dumpit(&dump, msg); 11709ab15465SRichard Alpe case TIPC_CMD_ENABLE_BEARER: 11719ab15465SRichard Alpe msg->req_type = TIPC_TLV_BEARER_CONFIG; 1172ed4ffdfeSYing Xue doit.doit = __tipc_nl_bearer_enable; 11739ab15465SRichard Alpe doit.transcode = tipc_nl_compat_bearer_enable; 11749ab15465SRichard Alpe return tipc_nl_compat_doit(&doit, msg); 11759ab15465SRichard Alpe case TIPC_CMD_DISABLE_BEARER: 11769ab15465SRichard Alpe msg->req_type = TIPC_TLV_BEARER_NAME; 1177ed4ffdfeSYing Xue doit.doit = __tipc_nl_bearer_disable; 11789ab15465SRichard Alpe doit.transcode = tipc_nl_compat_bearer_disable; 11799ab15465SRichard Alpe return tipc_nl_compat_doit(&doit, msg); 1180f2b3b2d4SRichard Alpe case TIPC_CMD_SHOW_LINK_STATS: 1181f2b3b2d4SRichard Alpe msg->req_type = TIPC_TLV_LINK_NAME; 1182f2b3b2d4SRichard Alpe msg->rep_size = ULTRA_STRING_MAX_LEN; 1183f2b3b2d4SRichard Alpe msg->rep_type = TIPC_TLV_ULTRA_STRING; 118438206d59SJon Paul Maloy dump.dumpit = tipc_nl_node_dump_link; 1185f2b3b2d4SRichard Alpe dump.format = tipc_nl_compat_link_stat_dump; 1186f2b3b2d4SRichard Alpe return tipc_nl_compat_dumpit(&dump, msg); 1187357ebdbfSRichard Alpe case TIPC_CMD_GET_LINKS: 1188357ebdbfSRichard Alpe msg->req_type = TIPC_TLV_NET_ADDR; 1189357ebdbfSRichard Alpe msg->rep_size = ULTRA_STRING_MAX_LEN; 119038206d59SJon Paul Maloy dump.dumpit = tipc_nl_node_dump_link; 1191357ebdbfSRichard Alpe dump.format = tipc_nl_compat_link_dump; 1192357ebdbfSRichard Alpe return tipc_nl_compat_dumpit(&dump, msg); 119337e2d484SRichard Alpe case TIPC_CMD_SET_LINK_TOL: 119437e2d484SRichard Alpe case TIPC_CMD_SET_LINK_PRI: 119537e2d484SRichard Alpe case TIPC_CMD_SET_LINK_WINDOW: 119637e2d484SRichard Alpe msg->req_type = TIPC_TLV_LINK_CONFIG; 11975be9c086SJon Paul Maloy doit.doit = tipc_nl_node_set_link; 119837e2d484SRichard Alpe doit.transcode = tipc_nl_compat_link_set; 119937e2d484SRichard Alpe return tipc_nl_compat_doit(&doit, msg); 12001817877bSRichard Alpe case TIPC_CMD_RESET_LINK_STATS: 12011817877bSRichard Alpe msg->req_type = TIPC_TLV_LINK_NAME; 12025be9c086SJon Paul Maloy doit.doit = tipc_nl_node_reset_link_stats; 12031817877bSRichard Alpe doit.transcode = tipc_nl_compat_link_reset_stats; 12041817877bSRichard Alpe return tipc_nl_compat_doit(&doit, msg); 120544a8ae94SRichard Alpe case TIPC_CMD_SHOW_NAME_TABLE: 120644a8ae94SRichard Alpe msg->req_type = TIPC_TLV_NAME_TBL_QUERY; 120744a8ae94SRichard Alpe msg->rep_size = ULTRA_STRING_MAX_LEN; 120844a8ae94SRichard Alpe msg->rep_type = TIPC_TLV_ULTRA_STRING; 120944a8ae94SRichard Alpe dump.header = tipc_nl_compat_name_table_dump_header; 121044a8ae94SRichard Alpe dump.dumpit = tipc_nl_name_table_dump; 121144a8ae94SRichard Alpe dump.format = tipc_nl_compat_name_table_dump; 121244a8ae94SRichard Alpe return tipc_nl_compat_dumpit(&dump, msg); 1213487d2a3aSRichard Alpe case TIPC_CMD_SHOW_PORTS: 1214487d2a3aSRichard Alpe msg->rep_size = ULTRA_STRING_MAX_LEN; 1215487d2a3aSRichard Alpe msg->rep_type = TIPC_TLV_ULTRA_STRING; 1216487d2a3aSRichard Alpe dump.dumpit = tipc_nl_sk_dump; 1217487d2a3aSRichard Alpe dump.format = tipc_nl_compat_sk_dump; 1218487d2a3aSRichard Alpe return tipc_nl_compat_dumpit(&dump, msg); 12195bfc335aSRichard Alpe case TIPC_CMD_GET_MEDIA_NAMES: 12205bfc335aSRichard Alpe msg->rep_size = MAX_MEDIA * TLV_SPACE(TIPC_MAX_MEDIA_NAME); 12215bfc335aSRichard Alpe dump.dumpit = tipc_nl_media_dump; 12225bfc335aSRichard Alpe dump.format = tipc_nl_compat_media_dump; 12235bfc335aSRichard Alpe return tipc_nl_compat_dumpit(&dump, msg); 12244b28cb58SRichard Alpe case TIPC_CMD_GET_NODES: 12254b28cb58SRichard Alpe msg->rep_size = ULTRA_STRING_MAX_LEN; 12264b28cb58SRichard Alpe dump.dumpit = tipc_nl_node_dump; 12274b28cb58SRichard Alpe dump.format = tipc_nl_compat_node_dump; 12284b28cb58SRichard Alpe return tipc_nl_compat_dumpit(&dump, msg); 1229d7cc75d3SRichard Alpe case TIPC_CMD_SET_NODE_ADDR: 1230d7cc75d3SRichard Alpe msg->req_type = TIPC_TLV_NET_ADDR; 1231ed4ffdfeSYing Xue doit.doit = __tipc_nl_net_set; 1232d7cc75d3SRichard Alpe doit.transcode = tipc_nl_compat_net_set; 1233d7cc75d3SRichard Alpe return tipc_nl_compat_doit(&doit, msg); 1234964f9501SRichard Alpe case TIPC_CMD_SET_NETID: 1235964f9501SRichard Alpe msg->req_type = TIPC_TLV_UNSIGNED; 1236ed4ffdfeSYing Xue doit.doit = __tipc_nl_net_set; 1237964f9501SRichard Alpe doit.transcode = tipc_nl_compat_net_set; 1238964f9501SRichard Alpe return tipc_nl_compat_doit(&doit, msg); 12393c26181cSRichard Alpe case TIPC_CMD_GET_NETID: 12403c26181cSRichard Alpe msg->rep_size = sizeof(u32); 12413c26181cSRichard Alpe dump.dumpit = tipc_nl_net_dump; 12423c26181cSRichard Alpe dump.format = tipc_nl_compat_net_dump; 12433c26181cSRichard Alpe return tipc_nl_compat_dumpit(&dump, msg); 12445a81a637SRichard Alpe case TIPC_CMD_SHOW_STATS: 12455a81a637SRichard Alpe return tipc_cmd_show_stats_compat(msg); 1246d0796d1eSRichard Alpe } 1247d0796d1eSRichard Alpe 1248d0796d1eSRichard Alpe return -EOPNOTSUPP; 1249d0796d1eSRichard Alpe } 1250d0796d1eSRichard Alpe 1251d0796d1eSRichard Alpe static int tipc_nl_compat_recv(struct sk_buff *skb, struct genl_info *info) 1252d0796d1eSRichard Alpe { 1253d0796d1eSRichard Alpe int err; 1254d0796d1eSRichard Alpe int len; 1255d0796d1eSRichard Alpe struct tipc_nl_compat_msg msg; 1256d0796d1eSRichard Alpe struct nlmsghdr *req_nlh; 1257d0796d1eSRichard Alpe struct nlmsghdr *rep_nlh; 1258d0796d1eSRichard Alpe struct tipc_genlmsghdr *req_userhdr = info->userhdr; 1259d0796d1eSRichard Alpe 1260d0796d1eSRichard Alpe memset(&msg, 0, sizeof(msg)); 1261d0796d1eSRichard Alpe 1262d0796d1eSRichard Alpe req_nlh = (struct nlmsghdr *)skb->data; 1263d0796d1eSRichard Alpe msg.req = nlmsg_data(req_nlh) + GENL_HDRLEN + TIPC_GENL_HDRLEN; 1264d0796d1eSRichard Alpe msg.cmd = req_userhdr->cmd; 1265c3d6fb85SRichard Alpe msg.net = genl_info_net(info); 1266619b1745SFlorian Westphal msg.dst_sk = skb->sk; 1267d0796d1eSRichard Alpe 1268d0796d1eSRichard Alpe if ((msg.cmd & 0xC000) && (!netlink_net_capable(skb, CAP_NET_ADMIN))) { 1269d0796d1eSRichard Alpe msg.rep = tipc_get_err_tlv(TIPC_CFG_NOT_NET_ADMIN); 1270d0796d1eSRichard Alpe err = -EACCES; 1271d0796d1eSRichard Alpe goto send; 1272d0796d1eSRichard Alpe } 1273d0796d1eSRichard Alpe 1274d0796d1eSRichard Alpe len = nlmsg_attrlen(req_nlh, GENL_HDRLEN + TIPC_GENL_HDRLEN); 12752753ca5dSYing Xue if (!len || !TLV_OK(msg.req, len)) { 1276d0796d1eSRichard Alpe msg.rep = tipc_get_err_tlv(TIPC_CFG_NOT_SUPPORTED); 1277d0796d1eSRichard Alpe err = -EOPNOTSUPP; 1278d0796d1eSRichard Alpe goto send; 1279d0796d1eSRichard Alpe } 1280d0796d1eSRichard Alpe 1281d0796d1eSRichard Alpe err = tipc_nl_compat_handle(&msg); 1282b063bc5eSRichard Alpe if ((err == -EOPNOTSUPP) || (err == -EPERM)) 1283d0796d1eSRichard Alpe msg.rep = tipc_get_err_tlv(TIPC_CFG_NOT_SUPPORTED); 1284d0796d1eSRichard Alpe else if (err == -EINVAL) 1285d0796d1eSRichard Alpe msg.rep = tipc_get_err_tlv(TIPC_CFG_TLV_ERROR); 1286d0796d1eSRichard Alpe send: 1287d0796d1eSRichard Alpe if (!msg.rep) 1288d0796d1eSRichard Alpe return err; 1289d0796d1eSRichard Alpe 1290d0796d1eSRichard Alpe len = nlmsg_total_size(GENL_HDRLEN + TIPC_GENL_HDRLEN); 1291d0796d1eSRichard Alpe skb_push(msg.rep, len); 1292d0796d1eSRichard Alpe rep_nlh = nlmsg_hdr(msg.rep); 1293d0796d1eSRichard Alpe memcpy(rep_nlh, info->nlhdr, len); 1294d0796d1eSRichard Alpe rep_nlh->nlmsg_len = msg.rep->len; 1295c3d6fb85SRichard Alpe genlmsg_unicast(msg.net, msg.rep, NETLINK_CB(skb).portid); 1296d0796d1eSRichard Alpe 1297d0796d1eSRichard Alpe return err; 1298d0796d1eSRichard Alpe } 1299d0796d1eSRichard Alpe 1300042a9010SArvind Yadav static const struct genl_ops tipc_genl_compat_ops[] = { 1301bfb3e5ddSRichard Alpe { 1302bfb3e5ddSRichard Alpe .cmd = TIPC_GENL_CMD, 130322ae7cffSRichard Alpe .doit = tipc_nl_compat_recv, 1304bfb3e5ddSRichard Alpe }, 1305bfb3e5ddSRichard Alpe }; 1306bfb3e5ddSRichard Alpe 130756989f6dSJohannes Berg static struct genl_family tipc_genl_compat_family __ro_after_init = { 1308489111e5SJohannes Berg .name = TIPC_GENL_NAME, 1309489111e5SJohannes Berg .version = TIPC_GENL_VERSION, 1310489111e5SJohannes Berg .hdrsize = TIPC_GENL_HDRLEN, 1311489111e5SJohannes Berg .maxattr = 0, 1312489111e5SJohannes Berg .netnsok = true, 1313489111e5SJohannes Berg .module = THIS_MODULE, 1314489111e5SJohannes Berg .ops = tipc_genl_compat_ops, 1315489111e5SJohannes Berg .n_ops = ARRAY_SIZE(tipc_genl_compat_ops), 1316489111e5SJohannes Berg }; 1317489111e5SJohannes Berg 131856989f6dSJohannes Berg int __init tipc_netlink_compat_start(void) 1319bfb3e5ddSRichard Alpe { 1320bfb3e5ddSRichard Alpe int res; 1321bfb3e5ddSRichard Alpe 1322489111e5SJohannes Berg res = genl_register_family(&tipc_genl_compat_family); 1323bfb3e5ddSRichard Alpe if (res) { 1324bfb3e5ddSRichard Alpe pr_err("Failed to register legacy compat interface\n"); 1325bfb3e5ddSRichard Alpe return res; 1326bfb3e5ddSRichard Alpe } 1327bfb3e5ddSRichard Alpe 1328bfb3e5ddSRichard Alpe return 0; 1329bfb3e5ddSRichard Alpe } 1330bfb3e5ddSRichard Alpe 1331bfb3e5ddSRichard Alpe void tipc_netlink_compat_stop(void) 1332bfb3e5ddSRichard Alpe { 1333bfb3e5ddSRichard Alpe genl_unregister_family(&tipc_genl_compat_family); 1334bfb3e5ddSRichard Alpe } 1335