blob: bd75ea290e3a06bfa38e86238003f2a2f7d69000 [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;
52 struct sk_buff *rep;
53 struct tlv_desc *req;
54 struct sock *dst_sk;
55};
56
57struct tipc_nl_compat_cmd_dump {
58 int (*dumpit)(struct sk_buff *, struct netlink_callback *);
59 int (*format)(struct tipc_nl_compat_msg *msg, struct nlattr **attrs);
60};
61
62static int tipc_skb_tailroom(struct sk_buff *skb)
63{
64 int tailroom;
65 int limit;
66
67 tailroom = skb_tailroom(skb);
68 limit = TIPC_SKB_MAX - skb->len;
69
70 if (tailroom < limit)
71 return tailroom;
72
73 return limit;
74}
75
76static int tipc_add_tlv(struct sk_buff *skb, u16 type, void *data, u16 len)
77{
78 struct tlv_desc *tlv = (struct tlv_desc *)skb_tail_pointer(skb);
79
80 if (tipc_skb_tailroom(skb) < TLV_SPACE(len))
81 return -EMSGSIZE;
82
83 skb_put(skb, TLV_SPACE(len));
84 tlv->tlv_type = htons(type);
85 tlv->tlv_len = htons(TLV_LENGTH(len));
86 if (len && data)
87 memcpy(TLV_DATA(tlv), data, len);
88
89 return 0;
90}
91
92static struct sk_buff *tipc_tlv_alloc(int size)
93{
94 int hdr_len;
95 struct sk_buff *buf;
96
97 size = TLV_SPACE(size);
98 hdr_len = nlmsg_total_size(GENL_HDRLEN + TIPC_GENL_HDRLEN);
99
100 buf = alloc_skb(hdr_len + size, GFP_KERNEL);
101 if (!buf)
102 return NULL;
103
104 skb_reserve(buf, hdr_len);
105
106 return buf;
107}
108
109static struct sk_buff *tipc_get_err_tlv(char *str)
110{
111 int str_len = strlen(str) + 1;
112 struct sk_buff *buf;
113
114 buf = tipc_tlv_alloc(TLV_SPACE(str_len));
115 if (buf)
116 tipc_add_tlv(buf, TIPC_TLV_ERROR_STRING, str, str_len);
117
118 return buf;
119}
120
121static int __tipc_nl_compat_dumpit(struct tipc_nl_compat_cmd_dump *cmd,
122 struct tipc_nl_compat_msg *msg,
123 struct sk_buff *arg)
124{
125 int len = 0;
126 int err;
127 struct sk_buff *buf;
128 struct nlmsghdr *nlmsg;
129 struct netlink_callback cb;
130
131 memset(&cb, 0, sizeof(cb));
132 cb.nlh = (struct nlmsghdr *)arg->data;
133 cb.skb = arg;
134
135 buf = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
136 if (!buf)
137 return -ENOMEM;
138
139 buf->sk = msg->dst_sk;
140
141 do {
142 int rem;
143
144 len = (*cmd->dumpit)(buf, &cb);
145
146 nlmsg_for_each_msg(nlmsg, nlmsg_hdr(buf), len, rem) {
147 struct nlattr **attrs;
148
149 err = tipc_nlmsg_parse(nlmsg, &attrs);
150 if (err)
151 goto err_out;
152
153 err = (*cmd->format)(msg, attrs);
154 if (err)
155 goto err_out;
156
157 if (tipc_skb_tailroom(msg->rep) <= 1) {
158 err = -EMSGSIZE;
159 goto err_out;
160 }
161 }
162
163 skb_reset_tail_pointer(buf);
164 buf->len = 0;
165
166 } while (len);
167
168 err = 0;
169
170err_out:
171 kfree_skb(buf);
172
173 if (err == -EMSGSIZE) {
174 /* The legacy API only considered messages filling
175 * "ULTRA_STRING_MAX_LEN" to be truncated.
176 */
177 if ((TIPC_SKB_MAX - msg->rep->len) <= 1) {
178 char *tail = skb_tail_pointer(msg->rep);
179
180 if (*tail != '\0')
181 sprintf(tail - sizeof(REPLY_TRUNCATED) - 1,
182 REPLY_TRUNCATED);
183 }
184
185 return 0;
186 }
187
188 return err;
189}
190
191static int tipc_nl_compat_dumpit(struct tipc_nl_compat_cmd_dump *cmd,
192 struct tipc_nl_compat_msg *msg)
193{
194 int err;
195 struct sk_buff *arg;
196
197 msg->rep = tipc_tlv_alloc(msg->rep_size);
198 if (!msg->rep)
199 return -ENOMEM;
200
201 arg = nlmsg_new(0, GFP_KERNEL);
202 if (!arg) {
203 kfree_skb(msg->rep);
204 return -ENOMEM;
205 }
206
207 err = __tipc_nl_compat_dumpit(cmd, msg, arg);
208 if (err)
209 kfree_skb(msg->rep);
210
211 kfree_skb(arg);
212
213 return err;
214}
215
216static int tipc_nl_compat_bearer_dump(struct tipc_nl_compat_msg *msg,
217 struct nlattr **attrs)
218{
219 struct nlattr *bearer[TIPC_NLA_BEARER_MAX + 1];
220
221 nla_parse_nested(bearer, TIPC_NLA_BEARER_MAX, attrs[TIPC_NLA_BEARER],
222 NULL);
223
224 return tipc_add_tlv(msg->rep, TIPC_TLV_BEARER_NAME,
225 nla_data(bearer[TIPC_NLA_BEARER_NAME]),
226 nla_len(bearer[TIPC_NLA_BEARER_NAME]));
227}
228
229static int tipc_nl_compat_handle(struct tipc_nl_compat_msg *msg)
230{
231 struct tipc_nl_compat_cmd_dump dump;
232
233 memset(&dump, 0, sizeof(dump));
234
235 switch (msg->cmd) {
236 case TIPC_CMD_GET_BEARER_NAMES:
237 msg->rep_size = MAX_BEARERS * TLV_SPACE(TIPC_MAX_BEARER_NAME);
238 dump.dumpit = tipc_nl_bearer_dump;
239 dump.format = tipc_nl_compat_bearer_dump;
240 return tipc_nl_compat_dumpit(&dump, msg);
241 }
242
243 return -EOPNOTSUPP;
244}
245
246static int tipc_nl_compat_recv(struct sk_buff *skb, struct genl_info *info)
247{
248 int err;
249 int len;
250 struct tipc_nl_compat_msg msg;
251 struct nlmsghdr *req_nlh;
252 struct nlmsghdr *rep_nlh;
253 struct tipc_genlmsghdr *req_userhdr = info->userhdr;
254 struct net *net = genl_info_net(info);
255
256 memset(&msg, 0, sizeof(msg));
257
258 req_nlh = (struct nlmsghdr *)skb->data;
259 msg.req = nlmsg_data(req_nlh) + GENL_HDRLEN + TIPC_GENL_HDRLEN;
260 msg.cmd = req_userhdr->cmd;
261 msg.dst_sk = info->dst_sk;
262
263 if ((msg.cmd & 0xC000) && (!netlink_net_capable(skb, CAP_NET_ADMIN))) {
264 msg.rep = tipc_get_err_tlv(TIPC_CFG_NOT_NET_ADMIN);
265 err = -EACCES;
266 goto send;
267 }
268
269 len = nlmsg_attrlen(req_nlh, GENL_HDRLEN + TIPC_GENL_HDRLEN);
270 if (TLV_GET_LEN(msg.req) && !TLV_OK(msg.req, len)) {
271 msg.rep = tipc_get_err_tlv(TIPC_CFG_NOT_SUPPORTED);
272 err = -EOPNOTSUPP;
273 goto send;
274 }
275
276 err = tipc_nl_compat_handle(&msg);
277 if (err == -EOPNOTSUPP)
278 msg.rep = tipc_get_err_tlv(TIPC_CFG_NOT_SUPPORTED);
279 else if (err == -EINVAL)
280 msg.rep = tipc_get_err_tlv(TIPC_CFG_TLV_ERROR);
281send:
282 if (!msg.rep)
283 return err;
284
285 len = nlmsg_total_size(GENL_HDRLEN + TIPC_GENL_HDRLEN);
286 skb_push(msg.rep, len);
287 rep_nlh = nlmsg_hdr(msg.rep);
288 memcpy(rep_nlh, info->nlhdr, len);
289 rep_nlh->nlmsg_len = msg.rep->len;
290 genlmsg_unicast(net, msg.rep, NETLINK_CB(skb).portid);
291
292 return err;
293}
294
Richard Alpebfb3e5d2015-02-09 09:50:03 +0100295static int handle_cmd(struct sk_buff *skb, struct genl_info *info)
296{
297 struct net *net = genl_info_net(info);
298 struct sk_buff *rep_buf;
299 struct nlmsghdr *rep_nlh;
300 struct nlmsghdr *req_nlh = info->nlhdr;
301 struct tipc_genlmsghdr *req_userhdr = info->userhdr;
302 int hdr_space = nlmsg_total_size(GENL_HDRLEN + TIPC_GENL_HDRLEN);
303 u16 cmd;
304
305 if ((req_userhdr->cmd & 0xC000) &&
306 (!netlink_net_capable(skb, CAP_NET_ADMIN)))
307 cmd = TIPC_CMD_NOT_NET_ADMIN;
308 else
309 cmd = req_userhdr->cmd;
310
311 rep_buf = tipc_cfg_do_cmd(net, req_userhdr->dest, cmd,
312 nlmsg_data(req_nlh) + GENL_HDRLEN +
313 TIPC_GENL_HDRLEN,
314 nlmsg_attrlen(req_nlh, GENL_HDRLEN +
315 TIPC_GENL_HDRLEN), hdr_space);
316
317 if (rep_buf) {
318 skb_push(rep_buf, hdr_space);
319 rep_nlh = nlmsg_hdr(rep_buf);
320 memcpy(rep_nlh, req_nlh, hdr_space);
321 rep_nlh->nlmsg_len = rep_buf->len;
322 genlmsg_unicast(net, rep_buf, NETLINK_CB(skb).portid);
323 }
324
325 return 0;
326}
327
Richard Alped0796d12015-02-09 09:50:04 +0100328/* Temporary function to keep functionality throughout the patchset
329 * without having to mess with the global variables and other trickery
330 * of the old API.
331 */
332static int tipc_nl_compat_tmp_wrap(struct sk_buff *skb, struct genl_info *info)
333{
334 struct tipc_genlmsghdr *req = info->userhdr;
335
336 switch (req->cmd) {
337 case TIPC_CMD_GET_BEARER_NAMES:
338 return tipc_nl_compat_recv(skb, info);
339 }
340
341 return handle_cmd(skb, info);
342}
343
Richard Alpebfb3e5d2015-02-09 09:50:03 +0100344static struct genl_family tipc_genl_compat_family = {
345 .id = GENL_ID_GENERATE,
346 .name = TIPC_GENL_NAME,
347 .version = TIPC_GENL_VERSION,
348 .hdrsize = TIPC_GENL_HDRLEN,
349 .maxattr = 0,
350 .netnsok = true,
351};
352
353static struct genl_ops tipc_genl_compat_ops[] = {
354 {
355 .cmd = TIPC_GENL_CMD,
Richard Alped0796d12015-02-09 09:50:04 +0100356 .doit = tipc_nl_compat_tmp_wrap,
Richard Alpebfb3e5d2015-02-09 09:50:03 +0100357 },
358};
359
360int tipc_netlink_compat_start(void)
361{
362 int res;
363
364 res = genl_register_family_with_ops(&tipc_genl_compat_family,
365 tipc_genl_compat_ops);
366 if (res) {
367 pr_err("Failed to register legacy compat interface\n");
368 return res;
369 }
370
371 return 0;
372}
373
374void tipc_netlink_compat_stop(void)
375{
376 genl_unregister_family(&tipc_genl_compat_family);
377}