Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
[cascardo/linux.git] / net / batman-adv / netlink.c
1 /* Copyright (C) 2016 B.A.T.M.A.N. contributors:
2  *
3  * Matthias Schiffer
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of version 2 of the GNU General Public
7  * License as published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include "netlink.h"
19 #include "main.h"
20
21 #include <linux/errno.h>
22 #include <linux/fs.h>
23 #include <linux/genetlink.h>
24 #include <linux/if_ether.h>
25 #include <linux/init.h>
26 #include <linux/netdevice.h>
27 #include <linux/netlink.h>
28 #include <linux/printk.h>
29 #include <linux/stddef.h>
30 #include <linux/types.h>
31 #include <net/genetlink.h>
32 #include <net/netlink.h>
33 #include <uapi/linux/batman_adv.h>
34
35 #include "hard-interface.h"
36 #include "soft-interface.h"
37 #include "tp_meter.h"
38
39 struct sk_buff;
40
41 static struct genl_family batadv_netlink_family = {
42         .id = GENL_ID_GENERATE,
43         .hdrsize = 0,
44         .name = BATADV_NL_NAME,
45         .version = 1,
46         .maxattr = BATADV_ATTR_MAX,
47 };
48
49 /* multicast groups */
50 enum batadv_netlink_multicast_groups {
51         BATADV_NL_MCGRP_TPMETER,
52 };
53
54 static struct genl_multicast_group batadv_netlink_mcgrps[] = {
55         [BATADV_NL_MCGRP_TPMETER] = { .name = BATADV_NL_MCAST_GROUP_TPMETER },
56 };
57
58 static struct nla_policy batadv_netlink_policy[NUM_BATADV_ATTR] = {
59         [BATADV_ATTR_VERSION]           = { .type = NLA_STRING },
60         [BATADV_ATTR_ALGO_NAME]         = { .type = NLA_STRING },
61         [BATADV_ATTR_MESH_IFINDEX]      = { .type = NLA_U32 },
62         [BATADV_ATTR_MESH_IFNAME]       = { .type = NLA_STRING },
63         [BATADV_ATTR_MESH_ADDRESS]      = { .len = ETH_ALEN },
64         [BATADV_ATTR_HARD_IFINDEX]      = { .type = NLA_U32 },
65         [BATADV_ATTR_HARD_IFNAME]       = { .type = NLA_STRING },
66         [BATADV_ATTR_HARD_ADDRESS]      = { .len = ETH_ALEN },
67         [BATADV_ATTR_ORIG_ADDRESS]      = { .len = ETH_ALEN },
68         [BATADV_ATTR_TPMETER_RESULT]    = { .type = NLA_U8 },
69         [BATADV_ATTR_TPMETER_TEST_TIME] = { .type = NLA_U32 },
70         [BATADV_ATTR_TPMETER_BYTES]     = { .type = NLA_U64 },
71         [BATADV_ATTR_TPMETER_COOKIE]    = { .type = NLA_U32 },
72 };
73
74 /**
75  * batadv_netlink_mesh_info_put - fill in generic information about mesh
76  *  interface
77  * @msg: netlink message to be sent back
78  * @soft_iface: interface for which the data should be taken
79  *
80  * Return: 0 on success, < 0 on error
81  */
82 static int
83 batadv_netlink_mesh_info_put(struct sk_buff *msg, struct net_device *soft_iface)
84 {
85         struct batadv_priv *bat_priv = netdev_priv(soft_iface);
86         struct batadv_hard_iface *primary_if = NULL;
87         struct net_device *hard_iface;
88         int ret = -ENOBUFS;
89
90         if (nla_put_string(msg, BATADV_ATTR_VERSION, BATADV_SOURCE_VERSION) ||
91             nla_put_string(msg, BATADV_ATTR_ALGO_NAME,
92                            bat_priv->algo_ops->name) ||
93             nla_put_u32(msg, BATADV_ATTR_MESH_IFINDEX, soft_iface->ifindex) ||
94             nla_put_string(msg, BATADV_ATTR_MESH_IFNAME, soft_iface->name) ||
95             nla_put(msg, BATADV_ATTR_MESH_ADDRESS, ETH_ALEN,
96                     soft_iface->dev_addr))
97                 goto out;
98
99         primary_if = batadv_primary_if_get_selected(bat_priv);
100         if (primary_if && primary_if->if_status == BATADV_IF_ACTIVE) {
101                 hard_iface = primary_if->net_dev;
102
103                 if (nla_put_u32(msg, BATADV_ATTR_HARD_IFINDEX,
104                                 hard_iface->ifindex) ||
105                     nla_put_string(msg, BATADV_ATTR_HARD_IFNAME,
106                                    hard_iface->name) ||
107                     nla_put(msg, BATADV_ATTR_HARD_ADDRESS, ETH_ALEN,
108                             hard_iface->dev_addr))
109                         goto out;
110         }
111
112         ret = 0;
113
114  out:
115         if (primary_if)
116                 batadv_hardif_put(primary_if);
117
118         return ret;
119 }
120
121 /**
122  * batadv_netlink_get_mesh_info - handle incoming BATADV_CMD_GET_MESH_INFO
123  *  netlink request
124  * @skb: received netlink message
125  * @info: receiver information
126  *
127  * Return: 0 on success, < 0 on error
128  */
129 static int
130 batadv_netlink_get_mesh_info(struct sk_buff *skb, struct genl_info *info)
131 {
132         struct net *net = genl_info_net(info);
133         struct net_device *soft_iface;
134         struct sk_buff *msg = NULL;
135         void *msg_head;
136         int ifindex;
137         int ret;
138
139         if (!info->attrs[BATADV_ATTR_MESH_IFINDEX])
140                 return -EINVAL;
141
142         ifindex = nla_get_u32(info->attrs[BATADV_ATTR_MESH_IFINDEX]);
143         if (!ifindex)
144                 return -EINVAL;
145
146         soft_iface = dev_get_by_index(net, ifindex);
147         if (!soft_iface || !batadv_softif_is_valid(soft_iface)) {
148                 ret = -ENODEV;
149                 goto out;
150         }
151
152         msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
153         if (!msg) {
154                 ret = -ENOMEM;
155                 goto out;
156         }
157
158         msg_head = genlmsg_put(msg, info->snd_portid, info->snd_seq,
159                                &batadv_netlink_family, 0,
160                                BATADV_CMD_GET_MESH_INFO);
161         if (!msg_head) {
162                 ret = -ENOBUFS;
163                 goto out;
164         }
165
166         ret = batadv_netlink_mesh_info_put(msg, soft_iface);
167
168  out:
169         if (soft_iface)
170                 dev_put(soft_iface);
171
172         if (ret) {
173                 if (msg)
174                         nlmsg_free(msg);
175                 return ret;
176         }
177
178         genlmsg_end(msg, msg_head);
179         return genlmsg_reply(msg, info);
180 }
181
182 /**
183  * batadv_netlink_tp_meter_put - Fill information of started tp_meter session
184  * @msg: netlink message to be sent back
185  * @cookie: tp meter session cookie
186  *
187  *  Return: 0 on success, < 0 on error
188  */
189 static int
190 batadv_netlink_tp_meter_put(struct sk_buff *msg, u32 cookie)
191 {
192         if (nla_put_u32(msg, BATADV_ATTR_TPMETER_COOKIE, cookie))
193                 return -ENOBUFS;
194
195         return 0;
196 }
197
198 /**
199  * batadv_netlink_tpmeter_notify - send tp_meter result via netlink to client
200  * @bat_priv: the bat priv with all the soft interface information
201  * @dst: destination of tp_meter session
202  * @result: reason for tp meter session stop
203  * @test_time: total time ot the tp_meter session
204  * @total_bytes: bytes acked to the receiver
205  * @cookie: cookie of tp_meter session
206  *
207  * Return: 0 on success, < 0 on error
208  */
209 int batadv_netlink_tpmeter_notify(struct batadv_priv *bat_priv, const u8 *dst,
210                                   u8 result, u32 test_time, u64 total_bytes,
211                                   u32 cookie)
212 {
213         struct sk_buff *msg;
214         void *hdr;
215         int ret;
216
217         msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
218         if (!msg)
219                 return -ENOMEM;
220
221         hdr = genlmsg_put(msg, 0, 0, &batadv_netlink_family, 0,
222                           BATADV_CMD_TP_METER);
223         if (!hdr) {
224                 ret = -ENOBUFS;
225                 goto err_genlmsg;
226         }
227
228         if (nla_put_u32(msg, BATADV_ATTR_TPMETER_COOKIE, cookie))
229                 goto nla_put_failure;
230
231         if (nla_put_u32(msg, BATADV_ATTR_TPMETER_TEST_TIME, test_time))
232                 goto nla_put_failure;
233
234         if (nla_put_u64_64bit(msg, BATADV_ATTR_TPMETER_BYTES, total_bytes,
235                               BATADV_ATTR_PAD))
236                 goto nla_put_failure;
237
238         if (nla_put_u8(msg, BATADV_ATTR_TPMETER_RESULT, result))
239                 goto nla_put_failure;
240
241         if (nla_put(msg, BATADV_ATTR_ORIG_ADDRESS, ETH_ALEN, dst))
242                 goto nla_put_failure;
243
244         genlmsg_end(msg, hdr);
245
246         genlmsg_multicast_netns(&batadv_netlink_family,
247                                 dev_net(bat_priv->soft_iface), msg, 0,
248                                 BATADV_NL_MCGRP_TPMETER, GFP_KERNEL);
249
250         return 0;
251
252 nla_put_failure:
253         genlmsg_cancel(msg, hdr);
254         ret = -EMSGSIZE;
255
256 err_genlmsg:
257         nlmsg_free(msg);
258         return ret;
259 }
260
261 /**
262  * batadv_netlink_tp_meter_start - Start a new tp_meter session
263  * @skb: received netlink message
264  * @info: receiver information
265  *
266  * Return: 0 on success, < 0 on error
267  */
268 static int
269 batadv_netlink_tp_meter_start(struct sk_buff *skb, struct genl_info *info)
270 {
271         struct net *net = genl_info_net(info);
272         struct net_device *soft_iface;
273         struct batadv_priv *bat_priv;
274         struct sk_buff *msg = NULL;
275         u32 test_length;
276         void *msg_head;
277         int ifindex;
278         u32 cookie;
279         u8 *dst;
280         int ret;
281
282         if (!info->attrs[BATADV_ATTR_MESH_IFINDEX])
283                 return -EINVAL;
284
285         if (!info->attrs[BATADV_ATTR_ORIG_ADDRESS])
286                 return -EINVAL;
287
288         if (!info->attrs[BATADV_ATTR_TPMETER_TEST_TIME])
289                 return -EINVAL;
290
291         ifindex = nla_get_u32(info->attrs[BATADV_ATTR_MESH_IFINDEX]);
292         if (!ifindex)
293                 return -EINVAL;
294
295         dst = nla_data(info->attrs[BATADV_ATTR_ORIG_ADDRESS]);
296
297         test_length = nla_get_u32(info->attrs[BATADV_ATTR_TPMETER_TEST_TIME]);
298
299         soft_iface = dev_get_by_index(net, ifindex);
300         if (!soft_iface || !batadv_softif_is_valid(soft_iface)) {
301                 ret = -ENODEV;
302                 goto out;
303         }
304
305         msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
306         if (!msg) {
307                 ret = -ENOMEM;
308                 goto out;
309         }
310
311         msg_head = genlmsg_put(msg, info->snd_portid, info->snd_seq,
312                                &batadv_netlink_family, 0,
313                                BATADV_CMD_TP_METER);
314         if (!msg_head) {
315                 ret = -ENOBUFS;
316                 goto out;
317         }
318
319         bat_priv = netdev_priv(soft_iface);
320         batadv_tp_start(bat_priv, dst, test_length, &cookie);
321
322         ret = batadv_netlink_tp_meter_put(msg, cookie);
323
324  out:
325         if (soft_iface)
326                 dev_put(soft_iface);
327
328         if (ret) {
329                 if (msg)
330                         nlmsg_free(msg);
331                 return ret;
332         }
333
334         genlmsg_end(msg, msg_head);
335         return genlmsg_reply(msg, info);
336 }
337
338 /**
339  * batadv_netlink_tp_meter_start - Cancel a running tp_meter session
340  * @skb: received netlink message
341  * @info: receiver information
342  *
343  * Return: 0 on success, < 0 on error
344  */
345 static int
346 batadv_netlink_tp_meter_cancel(struct sk_buff *skb, struct genl_info *info)
347 {
348         struct net *net = genl_info_net(info);
349         struct net_device *soft_iface;
350         struct batadv_priv *bat_priv;
351         int ifindex;
352         u8 *dst;
353         int ret = 0;
354
355         if (!info->attrs[BATADV_ATTR_MESH_IFINDEX])
356                 return -EINVAL;
357
358         if (!info->attrs[BATADV_ATTR_ORIG_ADDRESS])
359                 return -EINVAL;
360
361         ifindex = nla_get_u32(info->attrs[BATADV_ATTR_MESH_IFINDEX]);
362         if (!ifindex)
363                 return -EINVAL;
364
365         dst = nla_data(info->attrs[BATADV_ATTR_ORIG_ADDRESS]);
366
367         soft_iface = dev_get_by_index(net, ifindex);
368         if (!soft_iface || !batadv_softif_is_valid(soft_iface)) {
369                 ret = -ENODEV;
370                 goto out;
371         }
372
373         bat_priv = netdev_priv(soft_iface);
374         batadv_tp_stop(bat_priv, dst, BATADV_TP_REASON_CANCEL);
375
376 out:
377         if (soft_iface)
378                 dev_put(soft_iface);
379
380         return ret;
381 }
382
383 static struct genl_ops batadv_netlink_ops[] = {
384         {
385                 .cmd = BATADV_CMD_GET_MESH_INFO,
386                 .flags = GENL_ADMIN_PERM,
387                 .policy = batadv_netlink_policy,
388                 .doit = batadv_netlink_get_mesh_info,
389         },
390         {
391                 .cmd = BATADV_CMD_TP_METER,
392                 .flags = GENL_ADMIN_PERM,
393                 .policy = batadv_netlink_policy,
394                 .doit = batadv_netlink_tp_meter_start,
395         },
396         {
397                 .cmd = BATADV_CMD_TP_METER_CANCEL,
398                 .flags = GENL_ADMIN_PERM,
399                 .policy = batadv_netlink_policy,
400                 .doit = batadv_netlink_tp_meter_cancel,
401         },
402 };
403
404 /**
405  * batadv_netlink_register - register batadv genl netlink family
406  */
407 void __init batadv_netlink_register(void)
408 {
409         int ret;
410
411         ret = genl_register_family_with_ops_groups(&batadv_netlink_family,
412                                                    batadv_netlink_ops,
413                                                    batadv_netlink_mcgrps);
414         if (ret)
415                 pr_warn("unable to register netlink family");
416 }
417
418 /**
419  * batadv_netlink_unregister - unregister batadv genl netlink family
420  */
421 void batadv_netlink_unregister(void)
422 {
423         genl_unregister_family(&batadv_netlink_family);
424 }