blob: 12b0f4424797483fb5341bdeba614a7bd7ad5d4a [file] [log] [blame]
Richard Alpebfb3e5d2015-02-09 09:50:03 +01001/*
2 * Copyright (c) 2014, Ericsson AB
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the names of the copyright holders nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
16 *
17 * Alternatively, this software may be distributed under the terms of the
18 * GNU General Public License ("GPL") version 2 as published by the Free
19 * Software Foundation.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 */
33
34#include "core.h"
35#include "config.h"
Richard Alped0796d12015-02-09 09:50:04 +010036#include "bearer.h"
Richard Alpebfb3e5d2015-02-09 09:50:03 +010037#include <net/genetlink.h>
38#include <linux/tipc_config.h>
39
Richard Alped0796d12015-02-09 09:50:04 +010040/* The legacy API had an artificial message length limit called
41 * ULTRA_STRING_MAX_LEN.
42 */
43#define ULTRA_STRING_MAX_LEN 32768
44
45#define TIPC_SKB_MAX TLV_SPACE(ULTRA_STRING_MAX_LEN)
46
47#define REPLY_TRUNCATED "<truncated>\n"
48
49struct tipc_nl_compat_msg {
50 u16 cmd;
51 int rep_size;
Richard Alpe9ab15462015-02-09 09:50:05 +010052 int req_type;
Richard Alped0796d12015-02-09 09:50:04 +010053 struct sk_buff *rep;
54 struct tlv_desc *req;
55 struct sock *dst_sk;
56};
57
58struct tipc_nl_compat_cmd_dump {
59 int (*dumpit)(struct sk_buff *, struct netlink_callback *);
60 int (*format)(struct tipc_nl_compat_msg *msg, struct nlattr **attrs);
61};
62
Richard Alpe9ab15462015-02-09 09:50:05 +010063struct tipc_nl_compat_cmd_doit {
64 int (*doit)(struct sk_buff *skb, struct genl_info *info);
65 int (*transcode)(struct sk_buff *skb, struct tipc_nl_compat_msg *msg);
66};
67
Richard Alped0796d12015-02-09 09:50:04 +010068static int tipc_skb_tailroom(struct sk_buff *skb)
69{
70 int tailroom;
71 int limit;
72
73 tailroom = skb_tailroom(skb);
74 limit = TIPC_SKB_MAX - skb->len;
75
76 if (tailroom < limit)
77 return tailroom;
78
79 return limit;
80}
81
82static int tipc_add_tlv(struct sk_buff *skb, u16 type, void *data, u16 len)
83{
84 struct tlv_desc *tlv = (struct tlv_desc *)skb_tail_pointer(skb);
85
86 if (tipc_skb_tailroom(skb) < TLV_SPACE(len))
87 return -EMSGSIZE;
88
89 skb_put(skb, TLV_SPACE(len));
90 tlv->tlv_type = htons(type);
91 tlv->tlv_len = htons(TLV_LENGTH(len));
92 if (len && data)
93 memcpy(TLV_DATA(tlv), data, len);
94
95 return 0;
96}
97
98static struct sk_buff *tipc_tlv_alloc(int size)
99{
100 int hdr_len;
101 struct sk_buff *buf;
102
103 size = TLV_SPACE(size);
104 hdr_len = nlmsg_total_size(GENL_HDRLEN + TIPC_GENL_HDRLEN);
105
106 buf = alloc_skb(hdr_len + size, GFP_KERNEL);
107 if (!buf)
108 return NULL;
109
110 skb_reserve(buf, hdr_len);
111
112 return buf;
113}
114
115static struct sk_buff *tipc_get_err_tlv(char *str)
116{
117 int str_len = strlen(str) + 1;
118 struct sk_buff *buf;
119
120 buf = tipc_tlv_alloc(TLV_SPACE(str_len));
121 if (buf)
122 tipc_add_tlv(buf, TIPC_TLV_ERROR_STRING, str, str_len);
123
124 return buf;
125}
126
127static int __tipc_nl_compat_dumpit(struct tipc_nl_compat_cmd_dump *cmd,
128 struct tipc_nl_compat_msg *msg,
129 struct sk_buff *arg)
130{
131 int len = 0;
132 int err;
133 struct sk_buff *buf;
134 struct nlmsghdr *nlmsg;
135 struct netlink_callback cb;
136
137 memset(&cb, 0, sizeof(cb));
138 cb.nlh = (struct nlmsghdr *)arg->data;
139 cb.skb = arg;
140
141 buf = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
142 if (!buf)
143 return -ENOMEM;
144
145 buf->sk = msg->dst_sk;
146
147 do {
148 int rem;
149
150 len = (*cmd->dumpit)(buf, &cb);
151
152 nlmsg_for_each_msg(nlmsg, nlmsg_hdr(buf), len, rem) {
153 struct nlattr **attrs;
154
155 err = tipc_nlmsg_parse(nlmsg, &attrs);
156 if (err)
157 goto err_out;
158
159 err = (*cmd->format)(msg, attrs);
160 if (err)
161 goto err_out;
162
163 if (tipc_skb_tailroom(msg->rep) <= 1) {
164 err = -EMSGSIZE;
165 goto err_out;
166 }
167 }
168
169 skb_reset_tail_pointer(buf);
170 buf->len = 0;
171
172 } while (len);
173
174 err = 0;
175
176err_out:
177 kfree_skb(buf);
178
179 if (err == -EMSGSIZE) {
180 /* The legacy API only considered messages filling
181 * "ULTRA_STRING_MAX_LEN" to be truncated.
182 */
183 if ((TIPC_SKB_MAX - msg->rep->len) <= 1) {
184 char *tail = skb_tail_pointer(msg->rep);
185
186 if (*tail != '\0')
187 sprintf(tail - sizeof(REPLY_TRUNCATED) - 1,
188 REPLY_TRUNCATED);
189 }
190
191 return 0;
192 }
193
194 return err;
195}
196
197static int tipc_nl_compat_dumpit(struct tipc_nl_compat_cmd_dump *cmd,
198 struct tipc_nl_compat_msg *msg)
199{
200 int err;
201 struct sk_buff *arg;
202
203 msg->rep = tipc_tlv_alloc(msg->rep_size);
204 if (!msg->rep)
205 return -ENOMEM;
206
207 arg = nlmsg_new(0, GFP_KERNEL);
208 if (!arg) {
209 kfree_skb(msg->rep);
210 return -ENOMEM;
211 }
212
213 err = __tipc_nl_compat_dumpit(cmd, msg, arg);
214 if (err)
215 kfree_skb(msg->rep);
216
217 kfree_skb(arg);
218
219 return err;
220}
221
Richard Alpe9ab15462015-02-09 09:50:05 +0100222static int __tipc_nl_compat_doit(struct tipc_nl_compat_cmd_doit *cmd,
223 struct tipc_nl_compat_msg *msg)
224{
225 int err;
226 struct sk_buff *doit_buf;
227 struct sk_buff *trans_buf;
228 struct nlattr **attrbuf;
229 struct genl_info info;
230
231 trans_buf = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
232 if (!trans_buf)
233 return -ENOMEM;
234
235 err = (*cmd->transcode)(trans_buf, msg);
236 if (err)
237 goto trans_out;
238
239 attrbuf = kmalloc((tipc_genl_family.maxattr + 1) *
240 sizeof(struct nlattr *), GFP_KERNEL);
241 if (!attrbuf) {
242 err = -ENOMEM;
243 goto trans_out;
244 }
245
246 err = nla_parse(attrbuf, tipc_genl_family.maxattr,
247 (const struct nlattr *)trans_buf->data,
248 trans_buf->len, NULL);
249 if (err)
250 goto parse_out;
251
252 doit_buf = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
253 if (!doit_buf) {
254 err = -ENOMEM;
255 goto parse_out;
256 }
257
258 doit_buf->sk = msg->dst_sk;
259
260 memset(&info, 0, sizeof(info));
261 info.attrs = attrbuf;
262
263 err = (*cmd->doit)(doit_buf, &info);
264
265 kfree_skb(doit_buf);
266parse_out:
267 kfree(attrbuf);
268trans_out:
269 kfree_skb(trans_buf);
270
271 return err;
272}
273
274static int tipc_nl_compat_doit(struct tipc_nl_compat_cmd_doit *cmd,
275 struct tipc_nl_compat_msg *msg)
276{
277 int err;
278
279 if (msg->req_type && !TLV_CHECK_TYPE(msg->req, msg->req_type))
280 return -EINVAL;
281
282 err = __tipc_nl_compat_doit(cmd, msg);
283 if (err)
284 return err;
285
286 /* The legacy API considered an empty message a success message */
287 msg->rep = tipc_tlv_alloc(0);
288 if (!msg->rep)
289 return -ENOMEM;
290
291 return 0;
292}
293
Richard Alped0796d12015-02-09 09:50:04 +0100294static int tipc_nl_compat_bearer_dump(struct tipc_nl_compat_msg *msg,
295 struct nlattr **attrs)
296{
297 struct nlattr *bearer[TIPC_NLA_BEARER_MAX + 1];
298
299 nla_parse_nested(bearer, TIPC_NLA_BEARER_MAX, attrs[TIPC_NLA_BEARER],
300 NULL);
301
302 return tipc_add_tlv(msg->rep, TIPC_TLV_BEARER_NAME,
303 nla_data(bearer[TIPC_NLA_BEARER_NAME]),
304 nla_len(bearer[TIPC_NLA_BEARER_NAME]));
305}
306
Richard Alpe9ab15462015-02-09 09:50:05 +0100307static int tipc_nl_compat_bearer_enable(struct sk_buff *skb,
308 struct tipc_nl_compat_msg *msg)
309{
310 struct nlattr *prop;
311 struct nlattr *bearer;
312 struct tipc_bearer_config *b;
313
314 b = (struct tipc_bearer_config *)TLV_DATA(msg->req);
315
316 bearer = nla_nest_start(skb, TIPC_NLA_BEARER);
317 if (!bearer)
318 return -EMSGSIZE;
319
320 if (nla_put_string(skb, TIPC_NLA_BEARER_NAME, b->name))
321 return -EMSGSIZE;
322
323 if (nla_put_u32(skb, TIPC_NLA_BEARER_DOMAIN, ntohl(b->disc_domain)))
324 return -EMSGSIZE;
325
326 if (ntohl(b->priority) <= TIPC_MAX_LINK_PRI) {
327 prop = nla_nest_start(skb, TIPC_NLA_BEARER_PROP);
328 if (!prop)
329 return -EMSGSIZE;
330 if (nla_put_u32(skb, TIPC_NLA_PROP_PRIO, ntohl(b->priority)))
331 return -EMSGSIZE;
332 nla_nest_end(skb, prop);
333 }
334 nla_nest_end(skb, bearer);
335
336 return 0;
337}
338
339static int tipc_nl_compat_bearer_disable(struct sk_buff *skb,
340 struct tipc_nl_compat_msg *msg)
341{
342 char *name;
343 struct nlattr *bearer;
344
345 name = (char *)TLV_DATA(msg->req);
346
347 bearer = nla_nest_start(skb, TIPC_NLA_BEARER);
348 if (!bearer)
349 return -EMSGSIZE;
350
351 if (nla_put_string(skb, TIPC_NLA_BEARER_NAME, name))
352 return -EMSGSIZE;
353
354 nla_nest_end(skb, bearer);
355
356 return 0;
357}
358
Richard Alped0796d12015-02-09 09:50:04 +0100359static int tipc_nl_compat_handle(struct tipc_nl_compat_msg *msg)
360{
361 struct tipc_nl_compat_cmd_dump dump;
Richard Alpe9ab15462015-02-09 09:50:05 +0100362 struct tipc_nl_compat_cmd_doit doit;
Richard Alped0796d12015-02-09 09:50:04 +0100363
364 memset(&dump, 0, sizeof(dump));
Richard Alpe9ab15462015-02-09 09:50:05 +0100365 memset(&doit, 0, sizeof(doit));
Richard Alped0796d12015-02-09 09:50:04 +0100366
367 switch (msg->cmd) {
368 case TIPC_CMD_GET_BEARER_NAMES:
369 msg->rep_size = MAX_BEARERS * TLV_SPACE(TIPC_MAX_BEARER_NAME);
370 dump.dumpit = tipc_nl_bearer_dump;
371 dump.format = tipc_nl_compat_bearer_dump;
372 return tipc_nl_compat_dumpit(&dump, msg);
Richard Alpe9ab15462015-02-09 09:50:05 +0100373 case TIPC_CMD_ENABLE_BEARER:
374 msg->req_type = TIPC_TLV_BEARER_CONFIG;
375 doit.doit = tipc_nl_bearer_enable;
376 doit.transcode = tipc_nl_compat_bearer_enable;
377 return tipc_nl_compat_doit(&doit, msg);
378 case TIPC_CMD_DISABLE_BEARER:
379 msg->req_type = TIPC_TLV_BEARER_NAME;
380 doit.doit = tipc_nl_bearer_disable;
381 doit.transcode = tipc_nl_compat_bearer_disable;
382 return tipc_nl_compat_doit(&doit, msg);
Richard Alped0796d12015-02-09 09:50:04 +0100383 }
384
385 return -EOPNOTSUPP;
386}
387
388static int tipc_nl_compat_recv(struct sk_buff *skb, struct genl_info *info)
389{
390 int err;
391 int len;
392 struct tipc_nl_compat_msg msg;
393 struct nlmsghdr *req_nlh;
394 struct nlmsghdr *rep_nlh;
395 struct tipc_genlmsghdr *req_userhdr = info->userhdr;
396 struct net *net = genl_info_net(info);
397
398 memset(&msg, 0, sizeof(msg));
399
400 req_nlh = (struct nlmsghdr *)skb->data;
401 msg.req = nlmsg_data(req_nlh) + GENL_HDRLEN + TIPC_GENL_HDRLEN;
402 msg.cmd = req_userhdr->cmd;
403 msg.dst_sk = info->dst_sk;
404
405 if ((msg.cmd & 0xC000) && (!netlink_net_capable(skb, CAP_NET_ADMIN))) {
406 msg.rep = tipc_get_err_tlv(TIPC_CFG_NOT_NET_ADMIN);
407 err = -EACCES;
408 goto send;
409 }
410
411 len = nlmsg_attrlen(req_nlh, GENL_HDRLEN + TIPC_GENL_HDRLEN);
412 if (TLV_GET_LEN(msg.req) && !TLV_OK(msg.req, len)) {
413 msg.rep = tipc_get_err_tlv(TIPC_CFG_NOT_SUPPORTED);
414 err = -EOPNOTSUPP;
415 goto send;
416 }
417
418 err = tipc_nl_compat_handle(&msg);
419 if (err == -EOPNOTSUPP)
420 msg.rep = tipc_get_err_tlv(TIPC_CFG_NOT_SUPPORTED);
421 else if (err == -EINVAL)
422 msg.rep = tipc_get_err_tlv(TIPC_CFG_TLV_ERROR);
423send:
424 if (!msg.rep)
425 return err;
426
427 len = nlmsg_total_size(GENL_HDRLEN + TIPC_GENL_HDRLEN);
428 skb_push(msg.rep, len);
429 rep_nlh = nlmsg_hdr(msg.rep);
430 memcpy(rep_nlh, info->nlhdr, len);
431 rep_nlh->nlmsg_len = msg.rep->len;
432 genlmsg_unicast(net, msg.rep, NETLINK_CB(skb).portid);
433
434 return err;
435}
436
Richard Alpebfb3e5d2015-02-09 09:50:03 +0100437static int handle_cmd(struct sk_buff *skb, struct genl_info *info)
438{
439 struct net *net = genl_info_net(info);
440 struct sk_buff *rep_buf;
441 struct nlmsghdr *rep_nlh;
442 struct nlmsghdr *req_nlh = info->nlhdr;
443 struct tipc_genlmsghdr *req_userhdr = info->userhdr;
444 int hdr_space = nlmsg_total_size(GENL_HDRLEN + TIPC_GENL_HDRLEN);
445 u16 cmd;
446
447 if ((req_userhdr->cmd & 0xC000) &&
448 (!netlink_net_capable(skb, CAP_NET_ADMIN)))
449 cmd = TIPC_CMD_NOT_NET_ADMIN;
450 else
451 cmd = req_userhdr->cmd;
452
453 rep_buf = tipc_cfg_do_cmd(net, req_userhdr->dest, cmd,
454 nlmsg_data(req_nlh) + GENL_HDRLEN +
455 TIPC_GENL_HDRLEN,
456 nlmsg_attrlen(req_nlh, GENL_HDRLEN +
457 TIPC_GENL_HDRLEN), hdr_space);
458
459 if (rep_buf) {
460 skb_push(rep_buf, hdr_space);
461 rep_nlh = nlmsg_hdr(rep_buf);
462 memcpy(rep_nlh, req_nlh, hdr_space);
463 rep_nlh->nlmsg_len = rep_buf->len;
464 genlmsg_unicast(net, rep_buf, NETLINK_CB(skb).portid);
465 }
466
467 return 0;
468}
469
Richard Alped0796d12015-02-09 09:50:04 +0100470/* Temporary function to keep functionality throughout the patchset
471 * without having to mess with the global variables and other trickery
472 * of the old API.
473 */
474static int tipc_nl_compat_tmp_wrap(struct sk_buff *skb, struct genl_info *info)
475{
476 struct tipc_genlmsghdr *req = info->userhdr;
477
478 switch (req->cmd) {
479 case TIPC_CMD_GET_BEARER_NAMES:
Richard Alpe9ab15462015-02-09 09:50:05 +0100480 case TIPC_CMD_ENABLE_BEARER:
481 case TIPC_CMD_DISABLE_BEARER:
Richard Alped0796d12015-02-09 09:50:04 +0100482 return tipc_nl_compat_recv(skb, info);
483 }
484
485 return handle_cmd(skb, info);
486}
487
Richard Alpebfb3e5d2015-02-09 09:50:03 +0100488static struct genl_family tipc_genl_compat_family = {
489 .id = GENL_ID_GENERATE,
490 .name = TIPC_GENL_NAME,
491 .version = TIPC_GENL_VERSION,
492 .hdrsize = TIPC_GENL_HDRLEN,
493 .maxattr = 0,
494 .netnsok = true,
495};
496
497static struct genl_ops tipc_genl_compat_ops[] = {
498 {
499 .cmd = TIPC_GENL_CMD,
Richard Alped0796d12015-02-09 09:50:04 +0100500 .doit = tipc_nl_compat_tmp_wrap,
Richard Alpebfb3e5d2015-02-09 09:50:03 +0100501 },
502};
503
504int tipc_netlink_compat_start(void)
505{
506 int res;
507
508 res = genl_register_family_with_ops(&tipc_genl_compat_family,
509 tipc_genl_compat_ops);
510 if (res) {
511 pr_err("Failed to register legacy compat interface\n");
512 return res;
513 }
514
515 return 0;
516}
517
518void tipc_netlink_compat_stop(void)
519{
520 genl_unregister_family(&tipc_genl_compat_family);
521}